nimble/host: initial Broadcast source implementation

This patch implements Broadcast feature in host. It includes
implementation of ISO in host, with application API, HCI commands and
ISO event handling. Host is now capable of managing BIGs with subgroups
and BISes, updating Broadcast announcements and pushing data to ISO, to
send in BIS events.

Maximum extended advertising size was set to 251 to fit Periodic
advertising data connected with Broadcast Audio
Announcement Service advertisements by default.

This patch does not implement Broadcast sink, only source.
This commit is contained in:
Krzysztof Kopyściński
2023-12-04 09:27:13 +01:00
committed by Krzysztof Kopyściński
parent b0fdcb9d50
commit 7e7b4f00c4
10 changed files with 993 additions and 3 deletions
+1
View File
@@ -43,6 +43,7 @@
#include "host/ble_sm.h"
#include "host/ble_store.h"
#include "host/ble_uuid.h"
#include "host/ble_iso.h"
#include "nimble/nimble_npl.h"
#ifdef __cplusplus
+3
View File
@@ -249,6 +249,9 @@ struct ble_hs_adv_fields {
/** Common Data Type: Mesh Beacon. */
#define BLE_HS_ADV_TYPE_MESH_BEACON 0x2b
/** Common Data Type: Broadcast Name. */
#define BLE_HS_ADV_TYPE_BROADCAST_NAME 0x30
/** Common Data Type: Manufacturer Specific Data. */
#define BLE_HS_ADV_TYPE_MFG_DATA 0xff
-3
View File
@@ -21,8 +21,6 @@
#define H_BLE_ISO_
#include "syscfg/syscfg.h"
#if MYNEWT_VAL(BLE_ISO)
/** ISO event: BIG Create Completed */
#define BLE_ISO_EVENT_BIG_CREATE_COMPLETE 0
@@ -124,4 +122,3 @@ int ble_iso_tx(uint16_t conn_handle, void *data, uint16_t data_len);
int ble_iso_init(void);
#endif
#endif
+486
View File
@@ -0,0 +1,486 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "host/ble_uuid.h"
#include "host/ble_audio_broadcast.h"
#include "os/util.h"
#if MYNEWT_VAL(BLE_ISO_BROADCASTER)
struct ble_audio_broadcast {
SLIST_ENTRY(ble_audio_broadcast) next;
uint8_t adv_instance;
struct ble_audio_base *base;
struct ble_iso_big_params *big_params;
ble_audio_broadcast_destroy_fn *destroy_cb;
void *args;
};
static SLIST_HEAD(, ble_audio_broadcast) ble_audio_broadcasts;
static struct os_mempool ble_audio_broadcast_pool;
static os_membuf_t ble_audio_broadcast_mem[
OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_MAX_BIG),
sizeof(struct ble_audio_broadcast))];
static bool
broadcast_bis_idx_ok(uint8_t bis_idx, struct ble_audio_base *base)
{
struct ble_audio_big_subgroup *big;
struct ble_audio_bis *bis;
uint8_t idx_cnt = 0;
STAILQ_FOREACH(big, &base->subs, next) {
STAILQ_FOREACH(bis, &big->bises, next) {
if (bis_idx == bis->idx) {
idx_cnt++;
}
}
}
return idx_cnt == 1;
}
static bool
broadcast_ltv_ok(uint8_t total_len, uint8_t *data)
{
if (total_len == 0 && data == NULL) {
return true;
}
uint8_t len = data[0];
uint8_t sum_len = 0;
while (total_len > sum_len) {
sum_len += len + 1;
len = data[sum_len];
}
return total_len == sum_len;
}
static struct ble_audio_broadcast *
ble_audio_broadcast_find(uint8_t adv_instance)
{
struct ble_audio_broadcast *broadcast;
SLIST_FOREACH(broadcast, &ble_audio_broadcasts, next) {
if (broadcast->adv_instance == adv_instance) {
return broadcast;
}
}
return NULL;
}
static struct ble_audio_broadcast *
ble_audio_broadcast_find_last()
{
struct ble_audio_broadcast *broadcast;
SLIST_FOREACH(broadcast, &ble_audio_broadcasts, next) {
}
return broadcast;
}
int
ble_audio_broadcast_create(const struct ble_broadcast_create_params *params,
ble_audio_broadcast_destroy_fn *destroy_cb,
void *args,
ble_gap_event_fn *gap_cb)
{
int rc;
uint8_t offset = 2;
uint8_t service_data[5] = {0x52, 0x18 };
uint8_t per_svc_data[MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE)] = { 0x51, 0x18 };
struct ble_audio_big_subgroup *subgroup;
struct ble_audio_bis *bis;
struct ble_hs_adv_fields adv_fields = {
.broadcast_name = (uint8_t *) params->name,
.broadcast_name_len = params->name != NULL ? strlen(params->name) : 0,
.svc_data_uuid16 = service_data,
.svc_data_uuid16_len = sizeof(service_data),
};
struct ble_hs_adv_fields per_adv_fields = { 0 };
struct os_mbuf *adv_data;
ble_uuid16_t audio_announcement_uuid[1];
uint8_t broadcast_id[3];
struct ble_audio_broadcast *broadcast;
if (strlen(params->name) < 4 || strlen(params->name) > 32) {
return BLE_HS_EINVAL;
}
broadcast = ble_audio_broadcast_find(params->adv_instance);
if (broadcast) {
return BLE_HS_EALREADY;
}
ble_hs_hci_rand(broadcast_id, 3);
params->base->broadcast_id = get_le24(broadcast_id);
broadcast = os_memblock_get(&ble_audio_broadcast_pool);
broadcast->adv_instance = params->adv_instance;
broadcast->base = params->base;
broadcast->big_params = params->big_params;
broadcast->destroy_cb = destroy_cb;
broadcast->args = args;
if (SLIST_EMPTY(&ble_audio_broadcasts)) {
SLIST_INSERT_HEAD(&ble_audio_broadcasts, broadcast, next);
} else {
SLIST_INSERT_AFTER(ble_audio_broadcast_find_last(), broadcast, next);
}
if (params->base->num_subgroups == 0) {
/**
* BAP specification 3.7.2.2 Basic Audio Announcements:
* Rule 1: There shall be at least one subgroup.
*/
BLE_HS_LOG_ERROR("No subgroups in BASE!\n");
return BLE_HS_EINVAL;
}
put_le24(&service_data[2], broadcast->base->broadcast_id);
put_le24(&per_svc_data[offset], params->base->presentation_delay);
offset += 3;
per_svc_data[offset] = params->base->num_subgroups;
offset++;
STAILQ_FOREACH(subgroup, &params->base->subs, next) {
if (subgroup->bis_cnt == 0) {
/**
* BAP specification 3.7.2.2 Basic Audio Announcements:
* Rule 2: There shall be at least one BIS per subgroup.
*/
BLE_HS_LOG_ERROR("No BIS in BIG!\n");
return BLE_HS_EINVAL;
}
if (!broadcast_ltv_ok(subgroup->metadata_len, subgroup->metadata)) {
BLE_HS_LOG_ERROR("Invalid Metadata!\n");
return BLE_HS_EINVAL;
}
if (!broadcast_ltv_ok(subgroup->codec_spec_config_len, subgroup->codec_spec_config)) {
BLE_HS_LOG_ERROR("Invalid Codec Configuration in subgroup!\n");
return BLE_HS_EINVAL;
}
per_svc_data[offset] = subgroup->bis_cnt;
offset++;
memcpy(&per_svc_data[offset], &subgroup->codec_id, 5);
offset += 5;
per_svc_data[offset] = subgroup->codec_spec_config_len;
offset++;
memcpy(&per_svc_data[offset], &subgroup->codec_spec_config,
subgroup->codec_spec_config_len);
offset += subgroup->codec_spec_config_len;
per_svc_data[offset] = subgroup->metadata_len;
offset++;
memcpy(&per_svc_data[offset], &subgroup->metadata,
subgroup->metadata_len);
offset += subgroup->metadata_len;
STAILQ_FOREACH(bis, &subgroup->bises, next) {
if (!broadcast_bis_idx_ok(bis->idx, params->base)) {
/**
* BAP specification 3.7.2.2 Basic Audio Announcements:
* Rule 3: Every BIS in the BIG, denoted by its BIS_index
* value, shall only be present in one subgroup.
*/
BLE_HS_LOG_ERROR("Duplicated BIS index!\n");
return BLE_HS_EINVAL;
}
if (!broadcast_ltv_ok(bis->codec_spec_config_len,
bis->codec_spec_config)) {
BLE_HS_LOG_ERROR("Invalid Codec Configuration in BIS!\n");
return BLE_HS_EINVAL;
}
per_svc_data[offset] = bis->idx;
offset++;
per_svc_data[offset] = bis->codec_spec_config_len;
offset++;
memcpy(&per_svc_data[offset], bis->codec_spec_config,
bis->codec_spec_config_len);
offset += bis->codec_spec_config_len;
}
}
audio_announcement_uuid[0] = (ble_uuid16_t) BLE_UUID16_INIT(BLE_BROADCAST_AUDIO_ANNOUNCEMENT_SVC_UUID);
per_adv_fields.uuids16 = audio_announcement_uuid;
per_adv_fields.num_uuids16 = 1;
per_adv_fields.uuids16_is_complete = 1;
per_adv_fields.svc_data_uuid16 = per_svc_data;
per_adv_fields.svc_data_uuid16_len = offset;
rc = ble_gap_ext_adv_configure(params->adv_instance,
params->extended_params, 0,
gap_cb, NULL);
if (rc) {
BLE_HS_LOG_ERROR("Could not configure the broadcast (rc=%d)\n", rc);
return 0;
}
adv_data = os_msys_get_pkthdr(BLE_HCI_MAX_EXT_ADV_DATA_LEN, 0);
if (!adv_data) {
BLE_HS_LOG_ERROR("No memory\n");
return BLE_HS_ENOMEM;
}
/* Set ext advertising data */
rc = ble_hs_adv_set_fields_mbuf(&adv_fields, adv_data);
if (rc) {
BLE_HS_LOG_ERROR("Failed to set extended advertising fields"
"(rc=%d)\n", rc);
return rc;
}
os_mbuf_append(adv_data, params->svc_data, params->svc_data_len);
rc = ble_gap_ext_adv_set_data(params->adv_instance, adv_data);
if (rc) {
BLE_HS_LOG_ERROR("Failed to set extended advertising data"
"(rc=%d)\n", rc);
return rc;
}
/* Clear buffer */
adv_data = os_msys_get_pkthdr(BLE_HCI_MAX_EXT_ADV_DATA_LEN, 0);
if (!adv_data) {
BLE_HS_LOG_ERROR("No memory\n");
return BLE_HS_ENOMEM;
}
rc = ble_gap_periodic_adv_configure(params->adv_instance, params->periodic_params);
if (rc) {
BLE_HS_LOG_ERROR("failed to configure periodic advertising"
"(rc=%d)\n", rc);
return rc;
}
rc = ble_hs_adv_set_fields_mbuf(&per_adv_fields, adv_data);
if (rc) {
BLE_HS_LOG_ERROR("Failed to set periodic advertising fields"
"(rc=%d)\n", rc);
return rc;
}
rc = ble_gap_periodic_adv_set_data(params->adv_instance, adv_data, NULL);
if (rc) {
BLE_HS_LOG_ERROR("Failed to set periodic advertising data"
"(rc=%d)\n", rc);
}
return rc;
}
int
ble_audio_broadcast_start(uint8_t adv_instance,
ble_iso_event_fn *cb, void *cb_arg)
{
struct ble_audio_big_subgroup *subgroup;
struct ble_iso_create_big_params create_big_params = { 0 };
struct ble_audio_broadcast *broadcast;
int rc;
broadcast = ble_audio_broadcast_find(adv_instance);
if (!broadcast) {
return BLE_HS_ENOENT;
}
STAILQ_FOREACH(subgroup, &broadcast->base->subs, next) {
create_big_params.bis_cnt += subgroup->bis_cnt;
}
create_big_params.adv_handle = broadcast->adv_instance;
create_big_params.cb = cb;
create_big_params.cb_arg = cb_arg;
rc = ble_gap_ext_adv_start(broadcast->adv_instance, 0, 0);
if (rc) {
BLE_HS_LOG_ERROR("Failed to start extended advertising (rc=%d)\n", rc);
return rc;
}
rc = ble_gap_periodic_adv_start(broadcast->adv_instance, NULL);
if (rc) {
BLE_HS_LOG_ERROR("Failed to start periodic advertising (rc=%d)\n", rc);
return rc;
}
rc = ble_iso_create_big(&create_big_params, broadcast->big_params);
if (rc) {
BLE_HS_LOG_ERROR("Failed to create BIG (rc=%d)\n", rc);
return rc;
}
return 0;
}
int
ble_audio_broadcast_stop(uint8_t adv_instance)
{
int rc;
rc = ble_gap_ext_adv_stop(adv_instance);
if (rc) {
BLE_HS_LOG_ERROR("Failed to stop extended advertising (rc=%d)\n", rc);
return rc;
}
rc = ble_gap_periodic_adv_stop(adv_instance);
if (rc) {
BLE_HS_LOG_ERROR("Failed to stop periodic advertising (rc=%d)\n", rc);
return rc;
}
return 0;
}
int
ble_audio_broadcast_destroy(uint8_t adv_instance)
{
struct ble_audio_broadcast *broadcast;
int rc;
broadcast = ble_audio_broadcast_find(adv_instance);
if (!broadcast) {
return BLE_HS_ENOENT;
}
rc = ble_gap_ext_adv_remove(adv_instance);
if (rc) {
BLE_HS_LOG_ERROR("Failed to remove extended advertising\n");
return rc;
}
rc = ble_iso_terminate_big(adv_instance);
if (rc) {
BLE_HS_LOG_ERROR("Failed to terminate BIG\n");
return rc;
}
os_memblock_put(&ble_audio_broadcast_pool, broadcast);
SLIST_REMOVE(&ble_audio_broadcasts, broadcast, ble_audio_broadcast, next);
return 0;
}
int
ble_audio_broadcast_update(const struct ble_broadcast_update_params *params)
{
uint8_t service_data[5] = {0x52, 0x18 };
struct ble_hs_adv_fields adv_fields = {
.broadcast_name = (uint8_t *) params->name,
.broadcast_name_len = params->name != NULL ? strlen(params->name) : 0,
.svc_data_uuid16 = service_data,
.svc_data_uuid16_len = sizeof(service_data)
};
struct os_mbuf *adv_data;
int rc;
if (strlen(params->name) < 4 || strlen(params->name) > 32) {
return BLE_HS_EINVAL;
}
memcpy(&service_data[2], &params->broadcast_id, 3);
adv_data = os_msys_get_pkthdr(BLE_HCI_MAX_EXT_ADV_DATA_LEN, 0);
os_mbuf_append(adv_data, params->svc_data, params->svc_data_len);
/* Set ext advertising data */
rc = ble_hs_adv_set_fields_mbuf(&adv_fields, adv_data);
if (rc) {
BLE_HS_LOG_ERROR("Failed to set extended advertising fields\n");
return rc;
}
rc = ble_gap_ext_adv_set_data(params->adv_instance, adv_data);
if (rc) {
BLE_HS_LOG_ERROR("Failed to set extended advertising data\n");
return rc;
}
return 0;
}
int
ble_audio_broadcast_build_sub(struct ble_audio_base *base,
struct ble_audio_big_subgroup *sub,
const struct ble_broadcast_subgroup_params
*params)
{
sub->codec_id = *params->codec_id;
memcpy(sub->codec_spec_config, params->codec_spec_config,
params->codec_spec_config_len);
sub->codec_spec_config_len = params->codec_spec_config_len;
memcpy(sub->metadata, params->metadata,
params->metadata_len);
sub->metadata_len = params->metadata_len;
if (STAILQ_EMPTY(&base->subs)) {
STAILQ_INSERT_HEAD(&base->subs, sub, next);
} else {
STAILQ_INSERT_TAIL(&base->subs, sub, next);
}
base->num_subgroups++;
return 0;
}
int
ble_audio_broadcast_build_bis(struct ble_audio_big_subgroup *sub,
struct ble_audio_bis *bis,
const struct ble_broadcast_bis_params
*params)
{
bis->idx = params->idx;
memcpy(bis->codec_spec_config, params->codec_spec_config,
params->codec_spec_config_len);
bis->codec_spec_config_len = params->codec_spec_config_len;
if (STAILQ_EMPTY(&sub->bises)) {
STAILQ_INSERT_HEAD(&sub->bises, bis, next);
} else {
STAILQ_INSERT_TAIL(&sub->bises, bis, next);
}
sub->bis_cnt++;
return 0;
}
int
ble_audio_broadcast_init(void)
{
int rc;
SLIST_INIT(&ble_audio_broadcasts);
rc = os_mempool_init(&ble_audio_broadcast_pool,
MYNEWT_VAL(BLE_MAX_BIG),
sizeof(struct ble_audio_broadcast),
ble_audio_broadcast_mem, "ble_audio_broadcast_pool");
SYSINIT_PANIC_ASSERT(rc == 0);
return 0;
}
#endif
+10
View File
@@ -24,6 +24,7 @@
#include "syscfg/syscfg.h"
#include "stats/stats.h"
#include "host/ble_hs.h"
#include "host/ble_audio_broadcast.h"
#include "ble_hs_priv.h"
#include "nimble/nimble_npl.h"
#ifndef MYNEWT
@@ -752,6 +753,15 @@ ble_hs_init(void)
SYSINIT_PANIC_ASSERT(rc == 0);
#endif
#if MYNEWT_VAL(BLE_ISO)
rc = ble_iso_init();
SYSINIT_PANIC_ASSERT(rc == 0);
#if MYNEWT_VAL(BLE_ISO_BROADCASTER)
rc = ble_audio_broadcast_init();
SYSINIT_PANIC_ASSERT(rc == 0);
#endif
#endif
ble_hs_stop_init();
ble_mqueue_init(&ble_hs_rx_q, ble_hs_event_rx_data, NULL);
+11
View File
@@ -354,6 +354,17 @@ adv_set_fields(const struct ble_hs_adv_fields *adv_fields,
}
}
/*** 0x30 - Broadcast name. */
if (adv_fields->broadcast_name != NULL && adv_fields->broadcast_name_len > 0) {
rc = ble_hs_adv_set_flat_mbuf(BLE_HS_ADV_TYPE_BROADCAST_NAME,
adv_fields->broadcast_name_len,
adv_fields->broadcast_name, dst,
&dst_len_local, max_len, om);
if (rc != 0) {
return rc;
}
}
/*** 0x16 - Service data - 16-bit UUID. */
if (adv_fields->svc_data_uuid16 != NULL && adv_fields->svc_data_uuid16_len) {
rc = ble_hs_adv_set_flat_mbuf(BLE_HS_ADV_TYPE_SVC_DATA_UUID16,
+44
View File
@@ -23,7 +23,9 @@
#include "os/os.h"
#include "nimble/hci_common.h"
#include "host/ble_gap.h"
#include "host/ble_iso.h"
#include "ble_hs_priv.h"
#include "ble_iso_priv.h"
_Static_assert(sizeof (struct hci_data_hdr) == BLE_HCI_DATA_HDR_SZ,
"struct hci_data_hdr must be 4 bytes");
@@ -63,6 +65,10 @@ static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_rpt;
static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_sync_lost;
static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_scan_req_rcvd;
static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_periodic_adv_sync_transfer;
#if MYNEWT_VAL(BLE_ISO_BROADCASTER)
static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_create_big_complete;
static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_terminate_big_complete;
#endif
#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS)
static ble_hs_hci_evt_le_fn ble_hs_hci_evt_le_biginfo_adv_report;
#endif
@@ -131,6 +137,12 @@ static ble_hs_hci_evt_le_fn * const ble_hs_hci_evt_le_dispatch[] = {
[BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED] = ble_hs_hci_evt_le_adv_set_terminated,
[BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD] = ble_hs_hci_evt_le_scan_req_rcvd,
[BLE_HCI_LE_SUBEV_PERIODIC_ADV_SYNC_TRANSFER] = ble_hs_hci_evt_le_periodic_adv_sync_transfer,
#if MYNEWT_VAL(BLE_ISO_BROADCASTER)
[BLE_HCI_LE_SUBEV_CREATE_BIG_COMPLETE] =
ble_hs_hci_evt_le_create_big_complete,
[BLE_HCI_LE_SUBEV_TERMINATE_BIG_COMPLETE] =
ble_hs_hci_evt_le_terminate_big_complete,
#endif
#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS)
[BLE_HCI_LE_SUBEV_BIGINFO_ADV_REPORT] = ble_hs_hci_evt_le_biginfo_adv_report,
#endif
@@ -735,6 +747,38 @@ ble_hs_hci_evt_le_periodic_adv_sync_transfer(uint8_t subevent, const void *data,
return 0;
}
#if MYNEWT_VAL(BLE_ISO_BROADCASTER)
static int
ble_hs_hci_evt_le_create_big_complete(uint8_t subevent, const void *data,
unsigned int len)
{
const struct ble_hci_ev_le_subev_create_big_complete *ev = data;
if (len != sizeof(*ev) + (ev->num_bis * sizeof(ev->conn_handle[0]))) {
return BLE_HS_EBADDATA;
}
ble_iso_rx_create_big_complete(ev);
return 0;
}
static int
ble_hs_hci_evt_le_terminate_big_complete(uint8_t subevent, const void *data,
unsigned int len)
{
const struct ble_hci_ev_le_subev_terminate_big_complete *ev = data;
if (len != sizeof(*ev)) {
return BLE_HS_EBADDATA;
}
ble_iso_rx_terminate_big_complete(ev);
return 0;
}
#endif
#if MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS)
static int
ble_hs_hci_evt_le_biginfo_adv_report(uint8_t subevent, const void *data,
+386
View File
@@ -0,0 +1,386 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <inttypes.h>
#include "syscfg/syscfg.h"
#include "ble_hs_mbuf_priv.h"
#if MYNEWT_VAL(BLE_ISO)
#include "os/os_mbuf.h"
#include "host/ble_hs_log.h"
#include "host/ble_hs.h"
#include "host/ble_iso.h"
#include "nimble/hci_common.h"
#include "sys/queue.h"
#include "ble_hs_hci_priv.h"
struct ble_iso_big {
SLIST_ENTRY(ble_iso_big) next;
uint8_t id;
uint16_t max_pdu;
uint8_t num_bis;
uint16_t conn_handles[MYNEWT_VAL(BLE_MAX_BIS)];
ble_iso_event_fn *cb;
void *cb_arg;
};
static SLIST_HEAD(, ble_iso_big) ble_iso_bigs;
static struct os_mempool ble_iso_big_pool;
static os_membuf_t ble_iso_big_mem[
OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_MAX_BIG), sizeof (struct ble_iso_big))];
static struct ble_iso_big *
ble_iso_big_alloc(uint8_t adv_handle)
{
struct ble_iso_big *new_big;
struct ble_iso_big *big;
if (adv_handle >= BLE_ADV_INSTANCES) {
BLE_HS_LOG_ERROR("Invalid advertising instance");
return NULL;
}
if (!ble_gap_ext_adv_active(adv_handle)) {
BLE_HS_LOG_ERROR("Instance not active");
return NULL;
}
new_big = os_memblock_get(&ble_iso_big_pool);
if (new_big == NULL) {
BLE_HS_LOG_ERROR("No more memory in pool");
/* Out of memory. */
return NULL;
}
memset(new_big, 0, sizeof *new_big);
SLIST_FOREACH(big, &ble_iso_bigs, next) {
if (big->id == adv_handle) {
BLE_HS_LOG_ERROR("Advertising instance (%d) already in use",
adv_handle);
return NULL;
}
}
new_big->id = adv_handle;
if (SLIST_EMPTY(&ble_iso_bigs)) {
SLIST_INSERT_HEAD(&ble_iso_bigs, new_big, next);
} else {
SLIST_INSERT_AFTER(big, new_big, next);
}
return new_big;
}
static struct ble_iso_big *
ble_iso_big_find_by_id(uint8_t big_id)
{
struct ble_iso_big *big;
SLIST_FOREACH(big, &ble_iso_bigs, next) {
if (big->id == big_id) {
return big;
}
}
return NULL;
}
static int
ble_iso_big_free(struct ble_iso_big *big)
{
SLIST_REMOVE(&ble_iso_bigs, big, ble_iso_big, next);
os_memblock_put(&ble_iso_big_pool, big);
return 0;
}
int
ble_iso_create_big(const struct ble_iso_create_big_params *create_params,
const struct ble_iso_big_params *big_params)
{
struct ble_hci_le_create_big_cp cp = { 0 };
struct ble_iso_big *big;
big = ble_iso_big_alloc(create_params->adv_handle);
if (big == NULL) {
return BLE_HS_ENOENT;
}
big->cb = create_params->cb;
big->cb_arg = create_params->cb_arg;
cp.big_handle = create_params->adv_handle;
cp.adv_handle = create_params->adv_handle;
if (create_params->bis_cnt > MYNEWT_VAL(BLE_MAX_BIS)) {
return BLE_HS_EINVAL;
}
cp.num_bis = create_params->bis_cnt;
put_le24(cp.sdu_interval, big_params->sdu_interval);
cp.max_sdu = big_params->max_sdu;
cp.max_transport_latency = big_params->max_transport_latency;
cp.rtn = big_params->rtn;
cp.phy = big_params->phy;
cp.packing = big_params->packing;
cp.framing = big_params->framing;
cp.encryption = big_params->encryption;
if (big_params->encryption) {
memcpy(cp.broadcast_code, big_params->broadcast_code, 16);
}
return ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
BLE_HCI_OCF_LE_CREATE_BIG),
&cp, sizeof(cp),NULL, 0);
}
int
ble_iso_terminate_big(uint8_t big_id)
{
struct ble_hci_le_terminate_big_cp cp;
struct ble_iso_big *big;
int rc;
big = ble_iso_big_find_by_id(big_id);
if (big == NULL) {
BLE_HS_LOG_ERROR("No BIG with id=%d\n", big_id);
return BLE_HS_ENOENT;
}
cp.big_handle = big->id;
cp.reason = BLE_ERR_CONN_TERM_LOCAL;
rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE,
BLE_HCI_OCF_LE_TERMINATE_BIG),
&cp, sizeof(cp),NULL, 0);
return rc;
}
int
ble_iso_init(void)
{
int rc;
SLIST_INIT(&ble_iso_bigs);
rc = os_mempool_init(&ble_iso_big_pool,
MYNEWT_VAL(BLE_MAX_BIG),
sizeof (struct ble_iso_big),
ble_iso_big_mem, "ble_iso_big_pool");
SYSINIT_PANIC_ASSERT(rc == 0);
return 0;
}
void
ble_iso_rx_create_big_complete(const struct ble_hci_ev_le_subev_create_big_complete *ev)
{
struct ble_iso_event event;
struct ble_iso_big *big;
int i;
big = ble_iso_big_find_by_id(ev->big_handle);
big->num_bis = ev->num_bis;
for (i = 0; i < ev->num_bis; i++) {
big->conn_handles[i] = ev->conn_handle[i];
}
big->max_pdu = ev->max_pdu;
event.type = BLE_ISO_EVENT_BIG_CREATE_COMPLETE;
event.big_created.desc.big_handle = ev->big_handle;
event.big_created.desc.big_sync_delay = get_le24(ev->big_sync_delay);
event.big_created.desc.transport_latency_big =
get_le24(ev->transport_latency_big);
event.big_created.desc.phy = ev->phy;
event.big_created.desc.nse = ev->nse;
event.big_created.desc.bn = ev->bn;
event.big_created.desc.pto = ev->pto;
event.big_created.desc.irc = ev->irc;
event.big_created.desc.max_pdu = ev->max_pdu;
event.big_created.desc.iso_interval = ev->iso_interval;
event.big_created.desc.num_bis = ev->num_bis;
memcpy(event.big_created.desc.conn_handle, ev->conn_handle,
ev->num_bis * sizeof(uint16_t));
if (big->cb != NULL) {
big->cb(&event, big->cb_arg);
}
}
void
ble_iso_rx_terminate_big_complete(const struct ble_hci_ev_le_subev_terminate_big_complete *ev)
{
struct ble_iso_event event;
struct ble_iso_big *big;
big = ble_iso_big_find_by_id(ev->big_handle);
event.type = BLE_ISO_EVENT_BIG_TERMINATE_COMPLETE;
event.big_terminated.big_handle = ev->big_handle;
event.big_terminated.reason = ev->reason;
if (big->cb != NULL) {
big->cb(&event, big->cb_arg);
}
ble_iso_big_free(big);
}
static int
ble_iso_tx_complete(uint16_t conn_handle, const uint8_t *data,
uint16_t data_len)
{
struct os_mbuf *om;
int rc;
om = ble_hs_mbuf_bare_pkt();
if (!om) {
return BLE_HS_ENOMEM;
}
os_mbuf_extend(om, 8);
/* Connection_Handle, PB_Flag, TS_Flag */
put_le16(&om->om_data[0],
BLE_HCI_ISO_HANDLE(conn_handle, BLE_HCI_ISO_PB_COMPLETE, 0));
/* Data_Total_Length = Data length + Packet_Sequence_Number placeholder */
put_le16(&om->om_data[2], data_len + 4);
/* Packet_Sequence_Number placeholder */
put_le16(&om->om_data[4], 0);
/* ISO_SDU_Length */
put_le16(&om->om_data[6], data_len);
rc = os_mbuf_append(om, data, data_len);
if (rc) {
return rc;
}
return ble_transport_to_ll_iso(om);
}
static int
ble_iso_tx_segmented(uint16_t conn_handle, uint16_t max_pdu,
const uint8_t *data, uint16_t data_len)
{
struct os_mbuf *om;
uint16_t data_left = data_len;
uint16_t packet_len;
uint16_t offset = 0;
uint8_t pb;
uint8_t ts;
int rc;
while (data_left) {
packet_len = min(max_pdu - (BLE_HCI_DATA_HDR_SZ + 4), data_left);
if (data_left == data_len) {
pb = BLE_HCI_ISO_PB_FIRST;
ts = 1;
} else if (packet_len == data_left) {
pb = BLE_HCI_ISO_PB_LAST;
ts = 0;
} else {
pb = BLE_HCI_ISO_PB_CONTINUATION;
ts = 0;
}
om = ble_hs_mbuf_bare_pkt();
if (!om) {
return BLE_HS_ENOMEM;
}
os_mbuf_extend(om, ts == 1 ? 10: 8);
/* Connection_Handle, PB_Flag, TS_Flag */
put_le16(&om->om_data[0],
BLE_HCI_ISO_HANDLE(conn_handle, pb, ts));
/* Data_Total_Length = Data length +
* Packet_Sequence_Number placeholder +
* optional timestamp*/
put_le16(&om->om_data[2], packet_len + 4 + 2 * ts);
if (ts) {
/* Timestamp placeholder */
put_le16(&om->om_data[4], 0);
/* Packet_Sequence_Number placeholder */
put_le16(&om->om_data[6], 0);
/* ISO_SDU_Length */
put_le16(&om->om_data[8], packet_len);
}
/* Packet_Sequence_Number placeholder */
put_le16(&om->om_data[4], 0);
/* ISO_SDU_Length */
put_le16(&om->om_data[6], packet_len);
rc = os_mbuf_append(om, data + offset, packet_len);
if (rc) {
return rc;
}
ble_transport_to_ll_iso(om);
offset += packet_len;
data_len -= packet_len;
}
return 0;
}
int
ble_iso_tx(uint16_t conn_handle, const uint8_t *data, uint16_t data_len)
{
struct ble_iso_big *big;
uint16_t max_pdu = 0;
int rc;
int i;
SLIST_FOREACH(big, &ble_iso_bigs, next) {
for (i = 0; i < big->num_bis; i++) {
if (big->conn_handles[i] == conn_handle) {
max_pdu = big->max_pdu;
break;
}
}
if (max_pdu > 0) {
break;
}
}
if (!max_pdu) {
return BLE_HS_ENOENT;
}
if (data_len <= max_pdu) {
rc = ble_iso_tx_complete(conn_handle, data, data_len);
} else {
rc = ble_iso_tx_segmented(conn_handle, max_pdu, data, data_len);
}
return rc;
}
#endif
+38
View File
@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef H_BLE_ISO_PRIV_
#define H_BLE_ISO_PRIV_
#include "nimble/hci_common.h"
#ifdef __cplusplus
extern "C" {
#endif
void
ble_iso_rx_create_big_complete(const struct ble_hci_ev_le_subev_create_big_complete *ev);
void
ble_iso_rx_terminate_big_complete(const struct ble_hci_ev_le_subev_terminate_big_complete *ev);
#ifdef __cplusplus
}
#endif
#endif /* H_BLE_ISO_PRIV_ */
+14
View File
@@ -486,6 +486,20 @@ syscfg.defs:
supported by host.
value: 0
BLE_MAX_BIG:
desciptrion: >
Number of available BIGs
value: 'MYNEWT_VAL_BLE_MULTI_ADV_INSTANCES'
restrictions:
- 'BLE_ISO_BROADCASTER if 0'
BLE_MAX_BIS:
description: >
Number of supported BISes
value: 4
restrictions:
- 'BLE_ISO_BROADCASTER if 0'
### Log settings.
BLE_HS_LOG_MOD: