mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
feat(ble_iso): Split ISO related files into components/bt/esp_ble_iso
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2021 Nordic Semiconductor ASA
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
|
||||
#include <../host/hci_core.h>
|
||||
|
||||
#include "common/host.h"
|
||||
|
||||
static struct bt_le_ext_adv ext_adv_pool[CONFIG_BT_EXT_ADV_MAX_ADV_SET];
|
||||
|
||||
static struct bt_le_ext_adv *ext_adv_find(uint8_t adv_handle)
|
||||
{
|
||||
struct bt_le_ext_adv *adv = NULL;
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(ext_adv_pool); i++) {
|
||||
if (atomic_test_bit(ext_adv_pool[i].flags,
|
||||
BT_PER_ADV_PARAMS_SET) &&
|
||||
ext_adv_pool[i].handle == adv_handle) {
|
||||
LOG_DBG("ExtAdvFound[%u][%u]", i, adv_handle);
|
||||
adv = &ext_adv_pool[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return adv;
|
||||
}
|
||||
|
||||
static struct bt_le_ext_adv *ext_adv_new(void)
|
||||
{
|
||||
struct bt_le_ext_adv *adv = NULL;
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(ext_adv_pool); i++) {
|
||||
if (atomic_test_bit(ext_adv_pool[i].flags,
|
||||
BT_PER_ADV_PARAMS_SET) == false) {
|
||||
adv = &ext_adv_pool[i];
|
||||
|
||||
memset(adv, 0, sizeof(*adv));
|
||||
atomic_set_bit(adv->flags, BT_PER_ADV_PARAMS_SET);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return adv;
|
||||
}
|
||||
|
||||
static void ext_adv_delete(struct bt_le_ext_adv *adv)
|
||||
{
|
||||
atomic_clear_bit(adv->flags, BT_PER_ADV_PARAMS_SET);
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
struct bt_le_ext_adv *bt_le_ext_adv_find(uint8_t adv_handle)
|
||||
{
|
||||
LOG_DBG("ExtAdvFind[%u]", adv_handle);
|
||||
return ext_adv_find(adv_handle);
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_ext_adv_new_safe(uint8_t adv_handle)
|
||||
{
|
||||
struct bt_le_ext_adv *adv = NULL;
|
||||
int err = 0;
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
adv = ext_adv_find(adv_handle);
|
||||
if (adv) {
|
||||
LOG_WRN("ExtAdvExist[%u]", adv_handle);
|
||||
err = -EEXIST;
|
||||
goto end;
|
||||
}
|
||||
|
||||
adv = ext_adv_new();
|
||||
if (adv == NULL) {
|
||||
LOG_ERR("NoFreeExtAdv[%u]", adv_handle);
|
||||
err = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
adv->handle = adv_handle;
|
||||
|
||||
end:
|
||||
bt_le_host_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_ext_adv_delete_safe(uint8_t adv_handle)
|
||||
{
|
||||
struct bt_le_ext_adv *adv = NULL;
|
||||
int err = 0;
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
adv = ext_adv_find(adv_handle);
|
||||
if (adv == NULL) {
|
||||
LOG_ERR("ExtAdvNotFound[%u]", adv_handle);
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ext_adv_delete(adv);
|
||||
|
||||
end:
|
||||
bt_le_host_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
_LIB_ONLY
|
||||
int bt_le_ext_adv_get_info(const struct bt_le_ext_adv *adv,
|
||||
struct bt_le_ext_adv_info *info)
|
||||
{
|
||||
if (adv == NULL || info == NULL) {
|
||||
LOG_ERR("ParamNull[%p][%p]", adv, info);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!IS_ARRAY_ELEMENT(ext_adv_pool, adv)) {
|
||||
LOG_ERR("ExtAdvNotInPool[%p]", adv);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/bluetooth/buf.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
|
||||
#include <../host/hci_core.h>
|
||||
#include <../host/iso_internal.h>
|
||||
|
||||
#include "common/host.h"
|
||||
#include "common/app/gap.h"
|
||||
|
||||
static bt_le_gap_app_cb gap_app_cb;
|
||||
|
||||
int bt_le_gap_app_cb_register(bt_le_gap_app_cb cb)
|
||||
{
|
||||
LOG_DBG("GapAppCbReg");
|
||||
|
||||
if (cb == NULL) {
|
||||
LOG_ERR("GapAppCbNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (gap_app_cb) {
|
||||
LOG_WRN("GapAppCbExist");
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
gap_app_cb = cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bt_le_gap_app_cb_unregister(void)
|
||||
{
|
||||
if (gap_app_cb) {
|
||||
LOG_DBG("GapAppCbUnreg");
|
||||
gap_app_cb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void bt_le_gap_app_cb_evt(struct bt_le_gap_app_event *event)
|
||||
{
|
||||
if (gap_app_cb) {
|
||||
gap_app_cb(event);
|
||||
}
|
||||
}
|
||||
|
||||
struct biginfo {
|
||||
uint16_t sync_handle;
|
||||
uint8_t num_bis;
|
||||
uint8_t nse;
|
||||
uint16_t iso_interval;
|
||||
uint8_t bn;
|
||||
uint8_t pto;
|
||||
uint8_t irc;
|
||||
uint16_t max_pdu;
|
||||
uint8_t sdu_interval[3];
|
||||
uint16_t max_sdu;
|
||||
uint8_t phy;
|
||||
uint8_t framing;
|
||||
uint8_t encryption;
|
||||
} __packed;
|
||||
|
||||
void bt_le_gap_app_biginfo_event(uint8_t *param)
|
||||
{
|
||||
struct bt_le_gap_app_event event = {
|
||||
.type = BT_LE_GAP_APP_EVENT_BIGINFO_RECV,
|
||||
};
|
||||
struct biginfo *biginfo;
|
||||
|
||||
biginfo = (void *)param;
|
||||
|
||||
event.biginfo_recv.sync_handle = biginfo->sync_handle;
|
||||
event.biginfo_recv.num_bis = biginfo->num_bis;
|
||||
event.biginfo_recv.nse = biginfo->nse;
|
||||
event.biginfo_recv.iso_interval = biginfo->iso_interval;
|
||||
event.biginfo_recv.bn = biginfo->bn;
|
||||
event.biginfo_recv.pto = biginfo->pto;
|
||||
event.biginfo_recv.irc = biginfo->irc;
|
||||
event.biginfo_recv.max_pdu = biginfo->max_pdu;
|
||||
event.biginfo_recv.sdu_interval = sys_get_le24(biginfo->sdu_interval);
|
||||
event.biginfo_recv.max_sdu = biginfo->max_sdu;
|
||||
event.biginfo_recv.phy = biginfo->phy;
|
||||
event.biginfo_recv.framing = biginfo->framing;
|
||||
event.biginfo_recv.encryption = biginfo->encryption;
|
||||
|
||||
bt_le_gap_app_cb_evt(&event);
|
||||
}
|
||||
|
||||
static void handle_ext_scan_recv_event_safe(struct bt_le_gap_app_param *param)
|
||||
{
|
||||
struct bt_le_gap_app_event event = {
|
||||
.type = BT_LE_GAP_APP_EVENT_EXT_SCAN_RECV,
|
||||
};
|
||||
|
||||
event.ext_scan_recv.event_type = param->ext_scan_recv.event_type;
|
||||
event.ext_scan_recv.addr.type = param->ext_scan_recv.addr.type;
|
||||
memcpy(event.ext_scan_recv.addr.val, param->ext_scan_recv.addr.val, BT_ADDR_SIZE);
|
||||
event.ext_scan_recv.rssi = param->ext_scan_recv.rssi;
|
||||
event.ext_scan_recv.tx_power = param->ext_scan_recv.tx_power;
|
||||
event.ext_scan_recv.sid = param->ext_scan_recv.sid;
|
||||
event.ext_scan_recv.pri_phy = param->ext_scan_recv.pri_phy;
|
||||
event.ext_scan_recv.sec_phy = param->ext_scan_recv.sec_phy;
|
||||
event.ext_scan_recv.per_adv_itvl = param->ext_scan_recv.per_adv_itvl;
|
||||
event.ext_scan_recv.data_len = param->ext_scan_recv.data_len;
|
||||
event.ext_scan_recv.data = param->ext_scan_recv.data;
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
bt_le_scan_recv_listener(event.ext_scan_recv.event_type,
|
||||
event.ext_scan_recv.addr.type,
|
||||
event.ext_scan_recv.addr.val,
|
||||
event.ext_scan_recv.rssi,
|
||||
event.ext_scan_recv.tx_power,
|
||||
event.ext_scan_recv.sid,
|
||||
event.ext_scan_recv.pri_phy,
|
||||
event.ext_scan_recv.sec_phy,
|
||||
event.ext_scan_recv.per_adv_itvl,
|
||||
event.ext_scan_recv.data_len,
|
||||
event.ext_scan_recv.data);
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
bt_le_gap_app_cb_evt(&event);
|
||||
|
||||
if (param->ext_scan_recv.data) {
|
||||
free(param->ext_scan_recv.data);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pa_sync_event_safe(struct bt_le_gap_app_param *param)
|
||||
{
|
||||
struct bt_le_gap_app_event event = {
|
||||
.type = BT_LE_GAP_APP_EVENT_PA_SYNC,
|
||||
};
|
||||
struct bt_le_per_adv_sync *per_adv_sync;
|
||||
int err;
|
||||
|
||||
event.pa_sync.status = param->pa_sync.status;
|
||||
event.pa_sync.sync_handle = param->pa_sync.sync_handle;
|
||||
event.pa_sync.addr.type = param->pa_sync.addr.type;
|
||||
memcpy(event.pa_sync.addr.val, param->pa_sync.addr.val, BT_ADDR_SIZE);
|
||||
event.pa_sync.sid = param->pa_sync.sid;
|
||||
event.pa_sync.adv_phy = param->pa_sync.adv_phy;
|
||||
event.pa_sync.per_adv_itvl = param->pa_sync.per_adv_itvl;
|
||||
event.pa_sync.adv_ca = param->pa_sync.adv_ca;
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
if (event.pa_sync.status) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
err = bt_le_per_adv_sync_new(event.pa_sync.sync_handle,
|
||||
event.pa_sync.sid,
|
||||
event.pa_sync.adv_phy,
|
||||
event.pa_sync.per_adv_itvl,
|
||||
event.pa_sync.addr.type,
|
||||
event.pa_sync.addr.val,
|
||||
&per_adv_sync);
|
||||
if (err) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
err = bt_le_per_adv_sync_establish_listener(event.pa_sync.sync_handle);
|
||||
assert(err == 0);
|
||||
|
||||
end:
|
||||
bt_le_host_unlock();
|
||||
|
||||
bt_le_gap_app_cb_evt(&event);
|
||||
}
|
||||
|
||||
static void handle_pa_sync_lost_event_safe(struct bt_le_gap_app_param *param)
|
||||
{
|
||||
struct bt_le_gap_app_event event = {
|
||||
.type = BT_LE_GAP_APP_EVENT_PA_SYNC_LOST,
|
||||
};
|
||||
int err;
|
||||
|
||||
event.pa_sync_lost.sync_handle = param->pa_sync_lost.sync_handle;
|
||||
event.pa_sync_lost.reason = param->pa_sync_lost.reason;
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
err = bt_le_per_adv_sync_lost_listener(event.pa_sync_lost.sync_handle);
|
||||
if (err) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
err = bt_le_per_adv_sync_delete(event.pa_sync_lost.sync_handle);
|
||||
assert(err == 0);
|
||||
|
||||
end:
|
||||
bt_le_host_unlock();
|
||||
|
||||
bt_le_gap_app_cb_evt(&event);
|
||||
}
|
||||
|
||||
static void handle_pa_sync_recv_event_safe(struct bt_le_gap_app_param *param)
|
||||
{
|
||||
struct bt_le_gap_app_event event = {
|
||||
.type = BT_LE_GAP_APP_EVENT_PA_SYNC_RECV,
|
||||
};
|
||||
|
||||
event.pa_sync_recv.sync_handle = param->pa_sync_recv.sync_handle;
|
||||
event.pa_sync_recv.tx_power = param->pa_sync_recv.tx_power;
|
||||
event.pa_sync_recv.rssi = param->pa_sync_recv.rssi;
|
||||
event.pa_sync_recv.data_status = param->pa_sync_recv.data_status;
|
||||
event.pa_sync_recv.data_len = param->pa_sync_recv.data_len;
|
||||
event.pa_sync_recv.data = param->pa_sync_recv.data;
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
bt_le_per_adv_sync_report_recv_listener(event.pa_sync_recv.sync_handle,
|
||||
event.pa_sync_recv.data,
|
||||
event.pa_sync_recv.data_len);
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
bt_le_gap_app_cb_evt(&event);
|
||||
|
||||
if (param->pa_sync_recv.data) {
|
||||
free(param->pa_sync_recv.data);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_acl_connect_event_safe(struct bt_le_gap_app_param *param)
|
||||
{
|
||||
struct bt_le_gap_app_event event = {
|
||||
.type = BT_LE_GAP_APP_EVENT_ACL_CONNECT,
|
||||
};
|
||||
struct bt_conn *conn;
|
||||
bt_addr_le_t dst;
|
||||
int err;
|
||||
|
||||
event.acl_connect.status = param->acl_connect.status;
|
||||
event.acl_connect.conn_handle = param->acl_connect.conn_handle;
|
||||
event.acl_connect.role = param->acl_connect.role;
|
||||
event.acl_connect.dst.type = param->acl_connect.dst.type;
|
||||
memcpy(event.acl_connect.dst.val, param->acl_connect.dst.val, BT_ADDR_SIZE);
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
if (event.acl_connect.status) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
conn = bt_le_acl_conn_find(event.acl_connect.conn_handle);
|
||||
if (conn) {
|
||||
LOG_WRN("AclConnExist[%u]", event.acl_connect.conn_handle);
|
||||
goto end;
|
||||
}
|
||||
|
||||
dst.type = event.acl_connect.dst.type;
|
||||
memcpy(dst.a.val, event.acl_connect.dst.val, BT_ADDR_SIZE);
|
||||
|
||||
/* Not encrypted at this moment */
|
||||
err = bt_le_acl_conn_new(event.acl_connect.conn_handle,
|
||||
event.acl_connect.role,
|
||||
&dst, BT_SECURITY_L1);
|
||||
if (err) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
err = bt_le_acl_conn_connected_listener(event.acl_connect.conn_handle);
|
||||
assert(err == 0);
|
||||
|
||||
end:
|
||||
bt_le_host_unlock();
|
||||
|
||||
bt_le_gap_app_cb_evt(&event);
|
||||
}
|
||||
|
||||
static void handle_acl_disconnect_event_safe(struct bt_le_gap_app_param *param)
|
||||
{
|
||||
struct bt_le_gap_app_event event = {
|
||||
.type = BT_LE_GAP_APP_EVENT_ACL_DISCONNECT,
|
||||
};
|
||||
int err;
|
||||
|
||||
event.acl_disconnect.conn_handle = param->acl_disconnect.conn_handle;
|
||||
event.acl_disconnect.reason = param->acl_disconnect.reason;
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
err = bt_le_acl_conn_disconnected_listener(event.acl_disconnect.conn_handle,
|
||||
event.acl_disconnect.reason);
|
||||
if (err) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
err = bt_le_acl_conn_delete(event.acl_disconnect.conn_handle);
|
||||
assert(err == 0);
|
||||
|
||||
end:
|
||||
bt_le_host_unlock();
|
||||
|
||||
bt_le_gap_app_cb_evt(&event);
|
||||
}
|
||||
|
||||
static void handle_security_change_event_safe(struct bt_le_gap_app_param *param)
|
||||
{
|
||||
struct bt_le_gap_app_event event = {
|
||||
.type = BT_LE_GAP_APP_EVENT_SECURITY_CHANGE,
|
||||
};
|
||||
struct bt_conn *conn;
|
||||
bt_addr_le_t dst;
|
||||
bool encrypted;
|
||||
int err;
|
||||
|
||||
event.security_change.status = param->security_change.status;
|
||||
event.security_change.conn_handle = param->security_change.conn_handle;
|
||||
event.security_change.role = param->security_change.role;
|
||||
event.security_change.sec_level = param->security_change.sec_level;
|
||||
event.security_change.bonded = param->security_change.bonded;
|
||||
event.security_change.dst.type = param->security_change.dst.type;
|
||||
memcpy(event.security_change.dst.val, param->security_change.dst.val, BT_ADDR_SIZE);
|
||||
|
||||
encrypted = false;
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
if (event.security_change.status) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
conn = bt_le_acl_conn_find(event.security_change.conn_handle);
|
||||
if (conn == NULL) {
|
||||
dst.type = event.security_change.dst.type;
|
||||
memcpy(dst.a.val, event.security_change.dst.val, BT_ADDR_SIZE);
|
||||
|
||||
err = bt_le_acl_conn_new(event.security_change.conn_handle,
|
||||
event.security_change.role,
|
||||
&dst, event.security_change.sec_level);
|
||||
if (err) {
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
err = bt_le_acl_conn_update(event.security_change.conn_handle,
|
||||
event.security_change.sec_level,
|
||||
&encrypted);
|
||||
assert(err == 0);
|
||||
}
|
||||
|
||||
err = bt_le_acl_conn_security_changed_listener(event.security_change.conn_handle,
|
||||
event.security_change.sec_level);
|
||||
assert(err == 0);
|
||||
|
||||
if (encrypted) {
|
||||
/* TODO: check if bonded or not */
|
||||
err = bt_le_acl_conn_pairing_completed_listener(event.security_change.conn_handle, true);
|
||||
assert(err == 0);
|
||||
}
|
||||
|
||||
end:
|
||||
bt_le_host_unlock();
|
||||
|
||||
bt_le_gap_app_cb_evt(&event);
|
||||
}
|
||||
|
||||
static void handle_identity_resolve_event_safe(struct bt_le_gap_app_param *param)
|
||||
{
|
||||
struct bt_le_gap_app_event event = {
|
||||
.type = BT_LE_GAP_APP_EVENT_IDENTITY_RESOLVE,
|
||||
};
|
||||
bt_addr_le_t identity;
|
||||
bt_addr_le_t rpa;
|
||||
|
||||
event.identity_resolve.conn_handle = param->identity_resolve.conn_handle;
|
||||
event.identity_resolve.rpa.type = param->identity_resolve.rpa.type;
|
||||
memcpy(event.identity_resolve.rpa.val, param->identity_resolve.rpa.val, BT_ADDR_SIZE);
|
||||
event.identity_resolve.identity.type = param->identity_resolve.identity.type;
|
||||
memcpy(event.identity_resolve.identity.val, param->identity_resolve.identity.val, BT_ADDR_SIZE);
|
||||
|
||||
identity.type = event.identity_resolve.identity.type;
|
||||
memcpy(identity.a.val, event.identity_resolve.identity.val, BT_ADDR_SIZE);
|
||||
|
||||
rpa.type = event.identity_resolve.rpa.type;
|
||||
memcpy(rpa.a.val, event.identity_resolve.rpa.val, BT_ADDR_SIZE);
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
bt_le_acl_conn_identity_resolved_listener(event.identity_resolve.conn_handle, &rpa, &identity);
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
bt_le_gap_app_cb_evt(&event);
|
||||
}
|
||||
|
||||
static void handle_bond_delete_event_safe(struct bt_le_gap_app_param *param)
|
||||
{
|
||||
struct bt_le_gap_app_event event = {
|
||||
.type = BT_LE_GAP_APP_EVENT_BOND_DELETE,
|
||||
};
|
||||
bt_addr_le_t peer;
|
||||
|
||||
event.bond_delete.id = param->bond_delete.id;
|
||||
event.bond_delete.peer.type = param->bond_delete.peer.type;
|
||||
memcpy(event.bond_delete.peer.val, param->bond_delete.peer.val, BT_ADDR_SIZE);
|
||||
|
||||
peer.type = event.bond_delete.peer.type;
|
||||
memcpy(peer.a.val, event.bond_delete.peer.val, sizeof(peer.a.val));
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
bt_le_acl_conn_bond_deleted_listener(event.bond_delete.id, &peer);
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
bt_le_gap_app_cb_evt(&event);
|
||||
}
|
||||
|
||||
void bt_le_gap_handle_event(uint8_t *data, size_t data_len)
|
||||
{
|
||||
struct bt_le_gap_app_param *param;
|
||||
|
||||
assert(data && data_len);
|
||||
|
||||
param = (struct bt_le_gap_app_param *)data;
|
||||
|
||||
switch (param->type) {
|
||||
case BT_LE_GAP_APP_PARAM_EXT_SCAN_RECV:
|
||||
handle_ext_scan_recv_event_safe(param);
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_PA_SYNC:
|
||||
handle_pa_sync_event_safe(param);
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_PA_SYNC_LOST:
|
||||
handle_pa_sync_lost_event_safe(param);
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_PA_SYNC_RECV:
|
||||
handle_pa_sync_recv_event_safe(param);
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_ACL_CONNECT:
|
||||
handle_acl_connect_event_safe(param);
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_ACL_DISCONNECT:
|
||||
handle_acl_disconnect_event_safe(param);
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_SECURITY_CHANGE:
|
||||
handle_security_change_event_safe(param);
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_IDENTITY_RESOLVE:
|
||||
handle_identity_resolve_event_safe(param);
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_BOND_DELETE:
|
||||
handle_bond_delete_event_safe(param);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
void bt_le_gap_app_post_event(uint8_t type, void *param)
|
||||
{
|
||||
/* Currently type is not used */
|
||||
ARG_UNUSED(type);
|
||||
|
||||
bt_le_nimble_gap_post_event(param);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/bluetooth/buf.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
|
||||
#include <../host/hci_core.h>
|
||||
#include <../host/iso_internal.h>
|
||||
|
||||
#include "common/host.h"
|
||||
#include "common/app/gatt.h"
|
||||
|
||||
static bt_le_gatt_app_cb gatt_app_cb;
|
||||
|
||||
int bt_le_gatt_app_cb_register(bt_le_gatt_app_cb cb)
|
||||
{
|
||||
LOG_DBG("GattAppCbReg");
|
||||
|
||||
if (cb == NULL) {
|
||||
LOG_ERR("GattAppCbNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (gatt_app_cb) {
|
||||
LOG_WRN("GattAppCbExist");
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
gatt_app_cb = cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bt_le_gatt_app_cb_unregister(void)
|
||||
{
|
||||
if (gatt_app_cb) {
|
||||
LOG_DBG("GattAppCbUnreg");
|
||||
gatt_app_cb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void bt_le_gatt_app_cb_evt(struct bt_le_gatt_app_event *event)
|
||||
{
|
||||
if (gatt_app_cb) {
|
||||
gatt_app_cb(event);
|
||||
}
|
||||
}
|
||||
|
||||
void bt_le_gatt_app_mtu_change_event(uint16_t conn_handle, uint16_t mtu)
|
||||
{
|
||||
struct bt_le_gatt_app_event event = {
|
||||
.type = BT_LE_GATT_APP_EVENT_GATT_MTU_CHANGE,
|
||||
};
|
||||
|
||||
LOG_DBG("GattMtuChangeAppEvt[%u][%u]", conn_handle, mtu);
|
||||
|
||||
event.gatt_mtu_change.conn_handle = conn_handle;
|
||||
event.gatt_mtu_change.mtu = mtu;
|
||||
|
||||
bt_le_gatt_app_cb_evt(&event);
|
||||
}
|
||||
|
||||
void bt_le_gattc_app_disc_cmpl_event(struct bt_le_gattc_disc_cmpl_event *param)
|
||||
{
|
||||
struct bt_le_gatt_app_event event = {
|
||||
.type = BT_LE_GATT_APP_EVENT_GATTC_DISC_CMPL,
|
||||
};
|
||||
|
||||
LOG_DBG("GattcDiscCmplAppEvt[%u][%02x]", param->conn_handle, param->status);
|
||||
|
||||
event.gattc_disc_cmpl.status = param->status;
|
||||
event.gattc_disc_cmpl.conn_handle = param->conn_handle;
|
||||
|
||||
bt_le_gatt_app_cb_evt(&event);
|
||||
}
|
||||
|
||||
void bt_le_gatts_app_subscribe_event(struct bt_le_gatts_subscribe_event *param)
|
||||
{
|
||||
struct bt_le_gatt_app_event event = {
|
||||
.type = BT_LE_GATT_APP_EVENT_GATTS_SUBSCRIBE,
|
||||
};
|
||||
|
||||
/* LOG_DBG("GattsSubAppEvt[%d][%u][%u][%u][%u][%u][%u][%02x]", */
|
||||
/* err, param->conn_handle, param->attr_handle, param->prev_notify, */
|
||||
/* param->cur_notify, param->prev_indicate, param->cur_indicate, */
|
||||
/* param->reason); */
|
||||
|
||||
event.gatts_subscribe.conn_handle = param->conn_handle;
|
||||
event.gatts_subscribe.attr_handle = param->attr_handle;
|
||||
event.gatts_subscribe.prev_notify = param->prev_notify;
|
||||
event.gatts_subscribe.cur_notify = param->cur_notify;
|
||||
event.gatts_subscribe.prev_indicate = param->prev_indicate;
|
||||
event.gatts_subscribe.cur_indicate = param->cur_indicate;
|
||||
event.gatts_subscribe.reason = param->reason;
|
||||
|
||||
bt_le_gatt_app_cb_evt(&event);
|
||||
}
|
||||
|
||||
void bt_le_gatt_app_post_event(uint8_t type, void *param)
|
||||
{
|
||||
/* Currently type is not used */
|
||||
ARG_UNUSED(type);
|
||||
|
||||
bt_le_nimble_gatt_post_event(param);
|
||||
}
|
||||
@@ -0,0 +1,672 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2025 Nordic Semiconductor ASA
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/bluetooth/common/bt_str.h>
|
||||
|
||||
#include <../host/keys.h>
|
||||
#include <../host/conn_internal.h>
|
||||
#include <../host/iso_internal.h>
|
||||
|
||||
#include "common/host.h"
|
||||
#include "common/app/gap.h"
|
||||
|
||||
static struct bt_conn acl_conns[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);
|
||||
|
||||
static sys_slist_t auth_info_cbs = SYS_SLIST_STATIC_INIT(&auth_info_cbs);
|
||||
|
||||
_IDF_ONLY
|
||||
void bt_conn_get_acl_conns(struct bt_conn **conns, uint8_t *count)
|
||||
{
|
||||
LOG_DBG("GetAclConns[%u]", ARRAY_SIZE(acl_conns));
|
||||
|
||||
*conns = acl_conns;
|
||||
*count = ARRAY_SIZE(acl_conns);
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
bool bt_conn_is_peer_addr_le(const struct bt_conn *conn, uint8_t id,
|
||||
const bt_addr_le_t *peer)
|
||||
{
|
||||
/* LOG_DBG("IsLeAddr[%s][%s]", bt_addr_le_str(&conn->le.dst), bt_addr_le_str(peer)); */
|
||||
|
||||
ARG_UNUSED(id);
|
||||
|
||||
assert(conn && peer);
|
||||
|
||||
/* Check against conn dst address as it may be the identity address */
|
||||
if (bt_addr_le_eq(peer, &conn->le.dst)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_LIB_IDF
|
||||
struct bt_conn *bt_conn_ref(struct bt_conn *conn)
|
||||
{
|
||||
return conn;
|
||||
}
|
||||
|
||||
_LIB_IDF
|
||||
void bt_conn_unref(struct bt_conn *conn)
|
||||
{
|
||||
ARG_UNUSED(conn);
|
||||
}
|
||||
|
||||
_LIB_ONLY
|
||||
void bt_conn_foreach(enum bt_conn_type type,
|
||||
void (*func)(struct bt_conn *conn, void *data),
|
||||
void *data)
|
||||
{
|
||||
assert(type == BT_CONN_TYPE_LE);
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(acl_conns); i++) {
|
||||
struct bt_conn *conn = &acl_conns[i];
|
||||
|
||||
/* Add this to avoid unexpected error log */
|
||||
if (conn->type == BT_CONN_TYPE_LE) {
|
||||
func(&acl_conns[i], data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_LIB_ONLY
|
||||
const bt_addr_le_t *bt_conn_get_dst(const struct bt_conn *conn)
|
||||
{
|
||||
assert(conn);
|
||||
|
||||
return &conn->le.dst;
|
||||
}
|
||||
|
||||
_LIB_IDF
|
||||
uint8_t bt_conn_index(const struct bt_conn *conn)
|
||||
{
|
||||
ptrdiff_t index = 0;
|
||||
|
||||
assert(conn);
|
||||
|
||||
switch (conn->type) {
|
||||
case BT_CONN_TYPE_LE:
|
||||
index = conn - acl_conns;
|
||||
assert(index >= 0 && index < ARRAY_SIZE(acl_conns));
|
||||
break;
|
||||
case BT_CONN_TYPE_ISO:
|
||||
index = conn - iso_conns;
|
||||
assert(index >= 0 && index < ARRAY_SIZE(iso_conns));
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* LOG_DBG("ConnIdx[%u]", index); */
|
||||
|
||||
return (uint8_t)index;
|
||||
}
|
||||
|
||||
#if CONFIG_BT_SMP
|
||||
_IDF_ONLY
|
||||
int bt_conn_set_security(struct bt_conn *conn, bt_security_t sec)
|
||||
{
|
||||
/* This function is used by CIS to set security level,
|
||||
* currently always return failure here to make sure
|
||||
* that before creating CIS, the ACL connection is
|
||||
* encrypted.
|
||||
*/
|
||||
return -ENOTSUP;
|
||||
}
|
||||
#endif /* CONFIG_BT_SMP */
|
||||
|
||||
static enum bt_conn_state conn_internal_to_public_state(bt_conn_state_t state)
|
||||
{
|
||||
switch (state) {
|
||||
case BT_CONN_DISCONNECTED:
|
||||
case BT_CONN_DISCONNECT_COMPLETE:
|
||||
return BT_CONN_STATE_DISCONNECTED;
|
||||
case BT_CONN_INITIATING:
|
||||
return BT_CONN_STATE_CONNECTING;
|
||||
case BT_CONN_CONNECTED:
|
||||
return BT_CONN_STATE_CONNECTED;
|
||||
case BT_CONN_DISCONNECTING:
|
||||
return BT_CONN_STATE_DISCONNECTING;
|
||||
default:
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
_LIB_ONLY
|
||||
int bt_conn_get_info(const struct bt_conn *conn, struct bt_conn_info *info)
|
||||
{
|
||||
assert(conn && info);
|
||||
|
||||
info->type = conn->type;
|
||||
info->role = conn->role;
|
||||
info->id = conn->id;
|
||||
info->state = conn_internal_to_public_state(conn->state);
|
||||
info->le.dst = &conn->le.dst;
|
||||
info->le.interval = conn->le.interval;
|
||||
|
||||
if (conn->encrypt) {
|
||||
/* Currently the flags is updated for lib usage.
|
||||
* TODO:
|
||||
* Update the flags based on the security information.
|
||||
*/
|
||||
info->security.flags = (BT_SECURITY_FLAG_SC | BT_SECURITY_FLAG_OOB);
|
||||
info->security.level = conn->sec_level;
|
||||
info->security.enc_key_size = BT_ENC_KEY_SIZE_MAX; /* 16 octets */
|
||||
} else {
|
||||
info->security.flags = 0;
|
||||
info->security.level = BT_SECURITY_L1;
|
||||
info->security.enc_key_size = 0;
|
||||
}
|
||||
|
||||
/* LOG_DBG("ConnGetInfo[%u][%02x]", info->state, info->security.flags); */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_LIB_ONLY
|
||||
int bt_conn_cb_register(struct bt_conn_cb *cb)
|
||||
{
|
||||
LOG_DBG("ConnCbReg");
|
||||
|
||||
if (cb == NULL) {
|
||||
LOG_ERR("ConnCbNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sys_slist_find(&conn_cbs, &cb->_node, NULL)) {
|
||||
LOG_WRN("ConnCbExist");
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
sys_slist_append(&conn_cbs, &cb->_node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_conn_cb_register_safe(struct bt_conn_cb *cb)
|
||||
{
|
||||
int err;
|
||||
bt_le_host_lock();
|
||||
err = bt_conn_cb_register(cb);
|
||||
bt_le_host_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
_NOT_USED
|
||||
int bt_conn_cb_unregister(struct bt_conn_cb *cb)
|
||||
{
|
||||
LOG_DBG("ConnCbUnreg");
|
||||
|
||||
if (cb == NULL) {
|
||||
LOG_ERR("ConnCbNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!sys_slist_find_and_remove(&conn_cbs, &cb->_node)) {
|
||||
LOG_ERR("ConnCbNotFound");
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_LIB_ONLY
|
||||
int bt_conn_auth_info_cb_register(struct bt_conn_auth_info_cb *cb)
|
||||
{
|
||||
LOG_DBG("ConnAuthInfoCbReg");
|
||||
|
||||
if (cb == NULL) {
|
||||
LOG_ERR("ConnAuthInfoCbNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sys_slist_find(&auth_info_cbs, &cb->node, NULL)) {
|
||||
LOG_WRN("ConnAuthInfoCbExist");
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
sys_slist_append(&auth_info_cbs, &cb->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_NOT_USED
|
||||
int bt_conn_auth_info_cb_unregister(struct bt_conn_auth_info_cb *cb)
|
||||
{
|
||||
LOG_DBG("ConnAuthInfoCbUnreg");
|
||||
|
||||
if (cb == NULL) {
|
||||
LOG_ERR("ConnAuthInfoCbNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!sys_slist_find_and_remove(&auth_info_cbs, &cb->node)) {
|
||||
LOG_ERR("ConnAuthInfoCbNotFound");
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
struct bt_conn *bt_conn_new(struct bt_conn *conns, size_t size)
|
||||
{
|
||||
assert(conns);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
struct bt_conn *conn = &conns[i];
|
||||
|
||||
if (conn->type == BT_CONN_TYPE_NONE) {
|
||||
(void)memset(conn, 0, sizeof(struct bt_conn));
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct bt_conn *acl_conn_lookup(uint16_t handle)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(acl_conns); i++) {
|
||||
if (acl_conns[i].type == BT_CONN_TYPE_LE &&
|
||||
acl_conns[i].handle == handle) {
|
||||
return &acl_conns[i];
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INF("AclConnNotFound[%u]", handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct bt_conn *iso_conn_lookup(uint16_t handle)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(iso_conns); i++) {
|
||||
if (iso_conns[i].type == BT_CONN_TYPE_ISO &&
|
||||
iso_conns[i].handle == handle) {
|
||||
return &iso_conns[i];
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INF("IsoConnNotFound[%u]", handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
struct bt_conn *bt_conn_lookup_handle(uint16_t handle, enum bt_conn_type type)
|
||||
{
|
||||
struct bt_conn *conn = NULL;
|
||||
|
||||
assert(type == BT_CONN_TYPE_LE || type == BT_CONN_TYPE_ISO);
|
||||
|
||||
/* LOG_DBG("ConnLookupHdl[%u][%u]", handle, type); */
|
||||
|
||||
if (type == BT_CONN_TYPE_LE) {
|
||||
conn = acl_conn_lookup(handle);
|
||||
} else {
|
||||
conn = iso_conn_lookup(handle);
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
struct bt_conn *bt_le_acl_conn_find(uint16_t conn_handle)
|
||||
{
|
||||
/* LOG_DBG("AclConnFind[%u]", conn_handle); */
|
||||
|
||||
return bt_conn_lookup_handle(conn_handle, BT_CONN_TYPE_LE);
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
struct bt_conn *bt_le_acl_conn_find_safe(uint16_t conn_handle)
|
||||
{
|
||||
struct bt_conn *conn = NULL;
|
||||
/* LOG_DBG("AclConnFind[%u]", conn_handle); */
|
||||
bt_le_host_lock();
|
||||
conn = bt_conn_lookup_handle(conn_handle, BT_CONN_TYPE_LE);
|
||||
bt_le_host_unlock();
|
||||
return conn;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_acl_conn_new(uint16_t conn_handle,
|
||||
uint8_t role,
|
||||
bt_addr_le_t *dst,
|
||||
uint8_t sec_level)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
assert(dst);
|
||||
|
||||
LOG_DBG("AclNew[%u][%u][%u][%s]",
|
||||
conn_handle, role, sec_level, bt_addr_le_str(dst));
|
||||
|
||||
conn = bt_conn_new(acl_conns, ARRAY_SIZE(acl_conns));
|
||||
if (conn) {
|
||||
conn->handle = conn_handle;
|
||||
conn->type = BT_CONN_TYPE_LE;
|
||||
conn->role = role;
|
||||
conn->state = BT_CONN_CONNECTED;
|
||||
conn->sec_level = sec_level;
|
||||
memcpy(&conn->le.dst, dst, sizeof(conn->le.dst));
|
||||
} else {
|
||||
LOG_ERR("NoFreeConn[%u]", conn_handle);
|
||||
}
|
||||
|
||||
return (conn ? 0 : -ENOMEM);
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_acl_conn_delete(uint16_t conn_handle)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
LOG_DBG("AclConnDel[%u]", conn_handle);
|
||||
|
||||
conn = bt_le_acl_conn_find(conn_handle);
|
||||
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("NotConn[%d]", __LINE__);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
memset(conn, 0, sizeof(struct bt_conn));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_acl_conn_update(uint16_t conn_handle,
|
||||
uint8_t sec_level,
|
||||
bool *encrypted)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
LOG_DBG("AclConnUpd[%u][%u]", conn_handle, sec_level);
|
||||
|
||||
conn = bt_le_acl_conn_find(conn_handle);
|
||||
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("NotConn[%d]", __LINE__);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
if (encrypted) {
|
||||
*encrypted = false;
|
||||
}
|
||||
|
||||
if (conn->sec_level < sec_level) {
|
||||
/* No encryption to encryption */
|
||||
if (conn->encrypt == 0 && conn->sec_level == BT_SECURITY_L1) {
|
||||
conn->encrypt = 1;
|
||||
|
||||
if (encrypted) {
|
||||
*encrypted = true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DBG("SecLevelUpd[%u][%u]", conn->sec_level, sec_level);
|
||||
conn->sec_level = sec_level;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acl_conn_set_state(struct bt_conn *conn, bt_conn_state_t state)
|
||||
{
|
||||
LOG_DBG("AclConnSetState");
|
||||
|
||||
conn->state = state;
|
||||
|
||||
switch (conn->state) {
|
||||
case BT_CONN_CONNECTED:
|
||||
break;
|
||||
case BT_CONN_INITIATING:
|
||||
break;
|
||||
case BT_CONN_DISCONNECT_COMPLETE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void iso_conn_set_state(struct bt_conn *conn, bt_conn_state_t state)
|
||||
{
|
||||
LOG_DBG("IsoConnSetState[%u]", state);
|
||||
|
||||
conn->state = state;
|
||||
|
||||
switch (conn->state) {
|
||||
case BT_CONN_CONNECTED:
|
||||
bt_iso_connected(conn);
|
||||
break;
|
||||
case BT_CONN_INITIATING:
|
||||
break;
|
||||
case BT_CONN_DISCONNECT_COMPLETE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state)
|
||||
{
|
||||
bt_conn_state_t old_state;
|
||||
|
||||
assert(conn);
|
||||
assert(conn->type == BT_CONN_TYPE_LE || conn->type == BT_CONN_TYPE_ISO);
|
||||
|
||||
LOG_DBG("ConnSetState[%u][%u]", conn->state, state);
|
||||
|
||||
if (conn->state == state) {
|
||||
return;
|
||||
}
|
||||
|
||||
old_state = conn->state;
|
||||
|
||||
switch (old_state) {
|
||||
case BT_CONN_DISCONNECTED:
|
||||
/* TODO: check if anything to do */
|
||||
break;
|
||||
case BT_CONN_INITIATING:
|
||||
/* TODO: check if anything to do */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (conn->type == BT_CONN_TYPE_LE) {
|
||||
acl_conn_set_state(conn, state);
|
||||
} else {
|
||||
iso_conn_set_state(conn, state);
|
||||
}
|
||||
}
|
||||
|
||||
static int iso_disconnect(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
return bt_le_iso_disconnect(conn->handle, reason);
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_conn_disconnect(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
assert(conn);
|
||||
|
||||
LOG_DBG("ConnDisconnect[%u][%u][%02x]", conn->state, conn->type, reason);
|
||||
|
||||
switch (conn->state) {
|
||||
case BT_CONN_CONNECTED:
|
||||
if (conn->type == BT_CONN_TYPE_LE) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (conn->type == BT_CONN_TYPE_ISO) {
|
||||
return iso_disconnect(conn, reason);
|
||||
}
|
||||
|
||||
assert(0);
|
||||
case BT_CONN_DISCONNECTING:
|
||||
return 0;
|
||||
case BT_CONN_DISCONNECTED:
|
||||
default:
|
||||
return -ENOTCONN;
|
||||
}
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_acl_conn_connected_listener(uint16_t conn_handle)
|
||||
{
|
||||
struct bt_conn_cb *listener = NULL;
|
||||
struct bt_conn *conn;
|
||||
|
||||
LOG_DBG("AclConnConnectedListener[%u]", conn_handle);
|
||||
|
||||
conn = bt_le_acl_conn_find(conn_handle);
|
||||
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("NotConn[%d]", __LINE__);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&conn_cbs, listener, _node) {
|
||||
if (listener->connected) {
|
||||
listener->connected(conn, BT_HCI_ERR_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_acl_conn_disconnected_listener(uint16_t conn_handle, uint8_t reason)
|
||||
{
|
||||
struct bt_conn_cb *listener = NULL;
|
||||
struct bt_conn *conn;
|
||||
|
||||
LOG_DBG("AclConnDisconnectedListener[%u][%02x]", conn_handle, reason);
|
||||
|
||||
conn = bt_le_acl_conn_find(conn_handle);
|
||||
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("NotConn[%d]", __LINE__);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&conn_cbs, listener, _node) {
|
||||
if (listener->disconnected) {
|
||||
listener->disconnected(conn, reason);
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_BT_ISO_UNICAST
|
||||
bt_le_acl_conn_disconnected_gatt_listener(conn_handle);
|
||||
#endif /* CONFIG_BT_ISO_UNICAST */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_acl_conn_security_changed_listener(uint16_t conn_handle, bt_security_t level)
|
||||
{
|
||||
struct bt_conn_cb *listener = NULL;
|
||||
struct bt_conn *conn;
|
||||
|
||||
LOG_DBG("AclConnSecurityChangedListener[%u][%u]", conn_handle, level);
|
||||
|
||||
conn = bt_le_acl_conn_find(conn_handle);
|
||||
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("NotConn[%d]", __LINE__);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&conn_cbs, listener, _node) {
|
||||
if (listener->security_changed) {
|
||||
listener->security_changed(conn, level, BT_SECURITY_ERR_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_acl_conn_identity_resolved_listener(uint16_t conn_handle,
|
||||
const bt_addr_le_t *rpa,
|
||||
const bt_addr_le_t *identity)
|
||||
{
|
||||
struct bt_conn_cb *listener = NULL;
|
||||
struct bt_conn *conn;
|
||||
|
||||
LOG_DBG("AclConnIdentityResolvedListener[%u][%s]",
|
||||
conn_handle, bt_addr_le_str(rpa));
|
||||
LOG_DBG("AclConnIdentityResolvedListener[%s]",
|
||||
bt_addr_le_str(identity));
|
||||
|
||||
conn = bt_le_acl_conn_find(conn_handle);
|
||||
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("NotConn[%d]", __LINE__);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&conn_cbs, listener, _node) {
|
||||
if (listener->identity_resolved) {
|
||||
listener->identity_resolved(conn, rpa, identity);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_acl_conn_pairing_completed_listener(uint16_t conn_handle, bool bonded)
|
||||
{
|
||||
struct bt_conn_auth_info_cb *listener = NULL;
|
||||
struct bt_conn *conn;
|
||||
|
||||
LOG_DBG("AclConnPairingCompletedListener[%u][%u]", conn_handle, bonded);
|
||||
|
||||
conn = bt_le_acl_conn_find(conn_handle);
|
||||
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("NotConn[%d]", __LINE__);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&auth_info_cbs, listener, node) {
|
||||
if (listener->pairing_complete) {
|
||||
listener->pairing_complete(conn, bonded);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_acl_conn_bond_deleted_listener(uint8_t id, const bt_addr_le_t *peer)
|
||||
{
|
||||
struct bt_conn_auth_info_cb *listener = NULL;
|
||||
|
||||
LOG_DBG("AclConnBondDeletedListener[%02x][%s]", id, bt_addr_le_str(peer));
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&auth_info_cbs, listener, node) {
|
||||
if (listener->bond_deleted) {
|
||||
listener->bond_deleted(id, peer);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2025 Nordic Semiconductor ASA
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2025 NXP
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/bluetooth/buf.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
|
||||
#include <../host/hci_core.h>
|
||||
#include <../host/iso_internal.h>
|
||||
|
||||
#include "common/host.h"
|
||||
|
||||
struct cmd_data {
|
||||
/** HCI status of the command completion */
|
||||
uint8_t status;
|
||||
|
||||
/** The command OpCode that the buffer contains */
|
||||
uint16_t opcode;
|
||||
|
||||
/** The state to update when command completes with success. */
|
||||
struct bt_hci_cmd_state_set *state;
|
||||
};
|
||||
|
||||
static struct cmd_data cmd_data;
|
||||
|
||||
#define cmd(buf) (&cmd_data)
|
||||
|
||||
NET_BUF_POOL_FIXED_DEFINE(hci_cmd_pool, 1, 255, 0, NULL);
|
||||
|
||||
_IDF_ONLY
|
||||
struct net_buf *bt_hci_cmd_create(uint16_t opcode, uint8_t param_len)
|
||||
{
|
||||
struct bt_hci_cmd_hdr *hdr;
|
||||
struct net_buf *buf;
|
||||
|
||||
LOG_DBG("HciCmdCreate[%04x][%u]", opcode, param_len);
|
||||
|
||||
buf = net_buf_alloc(&hci_cmd_pool, K_NO_WAIT);
|
||||
assert(buf);
|
||||
|
||||
net_buf_reserve(buf, BT_BUF_RESERVE);
|
||||
|
||||
cmd(buf)->opcode = opcode;
|
||||
cmd(buf)->state = NULL;
|
||||
|
||||
hdr = net_buf_add(buf, sizeof(*hdr));
|
||||
hdr->opcode = sys_cpu_to_le16(opcode);
|
||||
hdr->param_len = param_len;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_hci_cmd_send_sync(uint16_t opcode,
|
||||
struct net_buf *buf,
|
||||
struct net_buf **rsp)
|
||||
{
|
||||
LOG_DBG("HciCmdSendSync[%04x]", opcode);
|
||||
|
||||
return bt_le_nimble_iso_cmd_send_sync(opcode, buf, rsp);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "common/host.h"
|
||||
#include "common/app/gap.h"
|
||||
#include "common/app/gatt.h"
|
||||
|
||||
static struct k_mutex host_mutex;
|
||||
|
||||
#define TIMEOUT_MS (5000 / portTICK_PERIOD_MS) /* 5s */
|
||||
|
||||
#if HOST_LOCK_DEBUG
|
||||
void bt_le_host_lock_debug(const char *func, int line)
|
||||
#else /* HOST_LOCK_DEBUG */
|
||||
void bt_le_host_lock(void)
|
||||
#endif /* HOST_LOCK_DEBUG */
|
||||
{
|
||||
/* LOG_DBG("%s: %d", func, line); */
|
||||
k_mutex_lock(&host_mutex, TIMEOUT_MS);
|
||||
}
|
||||
|
||||
#if HOST_LOCK_DEBUG
|
||||
void bt_le_host_unlock_debug(const char *func, int line)
|
||||
#else /* HOST_LOCK_DEBUG */
|
||||
void bt_le_host_unlock(void)
|
||||
#endif /* HOST_LOCK_DEBUG */
|
||||
{
|
||||
/* LOG_DBG("%s: %d", func, line); */
|
||||
k_mutex_unlock(&host_mutex);
|
||||
}
|
||||
|
||||
int bt_le_host_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
LOG_DBG("HostInit");
|
||||
|
||||
k_mutex_create(&host_mutex);
|
||||
|
||||
#if CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT
|
||||
err = bt_le_l2cap_init();
|
||||
if (err) {
|
||||
goto delete_mutex;
|
||||
}
|
||||
#endif /* CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT */
|
||||
|
||||
err = bt_le_iso_init();
|
||||
if (err) {
|
||||
goto deinit_l2cap;
|
||||
}
|
||||
|
||||
err = bt_le_iso_task_init();
|
||||
if (err) {
|
||||
goto deinit_iso;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
deinit_iso:
|
||||
bt_le_iso_deinit();
|
||||
deinit_l2cap:
|
||||
#if CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT
|
||||
bt_le_l2cap_deinit();
|
||||
delete_mutex:
|
||||
#endif /* CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT */
|
||||
k_mutex_delete(&host_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void bt_le_host_deinit(void)
|
||||
{
|
||||
LOG_DBG("HostDeinit");
|
||||
|
||||
#if CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT
|
||||
bt_le_l2cap_deinit();
|
||||
#endif /* CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT */
|
||||
bt_le_iso_deinit();
|
||||
bt_le_iso_task_deinit();
|
||||
|
||||
k_mutex_delete(&host_mutex);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2021 Nordic Semiconductor ASA
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef HOST_COMMON_ADV_H_
|
||||
#define HOST_COMMON_ADV_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
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_delete_safe(uint8_t adv_handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HOST_COMMON_ADV_H_ */
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef HOST_COMMON_APP_GAP_H_
|
||||
#define HOST_COMMON_APP_GAP_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "nimble/gap.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct bt_le_addr {
|
||||
uint8_t type;
|
||||
uint8_t val[6];
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_ext_scan_recv_param {
|
||||
uint16_t event_type;
|
||||
struct bt_le_addr addr;
|
||||
int8_t rssi;
|
||||
int8_t tx_power;
|
||||
uint8_t sid;
|
||||
uint8_t pri_phy;
|
||||
uint8_t sec_phy;
|
||||
uint16_t per_adv_itvl;
|
||||
uint8_t data_len;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_pa_sync_param {
|
||||
uint8_t status;
|
||||
uint16_t sync_handle;
|
||||
struct bt_le_addr addr;
|
||||
uint8_t sid;
|
||||
uint8_t adv_phy;
|
||||
uint16_t per_adv_itvl;
|
||||
uint8_t adv_ca;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_pa_sync_lost_param {
|
||||
uint16_t sync_handle;
|
||||
uint8_t reason;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_pa_sync_recv_param {
|
||||
uint16_t sync_handle;
|
||||
int8_t tx_power;
|
||||
int8_t rssi;
|
||||
uint8_t data_status;
|
||||
uint8_t data_len;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_acl_connect_param {
|
||||
uint8_t status;
|
||||
uint16_t conn_handle;
|
||||
uint8_t role;
|
||||
struct bt_le_addr dst;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_acl_disconnect_param {
|
||||
uint16_t conn_handle;
|
||||
uint8_t reason;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_security_change_param {
|
||||
uint8_t status;
|
||||
uint16_t conn_handle;
|
||||
uint8_t role;
|
||||
uint8_t sec_level;
|
||||
uint8_t bonded : 1;
|
||||
struct bt_le_addr dst;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_identity_resolve_param {
|
||||
uint16_t conn_handle;
|
||||
struct bt_le_addr rpa;
|
||||
struct bt_le_addr identity;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_bond_delete_param {
|
||||
uint8_t id;
|
||||
struct bt_le_addr peer;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_biginfo_recv_param {
|
||||
uint16_t sync_handle;
|
||||
uint8_t num_bis;
|
||||
uint8_t nse;
|
||||
uint16_t iso_interval;
|
||||
uint8_t bn;
|
||||
uint8_t pto;
|
||||
uint8_t irc;
|
||||
uint16_t max_pdu;
|
||||
uint32_t sdu_interval;
|
||||
uint16_t max_sdu;
|
||||
uint8_t phy;
|
||||
uint8_t framing;
|
||||
uint8_t encryption;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_param {
|
||||
uint8_t type;
|
||||
|
||||
union {
|
||||
struct bt_le_gap_app_ext_scan_recv_param ext_scan_recv;
|
||||
struct bt_le_gap_app_pa_sync_param pa_sync;
|
||||
struct bt_le_gap_app_pa_sync_lost_param pa_sync_lost;
|
||||
struct bt_le_gap_app_pa_sync_recv_param pa_sync_recv;
|
||||
struct bt_le_gap_app_acl_connect_param acl_connect;
|
||||
struct bt_le_gap_app_acl_disconnect_param acl_disconnect;
|
||||
struct bt_le_gap_app_security_change_param security_change;
|
||||
struct bt_le_gap_app_identity_resolve_param identity_resolve;
|
||||
struct bt_le_gap_app_bond_delete_param bond_delete;
|
||||
};
|
||||
};
|
||||
|
||||
enum bt_le_gap_app_param_type {
|
||||
BT_LE_GAP_APP_PARAM_EXT_SCAN_RECV,
|
||||
BT_LE_GAP_APP_PARAM_PA_SYNC,
|
||||
BT_LE_GAP_APP_PARAM_PA_SYNC_LOST,
|
||||
BT_LE_GAP_APP_PARAM_PA_SYNC_RECV,
|
||||
BT_LE_GAP_APP_PARAM_ACL_CONNECT,
|
||||
BT_LE_GAP_APP_PARAM_ACL_DISCONNECT,
|
||||
BT_LE_GAP_APP_PARAM_SECURITY_CHANGE,
|
||||
BT_LE_GAP_APP_PARAM_IDENTITY_RESOLVE,
|
||||
BT_LE_GAP_APP_PARAM_BOND_DELETE,
|
||||
|
||||
BT_LE_GAP_APP_PARAM_MAX,
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_event_ext_scan_recv {
|
||||
uint16_t event_type;
|
||||
struct bt_le_addr addr;
|
||||
int8_t rssi;
|
||||
int8_t tx_power;
|
||||
uint8_t sid;
|
||||
uint8_t pri_phy;
|
||||
uint8_t sec_phy;
|
||||
uint16_t per_adv_itvl;
|
||||
uint8_t data_len;
|
||||
uint8_t *data; /* Will be freed after the event is processed */
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_event_pa_sync {
|
||||
uint8_t status;
|
||||
uint16_t sync_handle;
|
||||
struct bt_le_addr addr;
|
||||
uint8_t sid;
|
||||
uint8_t adv_phy;
|
||||
uint16_t per_adv_itvl;
|
||||
uint8_t adv_ca;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_event_pa_sync_lost {
|
||||
uint16_t sync_handle;
|
||||
uint8_t reason;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_event_pa_sync_recv {
|
||||
uint16_t sync_handle;
|
||||
int8_t tx_power;
|
||||
int8_t rssi;
|
||||
uint8_t data_status;
|
||||
uint8_t data_len;
|
||||
uint8_t *data; /* Will be freed after the event is processed */
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_event_acl_connect {
|
||||
uint8_t status;
|
||||
uint16_t conn_handle;
|
||||
uint8_t role;
|
||||
struct bt_le_addr dst;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_event_acl_disconnect {
|
||||
uint16_t conn_handle;
|
||||
uint8_t reason;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_event_security_change {
|
||||
uint8_t status;
|
||||
uint16_t conn_handle;
|
||||
uint8_t role;
|
||||
uint8_t sec_level;
|
||||
uint8_t bonded : 1;
|
||||
struct bt_le_addr dst;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_event_identity_resolve {
|
||||
uint16_t conn_handle;
|
||||
struct bt_le_addr rpa;
|
||||
struct bt_le_addr identity;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_event_bond_delete {
|
||||
uint8_t id;
|
||||
struct bt_le_addr peer;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_event_biginfo_recv {
|
||||
uint16_t sync_handle;
|
||||
uint8_t num_bis;
|
||||
uint8_t nse;
|
||||
uint16_t iso_interval;
|
||||
uint8_t bn;
|
||||
uint8_t pto;
|
||||
uint8_t irc;
|
||||
uint16_t max_pdu;
|
||||
uint32_t sdu_interval;
|
||||
uint16_t max_sdu;
|
||||
uint8_t phy;
|
||||
uint8_t framing;
|
||||
uint8_t encryption;
|
||||
};
|
||||
|
||||
struct bt_le_gap_app_event {
|
||||
uint8_t type;
|
||||
|
||||
union {
|
||||
struct bt_le_gap_app_event_ext_scan_recv ext_scan_recv;
|
||||
struct bt_le_gap_app_event_pa_sync pa_sync;
|
||||
struct bt_le_gap_app_event_pa_sync_lost pa_sync_lost;
|
||||
struct bt_le_gap_app_event_pa_sync_recv pa_sync_recv;
|
||||
struct bt_le_gap_app_event_acl_connect acl_connect;
|
||||
struct bt_le_gap_app_event_acl_disconnect acl_disconnect;
|
||||
struct bt_le_gap_app_event_security_change security_change;
|
||||
struct bt_le_gap_app_event_identity_resolve identity_resolve;
|
||||
struct bt_le_gap_app_event_bond_delete bond_delete;
|
||||
struct bt_le_gap_app_event_biginfo_recv biginfo_recv;
|
||||
};
|
||||
};
|
||||
|
||||
enum bt_le_gap_app_event_type {
|
||||
BT_LE_GAP_APP_EVENT_EXT_SCAN_RECV,
|
||||
BT_LE_GAP_APP_EVENT_PA_SYNC,
|
||||
BT_LE_GAP_APP_EVENT_PA_SYNC_LOST,
|
||||
BT_LE_GAP_APP_EVENT_PA_SYNC_RECV,
|
||||
BT_LE_GAP_APP_EVENT_ACL_CONNECT,
|
||||
BT_LE_GAP_APP_EVENT_ACL_DISCONNECT,
|
||||
BT_LE_GAP_APP_EVENT_SECURITY_CHANGE,
|
||||
BT_LE_GAP_APP_EVENT_IDENTITY_RESOLVE,
|
||||
BT_LE_GAP_APP_EVENT_BOND_DELETE,
|
||||
BT_LE_GAP_APP_EVENT_BIGINFO_RECV,
|
||||
|
||||
BT_LE_GAP_APP_EVENT_MAX,
|
||||
};
|
||||
|
||||
typedef void (*bt_le_gap_app_cb)(struct bt_le_gap_app_event *event);
|
||||
|
||||
int bt_le_gap_app_cb_register(bt_le_gap_app_cb cb);
|
||||
|
||||
void bt_le_gap_app_cb_unregister(void);
|
||||
|
||||
void bt_le_gap_app_biginfo_event(uint8_t *param);
|
||||
|
||||
void bt_le_gap_handle_event(uint8_t *data, size_t data_len);
|
||||
|
||||
void bt_le_gap_app_post_event(uint8_t type, void *param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HOST_COMMON_APP_GAP_H_ */
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef HOST_COMMON_APP_GATT_H_
|
||||
#define HOST_COMMON_APP_GATT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "nimble/gatt.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct bt_le_gatt_app_event_gatt_mtu_change {
|
||||
uint16_t conn_handle;
|
||||
uint16_t mtu;
|
||||
};
|
||||
|
||||
struct bt_le_gatt_app_event_gattc_disc_cmpl {
|
||||
uint8_t status;
|
||||
uint16_t conn_handle;
|
||||
};
|
||||
|
||||
struct bt_le_gatt_app_event_gatts_subscribe {
|
||||
uint16_t conn_handle;
|
||||
uint16_t attr_handle;
|
||||
uint8_t prev_notify : 1;
|
||||
uint8_t cur_notify : 1;
|
||||
uint8_t prev_indicate : 1;
|
||||
uint8_t cur_indicate : 1;
|
||||
uint8_t reason;
|
||||
};
|
||||
|
||||
struct bt_le_gatt_app_event {
|
||||
uint8_t type;
|
||||
|
||||
union {
|
||||
struct bt_le_gatt_app_event_gatt_mtu_change gatt_mtu_change;
|
||||
struct bt_le_gatt_app_event_gattc_disc_cmpl gattc_disc_cmpl;
|
||||
struct bt_le_gatt_app_event_gatts_subscribe gatts_subscribe;
|
||||
};
|
||||
};
|
||||
|
||||
enum bt_le_gatt_app_event_type {
|
||||
BT_LE_GATT_APP_EVENT_GATT_MTU_CHANGE,
|
||||
BT_LE_GATT_APP_EVENT_GATTC_DISC_CMPL,
|
||||
BT_LE_GATT_APP_EVENT_GATTS_SUBSCRIBE,
|
||||
|
||||
BT_LE_GATT_APP_EVENT_MAX,
|
||||
};
|
||||
|
||||
typedef void (*bt_le_gatt_app_cb)(struct bt_le_gatt_app_event *event);
|
||||
|
||||
int bt_le_gatt_app_cb_register(bt_le_gatt_app_cb cb);
|
||||
|
||||
void bt_le_gatt_app_cb_unregister(void);
|
||||
|
||||
void bt_le_gatt_app_mtu_change_event(uint16_t conn_handle, uint16_t mtu);
|
||||
|
||||
void bt_le_gattc_app_disc_cmpl_event(struct bt_le_gattc_disc_cmpl_event *param);
|
||||
|
||||
void bt_le_gatts_app_subscribe_event(struct bt_le_gatts_subscribe_event *param);
|
||||
|
||||
void bt_le_gatt_app_post_event(uint8_t type, void *param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HOST_COMMON_APP_GATT_H_ */
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2025 Nordic Semiconductor ASA
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef HOST_COMMON_CONN_H_
|
||||
#define HOST_COMMON_CONN_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "nimble/gap.h"
|
||||
#include "nimble/iso.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void bt_conn_get_acl_conns(struct bt_conn **conns, uint8_t *count);
|
||||
|
||||
struct bt_conn *bt_le_acl_conn_find(uint16_t conn_handle);
|
||||
|
||||
struct bt_conn *bt_le_acl_conn_find_safe(uint16_t conn_handle);
|
||||
|
||||
int bt_le_acl_conn_new(uint16_t conn_handle,
|
||||
uint8_t role,
|
||||
bt_addr_le_t *dst,
|
||||
uint8_t sec_level);
|
||||
|
||||
int bt_le_acl_conn_delete(uint16_t conn_handle);
|
||||
|
||||
int bt_le_acl_conn_update(uint16_t conn_handle,
|
||||
uint8_t sec_level,
|
||||
bool *encrypted);
|
||||
|
||||
int bt_le_acl_conn_connected_listener(uint16_t conn_handle);
|
||||
|
||||
int bt_le_acl_conn_disconnected_listener(uint16_t conn_handle, uint8_t reason);
|
||||
|
||||
int bt_le_acl_conn_security_changed_listener(uint16_t conn_handle, bt_security_t level);
|
||||
|
||||
int bt_le_acl_conn_identity_resolved_listener(uint16_t conn_handle,
|
||||
const bt_addr_le_t *rpa,
|
||||
const bt_addr_le_t *identity);
|
||||
|
||||
int bt_le_acl_conn_pairing_completed_listener(uint16_t conn_handle, bool bonded);
|
||||
|
||||
int bt_le_acl_conn_bond_deleted_listener(uint8_t id, const bt_addr_le_t *peer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HOST_COMMON_CONN_H_ */
|
||||
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2023 Nordic Semiconductor ASA
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef HOST_COMMON_GATT_H_
|
||||
#define HOST_COMMON_GATT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zephyr/autoconf.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
|
||||
#include "nimble/gatt.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct bt_le_gatt_mtu_change_event {
|
||||
uint16_t conn_handle;
|
||||
uint16_t mtu;
|
||||
};
|
||||
|
||||
enum {
|
||||
GATTC_DISC_TYPE_DUMMY,
|
||||
GATTC_DISC_TYPE_SVC_BY_UUID,
|
||||
GATTC_DISC_TYPE_INC_SVCS,
|
||||
GATTC_DISC_TYPE_CHRS_BY_UUID,
|
||||
GATTC_DISC_TYPE_ALL_CHRS,
|
||||
GATTC_DISC_TYPE_ALL_DSCS, /* Not used for now */
|
||||
|
||||
GATTC_DISC_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct bt_le_gattc_discover_event {
|
||||
uint8_t type;
|
||||
uint16_t conn_handle;
|
||||
uint16_t uuid;
|
||||
struct bt_gatt_discover_params *params;
|
||||
};
|
||||
|
||||
struct bt_le_gattc_disc_cmpl_event {
|
||||
uint8_t status;
|
||||
uint16_t conn_handle;
|
||||
};
|
||||
|
||||
struct bt_le_gatts_subscribe_event {
|
||||
uint16_t conn_handle;
|
||||
uint16_t attr_handle;
|
||||
uint8_t prev_notify : 1;
|
||||
uint8_t cur_notify : 1;
|
||||
uint8_t prev_indicate : 1;
|
||||
uint8_t cur_indicate : 1;
|
||||
uint8_t reason;
|
||||
};
|
||||
|
||||
struct bt_le_gattc_notify_rx_event {
|
||||
bool is_notify;
|
||||
uint16_t conn_handle;
|
||||
uint16_t attr_handle;
|
||||
uint16_t len;
|
||||
uint8_t *value;
|
||||
};
|
||||
|
||||
struct bt_le_gatts_notify_tx_event {
|
||||
bool is_notify;
|
||||
uint16_t conn_handle;
|
||||
uint16_t attr_handle;
|
||||
uint8_t status;
|
||||
};
|
||||
|
||||
struct bt_le_gatt_event_param {
|
||||
uint8_t type;
|
||||
|
||||
union {
|
||||
struct bt_le_gatt_mtu_change_event gatt_mtu_change;
|
||||
struct bt_le_gattc_discover_event gattc_discover;
|
||||
struct bt_le_gattc_disc_cmpl_event gattc_disc_cmpl;
|
||||
struct bt_le_gatts_subscribe_event gatts_subscribe;
|
||||
struct bt_le_gattc_notify_rx_event gattc_notify_rx;
|
||||
struct bt_le_gatts_notify_tx_event gatts_notify_tx;
|
||||
};
|
||||
};
|
||||
|
||||
enum {
|
||||
BT_LE_GATT_MTU_CHANGE_EVENT,
|
||||
BT_LE_GATTC_DISCOVER_EVENT,
|
||||
BT_LE_GATTC_DISC_CMPL_EVENT,
|
||||
BT_LE_GATTS_SUBSCRIBE_EVENT,
|
||||
BT_LE_GATTC_NOTIFY_RX_EVENT,
|
||||
BT_LE_GATTS_NOTIFY_TX_EVENT,
|
||||
|
||||
BT_LE_GATT_EVENT_MAX,
|
||||
};
|
||||
|
||||
static inline char *audio_svc_uuid_to_str(uint16_t uuid)
|
||||
{
|
||||
switch (uuid) {
|
||||
case BT_UUID_GAP_VAL:
|
||||
return "GAP";
|
||||
case BT_UUID_GATT_VAL:
|
||||
return "GATT";
|
||||
case BT_UUID_AICS_VAL:
|
||||
return "AICS";
|
||||
case BT_UUID_CAS_VAL:
|
||||
return "CAS";
|
||||
case BT_UUID_VCS_VAL:
|
||||
return "VCS";
|
||||
case BT_UUID_VOCS_VAL:
|
||||
return "VOCS";
|
||||
case BT_UUID_CSIS_VAL:
|
||||
return "CSIS";
|
||||
case BT_UUID_MCS_VAL:
|
||||
return "MCS";
|
||||
case BT_UUID_GMCS_VAL:
|
||||
return "GMCS";
|
||||
case BT_UUID_TBS_VAL:
|
||||
return "TBS";
|
||||
case BT_UUID_GTBS_VAL:
|
||||
return "GTBS";
|
||||
case BT_UUID_MICS_VAL:
|
||||
return "MICS";
|
||||
case BT_UUID_ASCS_VAL:
|
||||
return "ASCS";
|
||||
case BT_UUID_BASS_VAL:
|
||||
return "BASS";
|
||||
case BT_UUID_PACS_VAL:
|
||||
return "PACS";
|
||||
case BT_UUID_BASIC_AUDIO_VAL:
|
||||
return "BASIC_AUDIO";
|
||||
case BT_UUID_HAS_VAL:
|
||||
return "HAS";
|
||||
case BT_UUID_TMAS_VAL:
|
||||
return "TMAS";
|
||||
case BT_UUID_PBA_VAL:
|
||||
return "PBA";
|
||||
case BT_UUID_GMAS_VAL:
|
||||
return "GMAS";
|
||||
case BT_UUID_OTS_VAL:
|
||||
return "OTS";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static inline char *audio_chrc_uuid_to_str(uint16_t uuid)
|
||||
{
|
||||
switch (uuid) {
|
||||
case BT_UUID_OTS_FEATURE_VAL:
|
||||
return "OTS_FEATURE";
|
||||
case BT_UUID_OTS_NAME_VAL:
|
||||
return "OTS_NAME";
|
||||
case BT_UUID_OTS_TYPE_VAL:
|
||||
return "OTS_TYPE";
|
||||
case BT_UUID_OTS_SIZE_VAL:
|
||||
return "OTS_SIZE";
|
||||
case BT_UUID_OTS_FIRST_CREATED_VAL:
|
||||
return "OTS_FIRST_CREATED";
|
||||
case BT_UUID_OTS_LAST_MODIFIED_VAL:
|
||||
return "OTS_LAST_MODIFIED";
|
||||
case BT_UUID_OTS_ID_VAL:
|
||||
return "OTS_ID";
|
||||
case BT_UUID_OTS_PROPERTIES_VAL:
|
||||
return "OTS_PROPERTIES";
|
||||
case BT_UUID_OTS_ACTION_CP_VAL:
|
||||
return "OTS_ACTION_CP";
|
||||
case BT_UUID_OTS_LIST_CP_VAL:
|
||||
return "OTS_LIST_CP";
|
||||
case BT_UUID_OTS_LIST_FILTER_VAL:
|
||||
return "OTS_LIST_FILTER";
|
||||
case BT_UUID_OTS_CHANGED_VAL:
|
||||
return "OTS_CHANGED";
|
||||
case BT_UUID_GATT_TMAPR_VAL:
|
||||
return "TMAP_ROLE";
|
||||
case BT_UUID_AICS_STATE_VAL:
|
||||
return "AICS_STATE";
|
||||
case BT_UUID_AICS_GAIN_SETTINGS_VAL:
|
||||
return "AICS_GAIN_SETTINGS";
|
||||
case BT_UUID_AICS_INPUT_TYPE_VAL:
|
||||
return "AICS_INPUT_TYPE";
|
||||
case BT_UUID_AICS_INPUT_STATUS_VAL:
|
||||
return "AICS_INPUT_STATUS";
|
||||
case BT_UUID_AICS_CONTROL_VAL:
|
||||
return "AICS_CONTROL";
|
||||
case BT_UUID_AICS_DESCRIPTION_VAL:
|
||||
return "AICS_DESCRIPTION";
|
||||
case BT_UUID_VCS_STATE_VAL:
|
||||
return "VCS_STATE";
|
||||
case BT_UUID_VCS_CONTROL_VAL:
|
||||
return "VCS_CONTROL";
|
||||
case BT_UUID_VCS_FLAGS_VAL:
|
||||
return "VCS_FLAGS";
|
||||
case BT_UUID_VOCS_STATE_VAL:
|
||||
return "VOCS_STATE";
|
||||
case BT_UUID_VOCS_LOCATION_VAL:
|
||||
return "VOCS_LOCATION";
|
||||
case BT_UUID_VOCS_CONTROL_VAL:
|
||||
return "VOCS_CONTROL";
|
||||
case BT_UUID_VOCS_DESCRIPTION_VAL:
|
||||
return "VOCS_DESCRIPTION";
|
||||
case BT_UUID_CSIS_SIRK_VAL:
|
||||
return "CSIS_SIRK";
|
||||
case BT_UUID_CSIS_SET_SIZE_VAL:
|
||||
return "CSIS_SET_SIZE";
|
||||
case BT_UUID_CSIS_SET_LOCK_VAL:
|
||||
return "CSIS_SET_LOCK";
|
||||
case BT_UUID_CSIS_RANK_VAL:
|
||||
return "CSIS_RANK";
|
||||
case BT_UUID_MCS_PLAYER_NAME_VAL:
|
||||
return "MCS_PLAYER_NAME";
|
||||
case BT_UUID_MCS_ICON_OBJ_ID_VAL:
|
||||
return "MCS_ICON_OBJ_ID";
|
||||
case BT_UUID_MCS_ICON_URL_VAL:
|
||||
return "MCS_ICON_URL";
|
||||
case BT_UUID_MCS_TRACK_CHANGED_VAL:
|
||||
return "MCS_TRACK_CHANGED";
|
||||
case BT_UUID_MCS_TRACK_TITLE_VAL:
|
||||
return "MCS_TRACK_TITLE";
|
||||
case BT_UUID_MCS_TRACK_DURATION_VAL:
|
||||
return "MCS_TRACK_DURATION";
|
||||
case BT_UUID_MCS_TRACK_POSITION_VAL:
|
||||
return "MCS_TRACK_POSITION";
|
||||
case BT_UUID_MCS_PLAYBACK_SPEED_VAL:
|
||||
return "MCS_PLAYBACK_SPEED";
|
||||
case BT_UUID_MCS_SEEKING_SPEED_VAL:
|
||||
return "MCS_SEEKING_SPEED";
|
||||
case BT_UUID_MCS_TRACK_SEGMENTS_OBJ_ID_VAL:
|
||||
return "MCS_TRACK_SEGMENTS_OBJ_ID";
|
||||
case BT_UUID_MCS_CURRENT_TRACK_OBJ_ID_VAL:
|
||||
return "MCS_CURRENT_TRACK_OBJ_ID";
|
||||
case BT_UUID_MCS_NEXT_TRACK_OBJ_ID_VAL:
|
||||
return "MCS_NEXT_TRACK_OBJ_ID";
|
||||
case BT_UUID_MCS_PARENT_GROUP_OBJ_ID_VAL:
|
||||
return "MCS_PARENT_GROUP_OBJ_ID";
|
||||
case BT_UUID_MCS_CURRENT_GROUP_OBJ_ID_VAL:
|
||||
return "MCS_CURRENT_GROUP_OBJ_ID";
|
||||
case BT_UUID_MCS_PLAYING_ORDER_VAL:
|
||||
return "MCS_PLAYING_ORDER";
|
||||
case BT_UUID_MCS_PLAYING_ORDERS_VAL:
|
||||
return "MCS_PLAYING_ORDERS";
|
||||
case BT_UUID_MCS_MEDIA_STATE_VAL:
|
||||
return "MCS_MEDIA_STATE";
|
||||
case BT_UUID_MCS_MEDIA_CONTROL_POINT_VAL:
|
||||
return "MCS_MEDIA_CONTROL_POINT";
|
||||
case BT_UUID_MCS_MEDIA_CONTROL_OPCODES_VAL:
|
||||
return "MCS_MEDIA_CONTROL_OPCODES";
|
||||
case BT_UUID_MCS_SEARCH_RESULTS_OBJ_ID_VAL:
|
||||
return "MCS_SEARCH_RESULTS_OBJ_ID";
|
||||
case BT_UUID_MCS_SEARCH_CONTROL_POINT_VAL:
|
||||
return "MCS_SEARCH_CONTROL_POINT";
|
||||
case BT_UUID_TBS_PROVIDER_NAME_VAL:
|
||||
return "TBS_PROVIDER_NAME";
|
||||
case BT_UUID_TBS_UCI_VAL:
|
||||
return "TBS_UCI";
|
||||
case BT_UUID_TBS_TECHNOLOGY_VAL:
|
||||
return "TBS_TECHNOLOGY";
|
||||
case BT_UUID_TBS_URI_LIST_VAL:
|
||||
return "TBS_URI_LIST";
|
||||
case BT_UUID_TBS_SIGNAL_STRENGTH_VAL:
|
||||
return "TBS_SIGNAL_STRENGTH";
|
||||
case BT_UUID_TBS_SIGNAL_INTERVAL_VAL:
|
||||
return "TBS_SIGNAL_INTERVAL";
|
||||
case BT_UUID_TBS_LIST_CURRENT_CALLS_VAL:
|
||||
return "TBS_LIST_CURRENT_CALLS";
|
||||
case BT_UUID_CCID_VAL:
|
||||
return "CCID";
|
||||
case BT_UUID_TBS_STATUS_FLAGS_VAL:
|
||||
return "TBS_STATUS_FLAGS";
|
||||
case BT_UUID_TBS_INCOMING_URI_VAL:
|
||||
return "TBS_INCOMING_URI";
|
||||
case BT_UUID_TBS_CALL_STATE_VAL:
|
||||
return "TBS_CALL_STATE";
|
||||
case BT_UUID_TBS_CALL_CONTROL_POINT_VAL:
|
||||
return "TBS_CALL_CONTROL_POINT";
|
||||
case BT_UUID_TBS_OPTIONAL_OPCODES_VAL:
|
||||
return "TBS_OPTIONAL_OPCODES";
|
||||
case BT_UUID_TBS_TERMINATE_REASON_VAL:
|
||||
return "TBS_TERMINATE_REASON";
|
||||
case BT_UUID_TBS_INCOMING_CALL_VAL:
|
||||
return "TBS_INCOMING_CALL";
|
||||
case BT_UUID_TBS_FRIENDLY_NAME_VAL:
|
||||
return "TBS_FRIENDLY_NAME";
|
||||
case BT_UUID_MICS_MUTE_VAL:
|
||||
return "MICS_MUTE";
|
||||
case BT_UUID_ASCS_ASE_SNK_VAL:
|
||||
return "ASCS_ASE_SNK";
|
||||
case BT_UUID_ASCS_ASE_SRC_VAL:
|
||||
return "ASCS_ASE_SRC";
|
||||
case BT_UUID_ASCS_ASE_CP_VAL:
|
||||
return "ASCS_ASE_CP";
|
||||
case BT_UUID_BASS_CONTROL_POINT_VAL:
|
||||
return "BASS_CP";
|
||||
case BT_UUID_BASS_RECV_STATE_VAL:
|
||||
return "BASS_RECV_STATE";
|
||||
case BT_UUID_PACS_SNK_VAL:
|
||||
return "PACS_SNK";
|
||||
case BT_UUID_PACS_SNK_LOC_VAL:
|
||||
return "PACS_SNK_LOC";
|
||||
case BT_UUID_PACS_SRC_VAL:
|
||||
return "PACS_SRC";
|
||||
case BT_UUID_PACS_SRC_LOC_VAL:
|
||||
return "PACS_SRC_LOC";
|
||||
case BT_UUID_PACS_AVAILABLE_CONTEXT_VAL:
|
||||
return "PACS_AVAILABLE_CONTEXT";
|
||||
case BT_UUID_PACS_SUPPORTED_CONTEXT_VAL:
|
||||
return "PACS_SUPPORTED_CONTEXT";
|
||||
case BT_UUID_HAS_HEARING_AID_FEATURES_VAL:
|
||||
return "HAS_HEARING_AID_FEATURES";
|
||||
case BT_UUID_HAS_PRESET_CONTROL_POINT_VAL:
|
||||
return "HAS_PRESET_CONTROL_POINT";
|
||||
case BT_UUID_HAS_ACTIVE_PRESET_INDEX_VAL:
|
||||
return "HAS_ACTIVE_PRESET_INDEX";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
struct gattc_sub {
|
||||
uint8_t id;
|
||||
bt_addr_le_t peer;
|
||||
sys_slist_t list;
|
||||
};
|
||||
|
||||
struct gatt_incl {
|
||||
uint16_t start_handle;
|
||||
uint16_t end_handle;
|
||||
uint16_t uuid16;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct gatt_chrc {
|
||||
uint8_t properties;
|
||||
uint16_t value_handle;
|
||||
union {
|
||||
uint16_t uuid16;
|
||||
uint8_t uuid[16];
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
struct bt_att_write_req {
|
||||
uint16_t handle;
|
||||
uint8_t value[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct notify_data {
|
||||
const struct bt_gatt_attr *attr;
|
||||
uint16_t handle;
|
||||
int err;
|
||||
uint16_t type;
|
||||
union {
|
||||
struct bt_gatt_notify_params *nfy_params;
|
||||
struct bt_gatt_indicate_params *ind_params;
|
||||
};
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
int bt_gatts_sub_changed(uint16_t conn_handle,
|
||||
uint16_t ccc_handle,
|
||||
uint8_t cur_notify,
|
||||
uint8_t cur_indicate,
|
||||
uint8_t reason);
|
||||
|
||||
int bt_gattc_disc_start_safe(uint16_t conn_handle);
|
||||
|
||||
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_gatt_handle_event(uint8_t *data, size_t data_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HOST_COMMON_GATT_H_ */
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef HOST_COMMON_HOST_H_
|
||||
#define HOST_COMMON_HOST_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zephyr/autoconf.h>
|
||||
#include <zephyr/bluetooth/iso.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
|
||||
#include "common/adv.h"
|
||||
#include "common/conn.h"
|
||||
#include "common/iso.h"
|
||||
#include "common/l2cap.h"
|
||||
#include "common/scan.h"
|
||||
#include "common/gatt.h"
|
||||
#include "common/task.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define HOST_LOCK_DEBUG 0
|
||||
|
||||
#if HOST_LOCK_DEBUG
|
||||
void bt_le_host_lock_debug(const char *func, int line);
|
||||
void bt_le_host_unlock_debug(const char *func, int line);
|
||||
|
||||
#define bt_le_host_lock(...) bt_le_host_lock_debug(__func__, __LINE__)
|
||||
#define bt_le_host_unlock(...) bt_le_host_unlock_debug(__func__, __LINE__)
|
||||
#else /* HOST_LOCK_DEBUG */
|
||||
void bt_le_host_lock(void);
|
||||
void bt_le_host_unlock(void);
|
||||
#endif /* HOST_LOCK_DEBUG */
|
||||
|
||||
int bt_le_host_init(void);
|
||||
|
||||
void bt_le_host_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HOST_COMMON_HOST_H_ */
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2023 Nordic Semiconductor
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef HOST_COMMON_ISO_H_
|
||||
#define HOST_COMMON_ISO_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "nimble/iso.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct bt_le_iso_cb {
|
||||
void (*cis_dis)(struct net_buf *buf);
|
||||
void (*cis_est)(struct net_buf *buf);
|
||||
void (*cis_req)(struct net_buf *buf);
|
||||
void (*big_create)(struct net_buf *buf);
|
||||
void (*big_term)(struct net_buf *buf);
|
||||
void (*big_est)(struct net_buf *buf);
|
||||
void (*big_lost)(struct net_buf *buf);
|
||||
void (*biginfo)(struct net_buf *buf);
|
||||
|
||||
sys_snode_t node;
|
||||
};
|
||||
|
||||
struct bt_iso_tx_cb_info {
|
||||
uint16_t completed_pkts;
|
||||
uint16_t not_txd_pkts;
|
||||
|
||||
uint8_t pkt_cnt;
|
||||
struct {
|
||||
uint16_t seq_num;
|
||||
uint32_t tx_time_stamp;
|
||||
uint32_t time_offset : 24;
|
||||
uint32_t drift : 1;
|
||||
uint32_t sdu_ref_time;
|
||||
uint32_t sdu_hdl_time;
|
||||
uint32_t sdu_rec_time;
|
||||
} pkt[2];
|
||||
};
|
||||
|
||||
int bt_le_iso_cb_register_safe(struct bt_le_iso_cb *cb);
|
||||
|
||||
void bt_le_iso_cb_unregister_safe(struct bt_le_iso_cb *cb);
|
||||
|
||||
void bt_le_iso_handle_hci_event(uint8_t *data, size_t data_len);
|
||||
|
||||
void bt_le_iso_handle_tx_comp(uint8_t *data, size_t data_len);
|
||||
|
||||
void bt_le_iso_handle_rx_data(uint8_t *data, size_t data_len);
|
||||
|
||||
int bt_le_iso_tx(struct bt_iso_chan *chan,
|
||||
struct net_buf *buf,
|
||||
uint16_t seq_num,
|
||||
uint32_t time_stamp,
|
||||
bool ts_valid);
|
||||
|
||||
int bt_le_iso_rx(const uint8_t *data, uint16_t len, void *arg);
|
||||
|
||||
int bt_le_iso_disconnect(uint16_t conn_handle, uint8_t reason);
|
||||
|
||||
int bt_le_iso_init(void);
|
||||
|
||||
void bt_le_iso_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HOST_COMMON_ISO_H_ */
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2023 Nordic Semiconductor
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef HOST_COMMON_L2CAP_H_
|
||||
#define HOST_COMMON_L2CAP_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "nimble/l2cap.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define L2CAP_LE_SUCCESS 0x0000
|
||||
#define L2CAP_LE_ERR_PSM_NOT_SUPP 0x0002
|
||||
#define L2CAP_LE_ERR_NO_RESOURCES 0x0004
|
||||
#define L2CAP_LE_ERR_AUTHENTICATION 0x0005
|
||||
#define L2CAP_LE_ERR_AUTHORIZATION 0x0006
|
||||
#define L2CAP_LE_ERR_KEY_SIZE 0x0007
|
||||
#define L2CAP_LE_ERR_ENCRYPTION 0x0008
|
||||
#define L2CAP_LE_ERR_INVALID_SCID 0x0009
|
||||
#define L2CAP_LE_ERR_SCID_IN_USE 0x000A
|
||||
#define L2CAP_LE_ERR_UNACCEPT_PARAMS 0x000B
|
||||
#define L2CAP_LE_ERR_INVALID_PARAMS 0x000C
|
||||
|
||||
#define L2CAP_LE_OTS_PSM 0x0025
|
||||
#define L2CAP_LE_OTS_MTU MIN(CONFIG_BT_OTS_L2CAP_CHAN_TX_MTU, \
|
||||
CONFIG_BT_OTS_L2CAP_CHAN_RX_MTU)
|
||||
|
||||
int bt_le_l2cap_accept(uint16_t conn_handle, uint16_t psm,
|
||||
uint16_t scid, uint16_t mtu,
|
||||
uint16_t mps, uint16_t credits,
|
||||
uint16_t *result);
|
||||
|
||||
void bt_le_l2cap_connected(uint16_t conn_handle, uint16_t psm,
|
||||
uint16_t tx_cid, uint16_t tx_mtu,
|
||||
uint16_t rx_cid, uint16_t rx_mtu);
|
||||
|
||||
void bt_le_l2cap_disconnected(uint16_t conn_handle, uint16_t psm);
|
||||
|
||||
void bt_le_l2cap_received(uint16_t conn_handle, uint16_t psm,
|
||||
uint8_t *data, uint16_t len);
|
||||
|
||||
int bt_le_l2cap_init(void);
|
||||
|
||||
void bt_le_l2cap_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HOST_COMMON_L2CAP_H_ */
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2021 Nordic Semiconductor
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef HOST_COMMON_SCAN_H_
|
||||
#define HOST_COMMON_SCAN_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "nimble/gap.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void bt_le_scan_recv_listener(uint16_t event_type,
|
||||
uint8_t addr_type,
|
||||
uint8_t addr_val[6],
|
||||
int8_t rssi,
|
||||
int8_t tx_power,
|
||||
uint8_t sid,
|
||||
uint8_t pri_phy,
|
||||
uint8_t sec_phy,
|
||||
uint16_t per_adv_itvl,
|
||||
uint8_t data_len,
|
||||
uint8_t *data);
|
||||
|
||||
struct bt_le_per_adv_sync *bt_le_per_adv_sync_find_safe(uint16_t sync_handle);
|
||||
|
||||
int bt_le_per_adv_sync_new(uint16_t sync_handle,
|
||||
uint8_t sid,
|
||||
uint8_t phy,
|
||||
uint16_t interval,
|
||||
uint8_t addr_type,
|
||||
const uint8_t addr[6],
|
||||
struct bt_le_per_adv_sync **out_sync);
|
||||
|
||||
int bt_le_per_adv_sync_delete(uint16_t sync_handle);
|
||||
|
||||
int bt_le_per_adv_sync_establish_listener(uint16_t sync_handle);
|
||||
|
||||
int bt_le_per_adv_sync_lost_listener(uint16_t sync_handle);
|
||||
|
||||
int bt_le_per_adv_sync_report_recv_listener(uint16_t sync_handle,
|
||||
const uint8_t *data,
|
||||
size_t data_length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HOST_COMMON_SCAN_H_ */
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef HOST_COMMON_TASK_H_
|
||||
#define HOST_COMMON_TASK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_NIMBLE_PINNED_TO_CORE
|
||||
#define ISO_TASK_CORE CONFIG_BT_NIMBLE_PINNED_TO_CORE
|
||||
#else /* CONFIG_BT_NIMBLE_PINNED_TO_CORE */
|
||||
#define ISO_TASK_CORE (0)
|
||||
#endif /* CONFIG_BT_NIMBLE_PINNED_TO_CORE */
|
||||
|
||||
#define ISO_TASK_STACK_SIZE 4096
|
||||
#define ISO_TASK_NAME "iso_task"
|
||||
/* Ref:
|
||||
* - NimBLE Host task: configMAX_PRIORITIES - 4
|
||||
*/
|
||||
#define ISO_TASK_PRIO (configMAX_PRIORITIES - 4)
|
||||
|
||||
enum iso_queue_item_type {
|
||||
ISO_QUEUE_ITEM_TYPE_TIMER_EVENT,
|
||||
ISO_QUEUE_ITEM_TYPE_GAP_EVENT,
|
||||
ISO_QUEUE_ITEM_TYPE_GATT_EVENT,
|
||||
ISO_QUEUE_ITEM_TYPE_ISO_HCI_EVENT,
|
||||
ISO_QUEUE_ITEM_TYPE_ISO_TX_COMP,
|
||||
ISO_QUEUE_ITEM_TYPE_ISO_RX_DATA,
|
||||
|
||||
ISO_QUEUE_ITEM_TYPE_MAX,
|
||||
};
|
||||
|
||||
struct iso_queue_item {
|
||||
uint8_t type;
|
||||
uint8_t *data;
|
||||
size_t data_len;
|
||||
};
|
||||
|
||||
#define ISO_QUEUE_ITEM_COUNT 100
|
||||
#define ISO_QUEUE_ITEM_SIZE sizeof(struct iso_queue_item)
|
||||
|
||||
int bt_le_iso_task_post(enum iso_queue_item_type type,
|
||||
void *data, size_t data_len);
|
||||
|
||||
int bt_le_iso_task_init(void);
|
||||
|
||||
void bt_le_iso_task_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HOST_COMMON_TASK_H_ */
|
||||
@@ -0,0 +1,844 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2023 Nordic Semiconductor
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/bluetooth/hci_types.h>
|
||||
#include <zephyr/bluetooth/common/bt_str.h>
|
||||
|
||||
#include <../host/conn_internal.h>
|
||||
#include <../host/iso_internal.h>
|
||||
#include <../host/hci_core.h>
|
||||
|
||||
#include "common/host.h"
|
||||
#include "common/task.h"
|
||||
#include "common/app/gap.h"
|
||||
|
||||
#ifdef CONFIG_BT_NIMBLE_ISO_STD_FLOW_CTRL
|
||||
#define ISO_STD_FLOW_CTRL true
|
||||
#else /* CONFIG_BT_NIMBLE_ISO_STD_FLOW_CTRL */
|
||||
#define ISO_STD_FLOW_CTRL false
|
||||
#endif /* CONFIG_BT_NIMBLE_ISO_STD_FLOW_CTRL */
|
||||
|
||||
#define ISO_PKT_FIRST_FRAG (0b00)
|
||||
#define ISO_PKT_CONT_FRAG (0b01)
|
||||
#define ISO_PKT_COMP_SDU (0b10)
|
||||
#define ISO_PKT_LAST_FRAG (0b11)
|
||||
|
||||
#define BT_LE_FEAT_SET(feat, n) (feat[(n) >> 3] |= BIT((n) & 7))
|
||||
#define BT_LE_FEAT_UNSET(feat, n) (feat[(n) >> 3] &= ~BIT((n) & 7))
|
||||
|
||||
static sys_slist_t iso_cbs = SYS_SLIST_STATIC_INIT(&iso_cbs);
|
||||
|
||||
#if CONFIG_BT_ISO_UNICAST
|
||||
static void hci_le_cis_disconnected(struct net_buf *buf);
|
||||
#endif /* CONFIG_BT_ISO_UNICAST */
|
||||
|
||||
#if CONFIG_BT_ISO_TX
|
||||
static void iso_tx_sdu_clear(uint16_t conn_handle, bool is_big);
|
||||
#endif /* CONFIG_BT_ISO_TX */
|
||||
|
||||
extern void hci_le_biginfo_adv_report(struct net_buf *buf);
|
||||
|
||||
static struct bt_le_iso_cb iso_cb = {
|
||||
#if CONFIG_BT_ISO_UNICAST
|
||||
.cis_dis = hci_le_cis_disconnected,
|
||||
.cis_est = hci_le_cis_established,
|
||||
#if CONFIG_BT_ISO_PERIPHERAL
|
||||
.cis_req = hci_le_cis_req,
|
||||
#endif /* CONFIG_BT_ISO_PERIPHERAL */
|
||||
#endif /* CONFIG_BT_ISO_UNICAST */
|
||||
#if CONFIG_BT_ISO_BROADCASTER
|
||||
.big_create = hci_le_big_complete,
|
||||
.big_term = hci_le_big_terminate,
|
||||
#endif /* CONFIG_BT_ISO_BROADCASTER */
|
||||
#if CONFIG_BT_ISO_SYNC_RECEIVER
|
||||
.big_est = hci_le_big_sync_established,
|
||||
.big_lost = hci_le_big_sync_lost,
|
||||
.biginfo = hci_le_biginfo_adv_report,
|
||||
#endif /* CONFIG_BT_ISO_SYNC_RECEIVER */
|
||||
};
|
||||
|
||||
bool iso_log_enable = true;
|
||||
bool iso_hw_log_enable = true;
|
||||
bool isoal_post_lost = true;
|
||||
|
||||
extern uint16_t r_ble_ll_iso_free_buf_num_get(uint16_t conn_handle);
|
||||
extern int r_ble_hci_trans_hs_iso_tx(const uint8_t *data, uint16_t length, void *arg);
|
||||
extern void r_ble_ll_isoal_tx_comp_cb_set(void *cb);
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_iso_cb_register_safe(struct bt_le_iso_cb *cb)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
LOG_DBG("IsoCbReg");
|
||||
|
||||
if (cb == NULL) {
|
||||
LOG_ERR("IsoCbNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
if (sys_slist_find(&iso_cbs, &cb->node, NULL)) {
|
||||
LOG_WRN("IsoCbExist");
|
||||
err = -EEXIST;
|
||||
goto end;
|
||||
}
|
||||
|
||||
sys_slist_append(&iso_cbs, &cb->node);
|
||||
|
||||
end:
|
||||
bt_le_host_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
void bt_le_iso_cb_unregister_safe(struct bt_le_iso_cb *cb)
|
||||
{
|
||||
LOG_DBG("IsoCbUnreg");
|
||||
|
||||
if (cb == NULL) {
|
||||
LOG_ERR("IsoCbNull");
|
||||
return;
|
||||
}
|
||||
|
||||
bt_le_host_lock();
|
||||
sys_slist_find_and_remove(&iso_cbs, &cb->node);
|
||||
bt_le_host_unlock();
|
||||
}
|
||||
|
||||
#if CONFIG_BT_ISO_UNICAST
|
||||
static void hci_le_cis_disconnected(struct net_buf *buf)
|
||||
{
|
||||
struct bt_hci_evt_disconn_complete *evt = (void *)buf->data;
|
||||
struct bt_conn *iso;
|
||||
|
||||
LOG_DBG("CisDisconnectedEvt[%u]", evt->handle);
|
||||
|
||||
iso = bt_conn_lookup_handle(evt->handle, BT_CONN_TYPE_ISO);
|
||||
assert(iso);
|
||||
|
||||
iso->err = evt->reason;
|
||||
|
||||
bt_iso_disconnected(iso);
|
||||
bt_conn_unref(iso);
|
||||
}
|
||||
|
||||
static int iso_cis_disconn_evt_listener_safe(uint8_t *data)
|
||||
{
|
||||
struct bt_le_iso_cb *listener = NULL;
|
||||
struct net_buf buf = {0};
|
||||
|
||||
LOG_DBG("CisDisconnEvtListener");
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&iso_cbs, listener, node) {
|
||||
if (listener->cis_dis) {
|
||||
buf.data = data;
|
||||
listener->cis_dis(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_BT_ISO_TX
|
||||
iso_tx_sdu_clear(sys_get_le16(data + 1), false);
|
||||
#endif /* CONFIG_BT_ISO_TX */
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iso_cis_est_evt_listener_safe(uint8_t *data)
|
||||
{
|
||||
struct bt_le_iso_cb *listener = NULL;
|
||||
struct net_buf buf = {0};
|
||||
|
||||
LOG_DBG("CisEstEvtListener");
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&iso_cbs, listener, node) {
|
||||
if (listener->cis_est) {
|
||||
buf.data = data + 1; /* The first octet is subev_code */
|
||||
listener->cis_est(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_BT_ISO_PERIPHERAL
|
||||
static int iso_cis_req_evt_listener_safe(uint8_t *data)
|
||||
{
|
||||
struct bt_le_iso_cb *listener = NULL;
|
||||
struct net_buf buf = {0};
|
||||
|
||||
LOG_DBG("CisReqEvtListener");
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&iso_cbs, listener, node) {
|
||||
if (listener->cis_req) {
|
||||
buf.data = data + 1; /* The first octet is subev_code */
|
||||
listener->cis_req(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO_PERIPHERAL */
|
||||
#endif /* CONFIG_BT_ISO_UNICAST */
|
||||
|
||||
#if CONFIG_BT_ISO_BROADCASTER
|
||||
static int iso_create_big_comp_evt_listener_safe(uint8_t *data)
|
||||
{
|
||||
struct bt_le_iso_cb *listener = NULL;
|
||||
struct net_buf buf = {0};
|
||||
|
||||
LOG_DBG("CreateBigCompEvtListener");
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&iso_cbs, listener, node) {
|
||||
if (listener->big_create) {
|
||||
buf.data = data + 1; /* The first octet is subev_code */
|
||||
listener->big_create(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iso_term_big_comp_evt_listener_safe(uint8_t *data)
|
||||
{
|
||||
struct bt_le_iso_cb *listener = NULL;
|
||||
struct net_buf buf = {0};
|
||||
|
||||
LOG_DBG("TermBigCompEvtListener");
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&iso_cbs, listener, node) {
|
||||
if (listener->big_term) {
|
||||
buf.data = data + 1; /* The first octet is subev_code */
|
||||
listener->big_term(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
iso_tx_sdu_clear(data[1], true);
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO_BROADCASTER */
|
||||
|
||||
#if CONFIG_BT_ISO_SYNC_RECEIVER
|
||||
static int iso_big_sync_est_evt_listener_safe(uint8_t *data)
|
||||
{
|
||||
struct bt_le_iso_cb *listener = NULL;
|
||||
struct net_buf buf = {0};
|
||||
|
||||
LOG_DBG("BigSyncEstEvtListener");
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&iso_cbs, listener, node) {
|
||||
if (listener->big_est) {
|
||||
buf.data = data + 1; /* The first octet is subev_code */
|
||||
listener->big_est(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iso_big_sync_lost_evt_listener_safe(uint8_t *data)
|
||||
{
|
||||
struct bt_le_iso_cb *listener = NULL;
|
||||
struct net_buf buf = {0};
|
||||
|
||||
LOG_DBG("BigSyncLostEvtListener");
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&iso_cbs, listener, node) {
|
||||
if (listener->big_lost) {
|
||||
buf.data = data + 1; /* The first octet is subev_code */
|
||||
listener->big_lost(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iso_biginfo_adv_report_listener_safe(uint8_t *data)
|
||||
{
|
||||
struct bt_le_iso_cb *listener = NULL;
|
||||
struct net_buf buf = {0};
|
||||
|
||||
/* LOG_DBG("BigInfoAdvReportEvtListener"); */
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&iso_cbs, listener, node) {
|
||||
if (listener->biginfo) {
|
||||
buf.data = data + 1; /* The first octet is subev_code */
|
||||
listener->biginfo(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
/* The first octet is subev_code */
|
||||
bt_le_gap_app_biginfo_event(data + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO_SYNC_RECEIVER */
|
||||
|
||||
static void handle_iso_event(uint8_t *data, size_t data_len)
|
||||
{
|
||||
uint8_t event;
|
||||
bool le_meta;
|
||||
|
||||
/* Note:
|
||||
* The data_len is guaranteed by the Host that it is exactly
|
||||
* matching the expected length.
|
||||
*/
|
||||
|
||||
le_meta = data[0];
|
||||
event = data[1];
|
||||
|
||||
if (event != BT_HCI_EVT_LE_BIGINFO_ADV_REPORT) {
|
||||
LOG_DBG("HandleIsoEvt[%u][%u]", le_meta, event);
|
||||
}
|
||||
|
||||
#if CONFIG_BT_ISO_UNICAST
|
||||
if (le_meta == false) {
|
||||
assert(event == BT_HCI_EVT_DISCONN_COMPLETE);
|
||||
iso_cis_disconn_evt_listener_safe(data + 2);
|
||||
return;
|
||||
}
|
||||
#else /* CONFIG_BT_ISO_UNICAST */
|
||||
ARG_UNUSED(le_meta);
|
||||
#endif /* CONFIG_BT_ISO_UNICAST */
|
||||
|
||||
switch (event) {
|
||||
#if CONFIG_BT_ISO_UNICAST
|
||||
case BT_HCI_EVT_LE_CIS_ESTABLISHED:
|
||||
iso_cis_est_evt_listener_safe(data + 2);
|
||||
break;
|
||||
#if CONFIG_BT_ISO_PERIPHERAL
|
||||
case BT_HCI_EVT_LE_CIS_REQ:
|
||||
iso_cis_req_evt_listener_safe(data + 2);
|
||||
break;
|
||||
#endif /* CONFIG_BT_ISO_PERIPHERAL */
|
||||
#endif /* CONFIG_BT_ISO_UNICAST */
|
||||
#if CONFIG_BT_ISO_BROADCASTER
|
||||
case BT_HCI_EVT_LE_BIG_COMPLETE:
|
||||
iso_create_big_comp_evt_listener_safe(data + 2);
|
||||
break;
|
||||
case BT_HCI_EVT_LE_BIG_TERMINATE:
|
||||
iso_term_big_comp_evt_listener_safe(data + 2);
|
||||
break;
|
||||
#endif /* CONFIG_BT_ISO_BROADCASTER */
|
||||
#if CONFIG_BT_ISO_SYNC_RECEIVER
|
||||
case BT_HCI_EVT_LE_BIG_SYNC_ESTABLISHED:
|
||||
iso_big_sync_est_evt_listener_safe(data + 2);
|
||||
break;
|
||||
case BT_HCI_EVT_LE_BIG_SYNC_LOST:
|
||||
iso_big_sync_lost_evt_listener_safe(data + 2);
|
||||
break;
|
||||
case BT_HCI_EVT_LE_BIGINFO_ADV_REPORT:
|
||||
iso_biginfo_adv_report_listener_safe(data + 2);
|
||||
break;
|
||||
#endif /* CONFIG_BT_ISO_SYNC_RECEIVER */
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_le_iso_handle_hci_event(uint8_t *data, size_t data_len)
|
||||
{
|
||||
assert(data && data_len);
|
||||
handle_iso_event(data, data_len);
|
||||
free(data);
|
||||
}
|
||||
|
||||
#if CONFIG_BT_ISO_TX
|
||||
struct iso_tx_sdu_node {
|
||||
struct bt_iso_chan *chan;
|
||||
|
||||
const uint8_t *sdu;
|
||||
uint16_t sdu_len;
|
||||
|
||||
sys_snode_t node;
|
||||
};
|
||||
|
||||
static sys_slist_t iso_tx_sdu_list = SYS_SLIST_STATIC_INIT(&iso_tx_sdu_list);
|
||||
|
||||
static int iso_tx_sdu_insert(struct bt_iso_chan *chan, const uint8_t *sdu, uint16_t sdu_len)
|
||||
{
|
||||
struct iso_tx_sdu_node *sdu_node;
|
||||
|
||||
sdu_node = calloc(1, sizeof(*sdu_node));
|
||||
if (sdu_node == NULL) {
|
||||
LOG_ERR("IsoTxInsertNoMem[%u]", sizeof(*sdu_node));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sdu_node->chan = chan;
|
||||
sdu_node->sdu = sdu;
|
||||
sdu_node->sdu_len = sdu_len;
|
||||
|
||||
sys_slist_append(&iso_tx_sdu_list, &sdu_node->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iso_tx_sdu_clear(uint16_t handle, bool is_big)
|
||||
{
|
||||
struct iso_tx_sdu_node *sdu_node;
|
||||
struct iso_tx_sdu_node *sdu_tmp;
|
||||
struct bt_conn *iso;
|
||||
sys_snode_t *prev;
|
||||
|
||||
LOG_DBG("IsoTxSduClear[%u][%u]", handle, is_big);
|
||||
|
||||
prev = NULL;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&iso_tx_sdu_list, sdu_node, sdu_tmp, node) {
|
||||
iso = sdu_node->chan->iso;
|
||||
|
||||
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);
|
||||
|
||||
sys_slist_remove(&iso_tx_sdu_list, prev, &sdu_node->node);
|
||||
|
||||
free((void *)sdu_node->sdu);
|
||||
free(sdu_node);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
prev = &sdu_node->node;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (chan->state != BT_ISO_STATE_CONNECTED) {
|
||||
LOG_ERR("ChanNotConn[%u]", chan->iso->handle);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
if (chan->iso->iso.info.can_send == false) {
|
||||
LOG_ERR("ChanNotSend[%u]", chan->iso->handle);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (buf->len > chan->qos->tx->sdu) {
|
||||
LOG_ERR("ChanCannotSend[%u][%u][%u]",
|
||||
chan->iso->handle, buf->len, chan->qos->tx->sdu);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iso_tx_now(struct bt_iso_chan *chan, const uint8_t *sdu,
|
||||
uint16_t sdu_len, uint16_t seq_num,
|
||||
uint32_t time_stamp, bool ts_valid)
|
||||
{
|
||||
uint16_t data_total_len;
|
||||
uint8_t pb_flag;
|
||||
uint8_t *pkt;
|
||||
int err;
|
||||
|
||||
pb_flag = ISO_PKT_COMP_SDU;
|
||||
data_total_len = (ts_valid ? 4 : 0) + 4 + sdu_len;
|
||||
|
||||
/* From Spec:
|
||||
* In the Host to Controller direction, Data_Total_Length shall
|
||||
* be less than or equal to the size of buffer supported by the
|
||||
* Controller (which is returned using the ISO_Data_Packet_Length
|
||||
* return parameter of the LE Read Buffer Size command).
|
||||
*
|
||||
* TODO:
|
||||
* Check if ISO SDU needs to be fragmented here.
|
||||
*/
|
||||
|
||||
pkt = malloc(4 + data_total_len);
|
||||
if (pkt == NULL) {
|
||||
LOG_ERR("IsoTxNoMem[%u]", 4 + data_total_len);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sys_put_le16((ts_valid << 14) | (pb_flag << 12) | chan->iso->handle, pkt);
|
||||
sys_put_le16(data_total_len, pkt + 2);
|
||||
if (ts_valid) {
|
||||
sys_put_le32(time_stamp, pkt + 4);
|
||||
}
|
||||
sys_put_le16(seq_num, pkt + (ts_valid ? 8 : 4));
|
||||
sys_put_le16(sdu_len, pkt + (ts_valid ? 10 : 6));
|
||||
|
||||
memcpy(pkt + (ts_valid ? 12 : 8), sdu, sdu_len);
|
||||
|
||||
#if ISO_STD_FLOW_CTRL
|
||||
/* TODO:
|
||||
* - Use variables to record the ISO buffer size & count;
|
||||
* - Check if the available ISO buffers are enough to hold the SDU;
|
||||
*/
|
||||
#else /* ISO_STD_FLOW_CTRL */
|
||||
/* If the list is not empty, which means some ISO SDUs ahead of this
|
||||
* one are still waiting to be transmitted. In this case, this SDU
|
||||
* must be appended in the list.
|
||||
*
|
||||
* If the list is empty, but no free buffer exists in the controller,
|
||||
* then ISO flow control is triggered.
|
||||
*/
|
||||
if (sys_slist_is_empty(&iso_tx_sdu_list) == false ||
|
||||
r_ble_ll_iso_free_buf_num_get(chan->iso->handle) == 0) {
|
||||
/* LOG_INF("IsoFlowCtrl!"); */
|
||||
err = iso_tx_sdu_insert(chan, pkt, 4 + data_total_len);
|
||||
if (err) {
|
||||
free(pkt);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#endif /* ISO_STD_FLOW_CTRL */
|
||||
|
||||
err = r_ble_hci_trans_hs_iso_tx(pkt, 4 + data_total_len, NULL);
|
||||
if (err) {
|
||||
/* err = -1: No BIS or CIS found;
|
||||
* err = -2: Bad ISO packet;
|
||||
* err = -3: Not able to send (e.g., CIS tx bn is 0, etc);
|
||||
* err = -4: Not ready to send (e.g. BIS or CIS not established, DP not setup, etc.);
|
||||
* err = -5: Last SDU fragment (generated over HCI) is failed to send;
|
||||
*
|
||||
* Note:
|
||||
* The allocated pkt buffer will be freed in our controller.
|
||||
*
|
||||
* TODO:
|
||||
* If using controller from other vendors, the pkt buffer may
|
||||
* needs to be freed here.
|
||||
*/
|
||||
LOG_WRN("IsoTxFail[%d]", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
#if ISO_STD_FLOW_CTRL
|
||||
/* TODO:
|
||||
* - Decrease the available ISO buffer count;
|
||||
* - Increase the available ISO buffer count once NoCP is received;
|
||||
*/
|
||||
#endif /* ISO_STD_FLOW_CTRL */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_le_iso_tx(struct bt_iso_chan *chan,
|
||||
struct net_buf *buf,
|
||||
uint16_t seq_num,
|
||||
uint32_t time_stamp,
|
||||
bool ts_valid)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = validate_iso_send(chan, buf);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return iso_tx_now(chan, buf->data, buf->len, seq_num, time_stamp, ts_valid);
|
||||
}
|
||||
|
||||
struct iso_tx_comp_event {
|
||||
uint16_t conn_handle;
|
||||
struct bt_iso_tx_cb_info info;
|
||||
};
|
||||
|
||||
void bt_le_iso_handle_tx_comp(uint8_t *data, size_t data_len)
|
||||
{
|
||||
struct iso_tx_comp_event *evt = (struct iso_tx_comp_event *)data;
|
||||
struct iso_tx_sdu_node *sdu_node;
|
||||
struct bt_iso_chan *chan;
|
||||
struct bt_conn *iso;
|
||||
bt_conn_tx_cb_t cb;
|
||||
sys_snode_t *node;
|
||||
void *ud;
|
||||
int err;
|
||||
|
||||
assert(data && data_len == sizeof(*evt));
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
while (1) {
|
||||
/* If no free buffer exists, no need to check if any SDU is pending */
|
||||
if (r_ble_ll_iso_free_buf_num_get(evt->conn_handle) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Fetch and remove the first ISO SDU from list if list is not empty */
|
||||
node = sys_slist_get(&iso_tx_sdu_list);
|
||||
if (node == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
sdu_node = CONTAINER_OF(node, struct iso_tx_sdu_node, node);
|
||||
|
||||
err = r_ble_hci_trans_hs_iso_tx(sdu_node->sdu, sdu_node->sdu_len, NULL);
|
||||
if (err) {
|
||||
/* err = -1: No BIS or CIS found;
|
||||
* err = -2: Bad ISO packet;
|
||||
* err = -3: Not able to send (e.g., CIS tx bn is 0, etc);
|
||||
* err = -4: Not ready to send (e.g. BIS or CIS not established, DP not setup, etc.);
|
||||
* err = -5: Last SDU fragment (generated over HCI) is failed to send;
|
||||
*
|
||||
* Note:
|
||||
* The allocated pkt buffer will be freed in our controller.
|
||||
*
|
||||
* TODO:
|
||||
* If using controller from other vendors, the pkt buffer may
|
||||
* needs to be freed here.
|
||||
*/
|
||||
LOG_WRN("IsoTxFail[%d]", err);
|
||||
}
|
||||
|
||||
free(sdu_node);
|
||||
}
|
||||
|
||||
iso = bt_conn_lookup_handle(evt->conn_handle, BT_CONN_TYPE_ISO);
|
||||
if (iso == NULL) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
chan = iso->iso.chan;
|
||||
assert(chan);
|
||||
|
||||
cb = NULL;
|
||||
ud = NULL;
|
||||
chan->iso->get_and_clear_cb(NULL, NULL, &cb, &ud);
|
||||
|
||||
if (cb) {
|
||||
cb(chan->iso, &evt->info, 0);
|
||||
}
|
||||
|
||||
end:
|
||||
bt_le_host_unlock();
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void iso_tx_comp_cb(uint16_t conn_handle, void *info, size_t size)
|
||||
{
|
||||
struct iso_tx_comp_event *evt;
|
||||
int err;
|
||||
|
||||
/* Note:
|
||||
* This callback is invoked in the BLE Controller task, the operation in
|
||||
* this function shall be as simple as possible and avoid log output.
|
||||
* Avoid acquiring host_lock here to prevent deadlock when the ISO task
|
||||
* holds host_lock while waiting for a Controller response. Instead, post
|
||||
* the event to the ISO task for processing.
|
||||
*/
|
||||
|
||||
assert(size == sizeof(struct bt_iso_tx_cb_info));
|
||||
|
||||
evt = calloc(1, sizeof(*evt));
|
||||
if (evt == NULL) {
|
||||
LOG_ERR("IsoTxCompNoMem[%u]", sizeof(*evt));
|
||||
return;
|
||||
}
|
||||
|
||||
evt->conn_handle = conn_handle;
|
||||
memcpy(&evt->info, info, sizeof(evt->info));
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_ISO_TX_COMP, evt, sizeof(*evt));
|
||||
if (err) {
|
||||
LOG_ERR("IsoTxCompPostFail[%d]", err);
|
||||
free(evt);
|
||||
}
|
||||
}
|
||||
#else /* CONFIG_BT_ISO_TX */
|
||||
void bt_le_iso_handle_tx_comp(uint8_t *data, size_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO_TX */
|
||||
|
||||
#if CONFIG_BT_ISO_RX
|
||||
void bt_le_iso_handle_rx_data(uint8_t *data, size_t data_len)
|
||||
{
|
||||
struct net_buf buf = {0};
|
||||
|
||||
assert(data && data_len);
|
||||
|
||||
bt_le_host_lock();
|
||||
net_buf_simple_init_with_data(&buf.b, (void *)data, data_len);
|
||||
hci_iso(&buf);
|
||||
bt_le_host_unlock();
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
int bt_le_iso_rx(const uint8_t *data, uint16_t len, void *arg)
|
||||
{
|
||||
uint8_t *rx_data;
|
||||
int err;
|
||||
|
||||
/* Note:
|
||||
* This callback is invoked in the BLE Controller task. The data directly
|
||||
* points to the start of an ISO SDU, i.e. the conn_handle, etc. No extra
|
||||
* information for now.
|
||||
* Avoid acquiring host_lock here to prevent deadlock when the ISO task
|
||||
* holds host_lock while waiting for a Controller response. Instead, copy
|
||||
* the data and post the event to the ISO task for further processing.
|
||||
*/
|
||||
|
||||
ARG_UNUSED(arg);
|
||||
|
||||
rx_data = calloc(1, len);
|
||||
if (rx_data == NULL) {
|
||||
LOG_ERR("IsoRxNoMem[%u]", len);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(rx_data, data, len);
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_ISO_RX_DATA, rx_data, len);
|
||||
if (err) {
|
||||
LOG_ERR("IsoRxPostFail[%d]", err);
|
||||
free(rx_data);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else /* CONFIG_BT_ISO_RX */
|
||||
void bt_le_iso_handle_rx_data(uint8_t *data, size_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO_RX */
|
||||
|
||||
int bt_le_iso_disconnect(uint16_t conn_handle, uint8_t reason)
|
||||
{
|
||||
return bt_le_nimble_iso_disconnect(conn_handle, reason);
|
||||
}
|
||||
|
||||
static void iso_features_set(void)
|
||||
{
|
||||
#if CONFIG_BT_ISO_CENTRAL
|
||||
BT_LE_FEAT_SET(bt_dev.le.features, BT_LE_FEAT_BIT_CIS_CENTRAL);
|
||||
#endif /* CONFIG_BT_ISO_CENTRAL */
|
||||
|
||||
#if CONFIG_BT_ISO_PERIPHERAL
|
||||
BT_LE_FEAT_SET(bt_dev.le.features, BT_LE_FEAT_BIT_CIS_PERIPHERAL);
|
||||
#endif /* CONFIG_BT_ISO_PERIPHERAL */
|
||||
|
||||
#if CONFIG_BT_ISO_BROADCASTER
|
||||
BT_LE_FEAT_SET(bt_dev.le.features, BT_LE_FEAT_BIT_ISO_BROADCASTER);
|
||||
#endif /* CONFIG_BT_ISO_BROADCASTER */
|
||||
|
||||
#if CONFIG_BT_ISO_SYNC_RECEIVER
|
||||
BT_LE_FEAT_SET(bt_dev.le.features, BT_LE_FEAT_BIT_SYNC_RECEIVER);
|
||||
#endif /* CONFIG_BT_ISO_SYNC_RECEIVER */
|
||||
|
||||
LOG_DBG("IsoFeatSet[%s]", bt_hex(bt_dev.le.features, sizeof(bt_dev.le.features)));
|
||||
}
|
||||
|
||||
static void iso_features_unset(void)
|
||||
{
|
||||
#if CONFIG_BT_ISO_CENTRAL
|
||||
BT_LE_FEAT_UNSET(bt_dev.le.features, BT_LE_FEAT_BIT_CIS_CENTRAL);
|
||||
#endif /* CONFIG_BT_ISO_CENTRAL */
|
||||
|
||||
#if CONFIG_BT_ISO_PERIPHERAL
|
||||
BT_LE_FEAT_UNSET(bt_dev.le.features, BT_LE_FEAT_BIT_CIS_PERIPHERAL);
|
||||
#endif /* CONFIG_BT_ISO_PERIPHERAL */
|
||||
|
||||
#if CONFIG_BT_ISO_BROADCASTER
|
||||
BT_LE_FEAT_UNSET(bt_dev.le.features, BT_LE_FEAT_BIT_ISO_BROADCASTER);
|
||||
#endif /* CONFIG_BT_ISO_BROADCASTER */
|
||||
|
||||
#if CONFIG_BT_ISO_SYNC_RECEIVER
|
||||
BT_LE_FEAT_UNSET(bt_dev.le.features, BT_LE_FEAT_BIT_SYNC_RECEIVER);
|
||||
#endif /* CONFIG_BT_ISO_SYNC_RECEIVER */
|
||||
|
||||
LOG_DBG("IsoFeatUnset[%s]", bt_hex(bt_dev.le.features, sizeof(bt_dev.le.features)));
|
||||
}
|
||||
|
||||
int bt_le_iso_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
LOG_DBG("IsoInit");
|
||||
|
||||
iso_features_set();
|
||||
|
||||
err = bt_le_iso_cb_register_safe(&iso_cb);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
#if CONFIG_BT_ISO_TX
|
||||
r_ble_ll_isoal_tx_comp_cb_set(iso_tx_comp_cb);
|
||||
#endif /* CONFIG_BT_ISO_TX */
|
||||
|
||||
err = bt_le_nimble_iso_init();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bt_le_iso_deinit(void)
|
||||
{
|
||||
LOG_DBG("IsoDeinit");
|
||||
|
||||
iso_features_unset();
|
||||
|
||||
bt_le_iso_cb_unregister_safe(&iso_cb);
|
||||
|
||||
#if CONFIG_BT_ISO_TX
|
||||
r_ble_ll_isoal_tx_comp_cb_set(NULL);
|
||||
#endif /* CONFIG_BT_ISO_TX */
|
||||
|
||||
bt_le_nimble_iso_deinit();
|
||||
}
|
||||
@@ -0,0 +1,504 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileCopyrightText: 2023 Nordic Semiconductor
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/bluetooth/l2cap.h>
|
||||
|
||||
#include <../host/conn_internal.h>
|
||||
#include <ots_l2cap_internal.h>
|
||||
|
||||
#include "common/host.h"
|
||||
#include "common/app/gap.h"
|
||||
|
||||
#define L2CAP_LE_MIN_MTU 23
|
||||
#define L2CAP_ECRED_MIN_MTU 64
|
||||
|
||||
#define L2CAP_LE_CID_DYN_START 0x0040
|
||||
#define L2CAP_LE_CID_DYN_END 0x007F
|
||||
#define L2CAP_LE_CID_IS_DYN(_cid) (_cid >= L2CAP_LE_CID_DYN_START && _cid <= L2CAP_LE_CID_DYN_END)
|
||||
|
||||
#define L2CAP_LE_PSM_FIXED_START 0x0001
|
||||
#define L2CAP_LE_PSM_FIXED_END 0x007F
|
||||
#define L2CAP_LE_PSM_DYN_START 0x0080
|
||||
#define L2CAP_LE_PSM_DYN_END 0x00FF
|
||||
#define L2CAP_LE_PSM_IS_DYN(_psm) (_psm >= L2CAP_LE_PSM_DYN_START && _psm <= L2CAP_LE_PSM_DYN_END)
|
||||
|
||||
static sys_slist_t l2cap_servers = SYS_SLIST_STATIC_INIT(&l2cap_servers);
|
||||
|
||||
static struct bt_l2cap_chan *l2cap_lookup_tx_cid(struct bt_conn *conn, uint16_t cid)
|
||||
{
|
||||
struct bt_l2cap_chan *chan;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
|
||||
if (BT_L2CAP_LE_CHAN(chan)->tx.cid == cid) {
|
||||
return chan;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
static struct bt_l2cap_chan *l2cap_lookup_rx_cid(struct bt_conn *conn, uint16_t cid)
|
||||
{
|
||||
struct bt_l2cap_chan *chan;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
|
||||
if (BT_L2CAP_LE_CHAN(chan)->rx.cid == cid) {
|
||||
return chan;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct bt_l2cap_chan *l2cap_lookup_psm(struct bt_conn *conn, uint16_t psm)
|
||||
{
|
||||
struct bt_l2cap_chan *chan;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
|
||||
if (BT_L2CAP_LE_CHAN(chan)->psm == psm) {
|
||||
return chan;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool l2cap_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan, uint16_t psm)
|
||||
{
|
||||
LOG_DBG("L2capChanAdd[%04x]", psm);
|
||||
|
||||
/* Attach channel to the connection */
|
||||
if (sys_slist_find(&conn->channels, &chan->node, NULL)) {
|
||||
LOG_WRN("L2capChanExists[%04x]", psm);
|
||||
return false;
|
||||
}
|
||||
|
||||
sys_slist_append(&conn->channels, &chan->node);
|
||||
chan->conn = conn;
|
||||
|
||||
/* Set channel PSM */
|
||||
BT_L2CAP_LE_CHAN(chan)->psm = psm;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct bt_l2cap_server *l2cap_server_lookup_psm(uint16_t psm)
|
||||
{
|
||||
struct bt_l2cap_server *server = NULL;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&l2cap_servers, server, node) {
|
||||
if (server->psm == psm) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
static inline uint16_t err_to_result(int err)
|
||||
{
|
||||
switch (err) {
|
||||
case -ENOMEM:
|
||||
return L2CAP_LE_ERR_NO_RESOURCES;
|
||||
case -EACCES:
|
||||
return L2CAP_LE_ERR_AUTHORIZATION;
|
||||
case -EPERM:
|
||||
return L2CAP_LE_ERR_KEY_SIZE;
|
||||
case -ENOTSUP:
|
||||
/* This handle the cases where a fixed channel is registered but
|
||||
* for some reason (e.g. controller not supporting a feature)
|
||||
* cannot be used.
|
||||
*/
|
||||
return L2CAP_LE_ERR_PSM_NOT_SUPP;
|
||||
default:
|
||||
return L2CAP_LE_ERR_UNACCEPT_PARAMS;
|
||||
}
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_l2cap_accept(uint16_t conn_handle, uint16_t psm,
|
||||
uint16_t scid, uint16_t mtu,
|
||||
uint16_t mps, uint16_t credits,
|
||||
uint16_t *result)
|
||||
{
|
||||
struct bt_l2cap_server *server;
|
||||
struct bt_l2cap_chan *chan;
|
||||
struct bt_conn *conn;
|
||||
int err;
|
||||
|
||||
ARG_UNUSED(credits);
|
||||
|
||||
LOG_DBG("L2capAccept[%u][%04x][%04x][%u][%u]", conn_handle, psm, scid, mtu, mps);
|
||||
|
||||
conn = bt_le_acl_conn_find(conn_handle);
|
||||
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("NotConn[%d]", __LINE__);
|
||||
|
||||
*result = L2CAP_LE_ERR_INVALID_PARAMS;
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
/* Check if there is a server registered */
|
||||
server = l2cap_server_lookup_psm(psm);
|
||||
if (server == NULL) {
|
||||
LOG_ERR("SrvNotReg[%04x]", psm);
|
||||
|
||||
*result = L2CAP_LE_ERR_PSM_NOT_SUPP;
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (!L2CAP_LE_CID_IS_DYN(scid)) {
|
||||
LOG_ERR("NotDynScid[%04x]", scid);
|
||||
|
||||
*result = L2CAP_LE_ERR_INVALID_SCID;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan = l2cap_lookup_tx_cid(conn, scid);
|
||||
if (chan) {
|
||||
LOG_WRN("ScidUsed[%04x]", scid);
|
||||
|
||||
*result = L2CAP_LE_ERR_SCID_IN_USE;
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
if (server->accept == NULL) {
|
||||
LOG_ERR("SrvAcceptNull");
|
||||
|
||||
*result = L2CAP_LE_ERR_INVALID_PARAMS;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
err = server->accept(conn, server, &chan);
|
||||
if (err) {
|
||||
LOG_ERR("SrvAcceptFail[%d]", err);
|
||||
|
||||
*result = err_to_result(err);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (chan == NULL) {
|
||||
LOG_ERR("SrvAcceptNullChan");
|
||||
*result = L2CAP_LE_ERR_NO_RESOURCES;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (l2cap_chan_add(conn, chan, psm) == false) {
|
||||
*result = L2CAP_LE_ERR_NO_RESOURCES;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*result = L2CAP_LE_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
void bt_le_l2cap_connected(uint16_t conn_handle, uint16_t psm,
|
||||
uint16_t tx_cid, uint16_t tx_mtu,
|
||||
uint16_t rx_cid, uint16_t rx_mtu)
|
||||
{
|
||||
struct bt_l2cap_chan *chan;
|
||||
struct bt_conn *conn;
|
||||
|
||||
LOG_DBG("L2capConnected[%u][%04x][%04x][%u][%04x][%u]",
|
||||
conn_handle, psm, tx_cid, tx_mtu, rx_cid, rx_mtu);
|
||||
|
||||
conn = bt_le_acl_conn_find(conn_handle);
|
||||
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("NotConn[%d]", __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
chan = l2cap_lookup_psm(conn, psm);
|
||||
if (chan == NULL) {
|
||||
LOG_ERR("PsmNotFound[%04x]", psm);
|
||||
return;
|
||||
}
|
||||
|
||||
BT_L2CAP_LE_CHAN(chan)->tx.cid = tx_cid;
|
||||
BT_L2CAP_LE_CHAN(chan)->tx.mtu = tx_mtu;
|
||||
BT_L2CAP_LE_CHAN(chan)->rx.cid = rx_cid;
|
||||
BT_L2CAP_LE_CHAN(chan)->rx.mtu = rx_mtu;
|
||||
|
||||
if (chan->ops->connected) {
|
||||
chan->ops->connected(chan);
|
||||
}
|
||||
}
|
||||
|
||||
#define L2CAP_UGLY_TX_DONE_WORKAROUND 1
|
||||
|
||||
_IDF_ONLY
|
||||
void bt_le_l2cap_disconnected(uint16_t conn_handle, uint16_t psm)
|
||||
{
|
||||
#if L2CAP_UGLY_TX_DONE_WORKAROUND
|
||||
struct bt_gatt_ots_l2cap *l2cap_ctx;
|
||||
struct bt_l2cap_le_chan *l2chan;
|
||||
#endif /* L2CAP_UGLY_TX_DONE_WORKAROUND */
|
||||
struct bt_l2cap_chan *chan;
|
||||
struct bt_conn *conn;
|
||||
|
||||
LOG_DBG("L2capDisconnected[%u][%04x]", conn_handle, psm);
|
||||
|
||||
conn = bt_le_acl_conn_find(conn_handle);
|
||||
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("NotConn[%d]", __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
chan = l2cap_lookup_psm(conn, psm);
|
||||
if (chan == NULL) {
|
||||
LOG_ERR("PsmNotFound[%04x]", psm);
|
||||
return;
|
||||
}
|
||||
|
||||
#if L2CAP_UGLY_TX_DONE_WORKAROUND
|
||||
l2chan = CONTAINER_OF(chan, struct bt_l2cap_le_chan, chan);
|
||||
l2cap_ctx = CONTAINER_OF(l2chan, struct bt_gatt_ots_l2cap, ot_chan);
|
||||
|
||||
if (chan->ops->sent && l2cap_ctx->tx_done) {
|
||||
chan->ops->sent(chan);
|
||||
}
|
||||
#endif /* L2CAP_UGLY_TX_DONE_WORKAROUND */
|
||||
|
||||
if (chan->ops->disconnected) {
|
||||
chan->ops->disconnected(chan);
|
||||
}
|
||||
|
||||
sys_slist_find_and_remove(&conn->channels, &chan->node);
|
||||
chan->conn = NULL;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
void bt_le_l2cap_received(uint16_t conn_handle, uint16_t psm,
|
||||
uint8_t *data, uint16_t len)
|
||||
{
|
||||
struct bt_l2cap_chan *chan;
|
||||
struct net_buf buf = {0};
|
||||
struct bt_conn *conn;
|
||||
|
||||
LOG_DBG("L2capReceived[%u][%04x][%u]", conn_handle, psm, len);
|
||||
|
||||
conn = bt_le_acl_conn_find(conn_handle);
|
||||
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("NotConn[%d]", __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
chan = l2cap_lookup_psm(conn, psm);
|
||||
if (chan == NULL) {
|
||||
LOG_ERR("PsmNotFound[%04x]", psm);
|
||||
return;
|
||||
}
|
||||
|
||||
buf.data = data;
|
||||
buf.len = len;
|
||||
|
||||
if (chan->ops->recv) {
|
||||
chan->ops->recv(chan, &buf);
|
||||
}
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan, uint16_t psm)
|
||||
{
|
||||
int err;
|
||||
|
||||
LOG_DBG("L2capChanConnect[%04x]", psm);
|
||||
|
||||
if (chan == NULL) {
|
||||
LOG_ERR("L2capChanNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("L2capChanNotConn[%p]", conn);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
if (psm < L2CAP_LE_PSM_FIXED_START || psm > L2CAP_LE_PSM_DYN_END) {
|
||||
LOG_ERR("InvPsm[%04x]", psm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = bt_le_nimble_l2cap_chan_connect(conn->handle);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
l2cap_chan_add(conn, chan, psm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
|
||||
{
|
||||
int err;
|
||||
|
||||
LOG_DBG("L2capChanDisconnect");
|
||||
|
||||
if (chan == NULL) {
|
||||
LOG_ERR("L2capChanNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (chan->conn == NULL || chan->conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("L2capChanNotConn[%p]", chan->conn);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
err = bt_le_nimble_l2cap_chan_disconnect(chan);
|
||||
if (err) {
|
||||
/* If the disconnect failed, remove the channel from the connection.
|
||||
* Otherwise the removal will be handled by the disconnect callback.
|
||||
*/
|
||||
sys_slist_find_and_remove(&chan->conn->channels, &chan->node);
|
||||
chan->conn = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
||||
{
|
||||
if (chan == NULL || buf == NULL) {
|
||||
LOG_ERR("L2capChanBufNull[%p][%p]", chan, buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOG_DBG("L2capChanSend[%u]", buf->len);
|
||||
|
||||
if (chan->conn == NULL || chan->conn->state != BT_CONN_CONNECTED) {
|
||||
LOG_ERR("L2capChanNotConn[%p]", chan->conn);
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
if (buf->len > L2CAP_LE_OTS_MTU) {
|
||||
LOG_ERR("L2capTooLargeBufToSend[%u][%u]", buf->len, L2CAP_LE_OTS_MTU);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
return bt_le_nimble_l2cap_chan_send(chan, buf);
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_l2cap_server_register(struct bt_l2cap_server *server)
|
||||
{
|
||||
LOG_DBG("L2capSrvReg");
|
||||
|
||||
if (server == NULL) {
|
||||
LOG_ERR("SrvNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (server->accept == NULL) {
|
||||
LOG_ERR("SrvAcceptNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (server->sec_level > BT_SECURITY_L4) {
|
||||
LOG_ERR("InvSecLevel[%u]", server->sec_level);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (server->psm) {
|
||||
if (server->psm < L2CAP_LE_PSM_FIXED_START ||
|
||||
server->psm > L2CAP_LE_PSM_DYN_END) {
|
||||
LOG_ERR("InvPsm[%04x]", server->psm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check if given PSM is already in use */
|
||||
if (l2cap_server_lookup_psm(server->psm)) {
|
||||
LOG_WRN("PsmReg");
|
||||
return -EADDRINUSE;
|
||||
}
|
||||
|
||||
LOG_DBG("SrvPsm[%04x]", server->psm);
|
||||
} else {
|
||||
uint16_t psm;
|
||||
|
||||
for (psm = L2CAP_LE_PSM_DYN_START;
|
||||
psm <= L2CAP_LE_PSM_DYN_END; psm++) {
|
||||
if (l2cap_server_lookup_psm(psm) == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (psm > L2CAP_LE_PSM_DYN_END) {
|
||||
LOG_ERR("NoFreeDynPsm");
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
LOG_DBG("PsmNew[%04x]", psm);
|
||||
|
||||
server->psm = psm;
|
||||
}
|
||||
|
||||
if (server->sec_level < BT_SECURITY_L1) {
|
||||
server->sec_level = BT_SECURITY_L1;
|
||||
}
|
||||
|
||||
sys_slist_append(&l2cap_servers, &server->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_le_l2cap_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
LOG_DBG("L2capInit");
|
||||
|
||||
#if CONFIG_BT_OTS
|
||||
extern int bt_gatt_ots_conn_cb_register(void);
|
||||
err = bt_gatt_ots_conn_cb_register();
|
||||
if (err) {
|
||||
LOG_ERR("OtsConnCbRegFail[%d]", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
extern int bt_gatt_ots_instances_prepare_v2(void);
|
||||
err = bt_gatt_ots_instances_prepare_v2();
|
||||
if (err) {
|
||||
LOG_ERR("PrepOtsInstsFail[%d]", err);
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_BT_OTS */
|
||||
|
||||
#if CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT
|
||||
extern int bt_gatt_ots_l2cap_init_v2(void);
|
||||
err = bt_gatt_ots_l2cap_init_v2();
|
||||
if (err) {
|
||||
LOG_ERR("OtsL2capInitFail[%d]", err);
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT */
|
||||
|
||||
err = bt_le_nimble_l2cap_init();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bt_le_l2cap_deinit(void)
|
||||
{
|
||||
LOG_DBG("L2capDeinit");
|
||||
|
||||
/* TODO: L2CAP server deinit */
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2021 Nordic Semiconductor
|
||||
* SPDX-FileCopyrightText: 2015-2016 Intel Corporation
|
||||
* SPDX-FileContributor: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/iso.h>
|
||||
#include <zephyr/bluetooth/common/bt_str.h>
|
||||
|
||||
#include <../host/hci_core.h>
|
||||
|
||||
#include "common/host.h"
|
||||
|
||||
static sys_slist_t scan_cbs = SYS_SLIST_STATIC_INIT(&scan_cbs);
|
||||
|
||||
_LIB_ONLY
|
||||
int bt_le_scan_cb_register(struct bt_le_scan_cb *cb)
|
||||
{
|
||||
LOG_DBG("ScanCbReg");
|
||||
|
||||
if (cb == NULL) {
|
||||
LOG_ERR("ScanCbNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sys_slist_find(&scan_cbs, &cb->node, NULL)) {
|
||||
LOG_WRN("ScanCbExist");
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
sys_slist_append(&scan_cbs, &cb->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_NOT_USED
|
||||
void bt_le_scan_cb_unregister(struct bt_le_scan_cb *cb)
|
||||
{
|
||||
LOG_DBG("ScanCbUnreg");
|
||||
|
||||
if (cb == NULL) {
|
||||
LOG_ERR("ScanCbNull");
|
||||
return;
|
||||
}
|
||||
|
||||
sys_slist_find_and_remove(&scan_cbs, &cb->node);
|
||||
}
|
||||
|
||||
static uint8_t get_adv_type(uint8_t evt_type)
|
||||
{
|
||||
/* LOG_DBG("GetAdvType[%02x]", evt_type); */
|
||||
|
||||
switch (evt_type) {
|
||||
case (BT_HCI_LE_ADV_EVT_TYPE_CONN |
|
||||
BT_HCI_LE_ADV_EVT_TYPE_SCAN |
|
||||
BT_HCI_LE_ADV_EVT_TYPE_LEGACY):
|
||||
return BT_GAP_ADV_TYPE_ADV_IND;
|
||||
|
||||
case (BT_HCI_LE_ADV_EVT_TYPE_CONN |
|
||||
BT_HCI_LE_ADV_EVT_TYPE_DIRECT |
|
||||
BT_HCI_LE_ADV_EVT_TYPE_LEGACY):
|
||||
return BT_GAP_ADV_TYPE_ADV_DIRECT_IND;
|
||||
|
||||
case (BT_HCI_LE_ADV_EVT_TYPE_SCAN |
|
||||
BT_HCI_LE_ADV_EVT_TYPE_LEGACY):
|
||||
return BT_GAP_ADV_TYPE_ADV_SCAN_IND;
|
||||
|
||||
case BT_HCI_LE_ADV_EVT_TYPE_LEGACY:
|
||||
return BT_GAP_ADV_TYPE_ADV_NONCONN_IND;
|
||||
|
||||
case (BT_HCI_LE_ADV_EVT_TYPE_SCAN_RSP |
|
||||
BT_HCI_LE_ADV_EVT_TYPE_CONN |
|
||||
BT_HCI_LE_ADV_EVT_TYPE_SCAN |
|
||||
BT_HCI_LE_ADV_EVT_TYPE_LEGACY):
|
||||
case (BT_HCI_LE_ADV_EVT_TYPE_SCAN_RSP |
|
||||
BT_HCI_LE_ADV_EVT_TYPE_SCAN |
|
||||
BT_HCI_LE_ADV_EVT_TYPE_LEGACY):
|
||||
return BT_GAP_ADV_TYPE_SCAN_RSP;
|
||||
|
||||
default:
|
||||
return BT_GAP_ADV_TYPE_EXT_ADV;
|
||||
}
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
void bt_le_scan_recv_listener(uint16_t event_type,
|
||||
uint8_t addr_type,
|
||||
uint8_t addr_val[6],
|
||||
int8_t rssi,
|
||||
int8_t tx_power,
|
||||
uint8_t sid,
|
||||
uint8_t pri_phy,
|
||||
uint8_t sec_phy,
|
||||
uint16_t per_adv_itvl,
|
||||
uint8_t data_len,
|
||||
uint8_t *data)
|
||||
{
|
||||
struct net_buf_simple_state state = {0};
|
||||
struct bt_le_scan_recv_info info = {0};
|
||||
struct bt_le_scan_cb *listener = NULL;
|
||||
struct net_buf_simple buf = {0};
|
||||
bt_addr_le_t addr = {0};
|
||||
|
||||
/* LOG_DBG("ScanRecvListener[%u]", data_len); */
|
||||
|
||||
if (data == NULL || data_len == 0) {
|
||||
LOG_DBG("ScanRecvDataNull[%u]", data_len);
|
||||
return;
|
||||
}
|
||||
|
||||
addr.type = addr_type;
|
||||
memcpy(addr.a.val, addr_val, BT_ADDR_SIZE);
|
||||
|
||||
info.addr = &addr;
|
||||
info.sid = sid;
|
||||
info.rssi = rssi;
|
||||
info.tx_power = tx_power;
|
||||
info.adv_type = get_adv_type(event_type);
|
||||
info.adv_props = event_type;
|
||||
info.interval = per_adv_itvl;
|
||||
info.primary_phy = pri_phy;
|
||||
info.secondary_phy = sec_phy;
|
||||
|
||||
net_buf_simple_init_with_data(&buf, (void *)data, data_len);
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&scan_cbs, listener, node) {
|
||||
if (listener->recv) {
|
||||
net_buf_simple_save(&buf, &state);
|
||||
listener->recv(&info, &buf);
|
||||
net_buf_simple_restore(&buf, &state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct bt_le_per_adv_sync per_adv_sync_pool[CONFIG_BT_PER_ADV_SYNC_MAX];
|
||||
|
||||
static sys_slist_t pa_sync_cbs = SYS_SLIST_STATIC_INIT(&pa_sync_cbs);
|
||||
|
||||
_LIB_ONLY
|
||||
int bt_le_per_adv_sync_cb_register(struct bt_le_per_adv_sync_cb *cb)
|
||||
{
|
||||
LOG_DBG("PaSyncCbReg");
|
||||
|
||||
if (cb == NULL) {
|
||||
LOG_ERR("PaSyncCbNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sys_slist_find(&pa_sync_cbs, &cb->node, NULL)) {
|
||||
LOG_WRN("PaSyncCbExist");
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
sys_slist_append(&pa_sync_cbs, &cb->node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_LIB_ONLY
|
||||
int bt_le_per_adv_sync_get_info(struct bt_le_per_adv_sync *per_adv_sync,
|
||||
struct bt_le_per_adv_sync_info *info)
|
||||
{
|
||||
LOG_DBG("PaSyncGetInfo");
|
||||
|
||||
if (per_adv_sync == NULL || info == NULL) {
|
||||
LOG_ERR("PaSyncInfoNull[%p][%p]", per_adv_sync, info);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bt_addr_le_copy(&info->addr, &per_adv_sync->addr);
|
||||
info->sid = per_adv_sync->sid;
|
||||
info->phy = per_adv_sync->phy;
|
||||
info->interval = per_adv_sync->interval;
|
||||
|
||||
LOG_DBG("[%s][%u][%u][%u]",
|
||||
bt_addr_le_str(&info->addr), info->sid, info->phy, info->interval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_LIB_ONLY
|
||||
struct bt_le_per_adv_sync *bt_le_per_adv_sync_lookup_addr(const bt_addr_le_t *adv_addr,
|
||||
uint8_t sid)
|
||||
{
|
||||
struct bt_le_per_adv_sync *per_adv_sync = NULL;
|
||||
|
||||
assert(adv_addr);
|
||||
|
||||
LOG_DBG("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) &&
|
||||
bt_addr_le_eq(&per_adv_sync_pool[i].addr, adv_addr) &&
|
||||
per_adv_sync_pool[i].sid == sid) {
|
||||
per_adv_sync = &per_adv_sync_pool[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (per_adv_sync == NULL) {
|
||||
LOG_INF("PaSyncNotFound");
|
||||
}
|
||||
|
||||
return per_adv_sync;
|
||||
}
|
||||
|
||||
static struct bt_le_per_adv_sync *per_adv_sync_find(uint16_t handle)
|
||||
{
|
||||
struct bt_le_per_adv_sync *per_adv_sync = NULL;
|
||||
|
||||
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) &&
|
||||
per_adv_sync_pool[i].handle == handle) {
|
||||
per_adv_sync = &per_adv_sync_pool[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return per_adv_sync;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
struct bt_le_per_adv_sync *bt_le_per_adv_sync_find_safe(uint16_t sync_handle)
|
||||
{
|
||||
struct bt_le_per_adv_sync *per_adv_sync = NULL;
|
||||
LOG_DBG("PaSyncFind[%u]", sync_handle);
|
||||
bt_le_host_lock();
|
||||
per_adv_sync = per_adv_sync_find(sync_handle);
|
||||
bt_le_host_unlock();
|
||||
return per_adv_sync;
|
||||
}
|
||||
|
||||
static struct bt_le_per_adv_sync *per_adv_sync_new(void)
|
||||
{
|
||||
struct bt_le_per_adv_sync *per_adv_sync = NULL;
|
||||
|
||||
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) == false) {
|
||||
per_adv_sync = &per_adv_sync_pool[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (per_adv_sync) {
|
||||
(void)memset(per_adv_sync, 0, sizeof(*per_adv_sync));
|
||||
atomic_set_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED);
|
||||
}
|
||||
|
||||
return per_adv_sync;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_per_adv_sync_new(uint16_t sync_handle,
|
||||
uint8_t sid,
|
||||
uint8_t phy,
|
||||
uint16_t interval,
|
||||
uint8_t addr_type,
|
||||
const uint8_t addr[6],
|
||||
struct bt_le_per_adv_sync **out_sync)
|
||||
{
|
||||
struct bt_le_per_adv_sync *per_adv_sync;
|
||||
|
||||
LOG_DBG("PaSyncNew[%u][%u][%u][%u]", sync_handle, sid, phy, interval);
|
||||
|
||||
if (addr_type > BT_ADDR_LE_RANDOM_ID || addr == NULL) {
|
||||
LOG_ERR("InvAddr[%02x][%p]", addr_type, addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
per_adv_sync = per_adv_sync_find(sync_handle);
|
||||
if (per_adv_sync) {
|
||||
LOG_WRN("PaSyncExist[%u]", sync_handle);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
per_adv_sync = per_adv_sync_new();
|
||||
if (per_adv_sync == NULL) {
|
||||
LOG_ERR("NoFreePaSync[%u]", sync_handle);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
per_adv_sync->handle = sync_handle;
|
||||
per_adv_sync->sid = sid;
|
||||
per_adv_sync->phy = phy;
|
||||
per_adv_sync->interval = interval;
|
||||
per_adv_sync->addr.type = addr_type;
|
||||
memcpy(per_adv_sync->addr.a.val, addr, BT_ADDR_SIZE);
|
||||
|
||||
LOG_DBG("Addr[%s]", bt_addr_le_str(&per_adv_sync->addr));
|
||||
|
||||
if (out_sync) {
|
||||
*out_sync = per_adv_sync;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_per_adv_sync_delete(uint16_t sync_handle)
|
||||
{
|
||||
struct bt_le_per_adv_sync *per_adv_sync = NULL;
|
||||
|
||||
LOG_DBG("PaSyncDelete[%u]", sync_handle);
|
||||
|
||||
per_adv_sync = per_adv_sync_find(sync_handle);
|
||||
if (per_adv_sync == NULL) {
|
||||
LOG_ERR("PaSyncNotFound[%u]", sync_handle);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
(void)memset(per_adv_sync, 0, sizeof(*per_adv_sync));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_per_adv_sync_establish_listener(uint16_t sync_handle)
|
||||
{
|
||||
struct bt_le_per_adv_sync_synced_info info = {0};
|
||||
struct bt_le_per_adv_sync *per_adv_sync = NULL;
|
||||
struct bt_le_per_adv_sync_cb *listener = NULL;
|
||||
|
||||
LOG_DBG("PaSyncEstabListener[%u]", sync_handle);
|
||||
|
||||
per_adv_sync = per_adv_sync_find(sync_handle);
|
||||
if (per_adv_sync == NULL) {
|
||||
LOG_ERR("PaSyncNotFound[%u]", sync_handle);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
info.addr = &per_adv_sync->addr;
|
||||
info.sid = per_adv_sync->sid;
|
||||
info.interval = per_adv_sync->interval;
|
||||
info.phy = per_adv_sync->phy;
|
||||
info.conn = NULL; /* TODO: update for PAST */
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) {
|
||||
if (listener->synced) {
|
||||
listener->synced(per_adv_sync, &info);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_per_adv_sync_lost_listener(uint16_t sync_handle)
|
||||
{
|
||||
struct bt_le_per_adv_sync_term_info info = {0};
|
||||
struct bt_le_per_adv_sync *per_adv_sync = NULL;
|
||||
struct bt_le_per_adv_sync_cb *listener = NULL;
|
||||
|
||||
LOG_DBG("PaSyncLostListener[%u]", sync_handle);
|
||||
|
||||
per_adv_sync = per_adv_sync_find(sync_handle);
|
||||
if (per_adv_sync == NULL) {
|
||||
LOG_ERR("PaSyncNotFound[%u]", sync_handle);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
info.addr = &per_adv_sync->addr;
|
||||
info.sid = per_adv_sync->sid;
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) {
|
||||
if (listener->term) {
|
||||
listener->term(per_adv_sync, &info);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_per_adv_sync_report_recv_listener(uint16_t sync_handle,
|
||||
const uint8_t *data,
|
||||
size_t data_length)
|
||||
{
|
||||
struct bt_le_per_adv_sync_recv_info info = {0};
|
||||
struct bt_le_per_adv_sync *per_adv_sync = NULL;
|
||||
struct bt_le_per_adv_sync_cb *listener = NULL;
|
||||
struct net_buf_simple_state state = {0};
|
||||
struct net_buf_simple buf = {0};
|
||||
|
||||
/* LOG_DBG("PaSyncReportRecvListener[%u][%u]", sync_handle, data_length); */
|
||||
|
||||
if (data == NULL || data_length == 0) {
|
||||
LOG_DBG("PaSyncRecvDataNull[%u]", data_length);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
per_adv_sync = per_adv_sync_find(sync_handle);
|
||||
if (per_adv_sync == NULL) {
|
||||
LOG_ERR("PaSyncNotFound[%u]", sync_handle);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
info.addr = &per_adv_sync->addr;
|
||||
info.sid = per_adv_sync->sid;
|
||||
|
||||
net_buf_simple_init_with_data(&buf, (void *)data, data_length);
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) {
|
||||
if (listener->recv) {
|
||||
net_buf_simple_save(&buf, &state);
|
||||
listener->recv(per_adv_sync, &info, &buf);
|
||||
net_buf_simple_restore(&buf, &state);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
void hci_le_biginfo_adv_report(struct net_buf *buf)
|
||||
{
|
||||
struct bt_hci_evt_le_biginfo_adv_report *evt = (void *)buf->data;
|
||||
struct bt_le_per_adv_sync *per_adv_sync = NULL;
|
||||
struct bt_le_per_adv_sync_cb *listener = NULL;
|
||||
struct bt_iso_biginfo biginfo = {0};
|
||||
|
||||
/* LOG_DBG("BigInfoRecvListener[%u]", evt->sync_handle); */
|
||||
|
||||
per_adv_sync = per_adv_sync_find(evt->sync_handle);
|
||||
if (per_adv_sync == NULL) {
|
||||
LOG_ERR("PaSyncNotFound[%u]", evt->sync_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
biginfo.addr = &per_adv_sync->addr;
|
||||
biginfo.sid = per_adv_sync->sid;
|
||||
biginfo.num_bis = evt->num_bis;
|
||||
biginfo.sub_evt_count = evt->nse;
|
||||
biginfo.iso_interval = evt->iso_interval;
|
||||
biginfo.burst_number = evt->bn;
|
||||
biginfo.offset = evt->pto;
|
||||
biginfo.rep_count = evt->irc;
|
||||
biginfo.max_pdu = evt->max_pdu;
|
||||
biginfo.sdu_interval = sys_get_le24(evt->sdu_interval);
|
||||
biginfo.max_sdu = evt->max_sdu;
|
||||
biginfo.phy = evt->phy;
|
||||
biginfo.framing = evt->framing;
|
||||
biginfo.encryption = (evt->encryption ? true : false);
|
||||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) {
|
||||
if (listener->biginfo) {
|
||||
listener->biginfo(per_adv_sync, &biginfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_LIB_ONLY
|
||||
int bt_le_scan_start(const struct bt_le_scan_param *param, void *cb)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) == false) {
|
||||
err = bt_le_nimble_scan_start(param, cb);
|
||||
if (err == 0) {
|
||||
atomic_set_bit(bt_dev.flags, BT_DEV_SCANNING);
|
||||
}
|
||||
} else {
|
||||
err = -EALREADY;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
_LIB_ONLY
|
||||
int bt_le_scan_stop(void)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) {
|
||||
err = bt_le_nimble_scan_stop();
|
||||
if (err == 0) {
|
||||
atomic_clear_bit(bt_dev.flags, BT_DEV_SCANNING);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <../host/conn_internal.h>
|
||||
|
||||
#include "common/host.h"
|
||||
#include "common/iso.h"
|
||||
#include "common/app/gap.h"
|
||||
#include "common/app/gatt.h"
|
||||
|
||||
static QueueHandle_t iso_queue_handle;
|
||||
|
||||
static TaskHandle_t iso_task_handle;
|
||||
|
||||
extern void bt_le_timer_handle_event(void *arg);
|
||||
|
||||
static void iso_task(void *p)
|
||||
{
|
||||
struct iso_queue_item item = {0};
|
||||
|
||||
while (1) {
|
||||
xQueueReceive(iso_queue_handle, &item, portMAX_DELAY);
|
||||
|
||||
switch (item.type) {
|
||||
case ISO_QUEUE_ITEM_TYPE_TIMER_EVENT:
|
||||
bt_le_timer_handle_event(item.data);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_GAP_EVENT:
|
||||
bt_le_gap_handle_event(item.data, item.data_len);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_GATT_EVENT:
|
||||
bt_le_gatt_handle_event(item.data, item.data_len);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_ISO_HCI_EVENT:
|
||||
bt_le_iso_handle_hci_event(item.data, item.data_len);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_ISO_TX_COMP:
|
||||
bt_le_iso_handle_tx_comp(item.data, item.data_len);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_ISO_RX_DATA:
|
||||
bt_le_iso_handle_rx_data(item.data, item.data_len);
|
||||
break;
|
||||
default:
|
||||
if (item.data) {
|
||||
free(item.data);
|
||||
}
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int bt_le_iso_task_post(enum iso_queue_item_type type,
|
||||
void *data, size_t data_len)
|
||||
{
|
||||
struct iso_queue_item item = {0};
|
||||
int ret;
|
||||
|
||||
item.type = type;
|
||||
item.data = data;
|
||||
item.data_len = data_len;
|
||||
|
||||
ret = xQueueSend(iso_queue_handle, &item, portMAX_DELAY);
|
||||
if (ret != pdTRUE) {
|
||||
LOG_ERR("IsoQPostFail[%d]", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_le_iso_task_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
LOG_DBG("IsoTaskInit");
|
||||
|
||||
iso_queue_handle = xQueueCreate(ISO_QUEUE_ITEM_COUNT, ISO_QUEUE_ITEM_SIZE);
|
||||
if (iso_queue_handle == NULL) {
|
||||
LOG_ERR("IsoQCreateFail");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = xTaskCreatePinnedToCore(iso_task,
|
||||
ISO_TASK_NAME,
|
||||
ISO_TASK_STACK_SIZE,
|
||||
NULL,
|
||||
ISO_TASK_PRIO,
|
||||
&iso_task_handle,
|
||||
ISO_TASK_CORE);
|
||||
if (ret != pdTRUE) {
|
||||
LOG_ERR("IsoTaskCreateFail[%d]", ret);
|
||||
vQueueDelete(iso_queue_handle);
|
||||
iso_queue_handle = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bt_le_iso_task_deinit(void)
|
||||
{
|
||||
LOG_DBG("IsoTaskDeinit");
|
||||
|
||||
if (iso_task_handle) {
|
||||
vTaskDelete(iso_task_handle);
|
||||
iso_task_handle = NULL;
|
||||
}
|
||||
|
||||
if (iso_queue_handle) {
|
||||
vQueueDelete(iso_queue_handle);
|
||||
iso_queue_handle = NULL;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user