Bluetooth: Audio: Add TMAP broadcast samples
Add two TMAP broadcast sample applications. tmap_bms implements the Broadcast Media Sender role. It uses CAP Initiator broadcast APIs to broadcast an audio stream. tmap_bmr implements the Broadcast Media Receiver role. It scans and syncs to the PA having filtered the EA by the presence of TMAP role information. It also instantiates the VCP. Signed-off-by: Silviu Petria <silviu.petria@nxp.com>
This commit is contained in:
parent
613c32b03f
commit
f3aaf33422
24 changed files with 1215 additions and 0 deletions
13
samples/bluetooth/tmap_bmr/CMakeLists.txt
Normal file
13
samples/bluetooth/tmap_bmr/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(tmap_bmr)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/vcp_vol_renderer.c
|
||||
src/bap_broadcast_sink.c
|
||||
)
|
||||
|
||||
zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth)
|
12
samples/bluetooth/tmap_bmr/Kconfig
Normal file
12
samples/bluetooth/tmap_bmr/Kconfig
Normal file
|
@ -0,0 +1,12 @@
|
|||
#
|
||||
# Copyright (c) 2022 Codecoup
|
||||
# Copyright 2023 NXP
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
mainmenu "Bluetooth: Earbuds"
|
||||
|
||||
menu "Zephyr"
|
||||
source "Kconfig.zephyr"
|
||||
endmenu
|
21
samples/bluetooth/tmap_bmr/README.rst
Normal file
21
samples/bluetooth/tmap_bmr/README.rst
Normal file
|
@ -0,0 +1,21 @@
|
|||
.. _bluetooth_tmap_bmr:
|
||||
|
||||
Bluetooth: TMAP BMR
|
||||
###################
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
Application demonstrating the LE Audio TMAP Broadcast Media Receiver functionality.
|
||||
Implements the BMR role.
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
* A board with Bluetooth Low Energy 5.2 support
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
This sample can be found under :zephyr_file:`samples/bluetooth/tmap_bmr` in the Zephyr tree.
|
||||
|
||||
See :ref:`bluetooth samples section <bluetooth-samples>` for details.
|
10
samples/bluetooth/tmap_bmr/boards/native_posix.conf
Normal file
10
samples/bluetooth/tmap_bmr/boards/native_posix.conf
Normal file
|
@ -0,0 +1,10 @@
|
|||
CONFIG_LOG_MODE_IMMEDIATE=y
|
||||
CONFIG_BT_TINYCRYPT_ECC=y
|
||||
|
||||
CONFIG_LIBLC3=y
|
||||
CONFIG_FPU=y
|
||||
|
||||
# For LE-audio at 10ms intervals we need the tick counter to occur more frequently
|
||||
# than every 10 ms as each PDU for some reason takes 2 ticks to process.
|
||||
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000
|
||||
CONFIG_NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME=y
|
|
@ -0,0 +1,4 @@
|
|||
CONFIG_BT_CTLR_PERIPHERAL_ISO=y
|
||||
|
||||
# Supports the highest SDU size required by any BAP LC3 presets (155)
|
||||
CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE=155
|
|
@ -0,0 +1,8 @@
|
|||
# For LC3 the following configs are needed
|
||||
CONFIG_FPU=y
|
||||
CONFIG_LIBLC3=y
|
||||
# The LC3 codec uses a large amount of stack. This app runs the codec in the work-queue, hence
|
||||
# inctease stack size for that thread.
|
||||
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
|
||||
# LC3 lib requires floating point support in the c-lib NEWLIB is one way of getting that.
|
||||
CONFIG_NEWLIB_LIBC=y
|
|
@ -0,0 +1,8 @@
|
|||
# For LC3 the following configs are needed
|
||||
CONFIG_FPU=y
|
||||
CONFIG_LIBLC3=y
|
||||
# The LC3 codec uses a large amount of stack. This app runs the codec in the work-queue, hence
|
||||
# inctease stack size for that thread.
|
||||
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
|
||||
# LC3 lib requires floating point support in the c-lib NEWLIB is one way of getting that.
|
||||
CONFIG_NEWLIB_LIBC=y
|
37
samples/bluetooth/tmap_bmr/prj.conf
Normal file
37
samples/bluetooth/tmap_bmr/prj.conf
Normal file
|
@ -0,0 +1,37 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_LOG=y
|
||||
CONFIG_BT_PAC_SNK=y
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_PRIVACY=y
|
||||
CONFIG_BT_AUDIO=y
|
||||
CONFIG_UTF8=y
|
||||
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_KEYS_OVERWRITE_OLDEST=y
|
||||
CONFIG_BT_L2CAP_TX_BUF_COUNT=20
|
||||
|
||||
# TMAP support
|
||||
CONFIG_BT_TMAP=y
|
||||
|
||||
# CAP
|
||||
CONFIG_BT_CAP_ACCEPTOR=y
|
||||
|
||||
# BAP support
|
||||
CONFIG_BT_BAP_SCAN_DELEGATOR=y
|
||||
CONFIG_BT_BAP_BROADCAST_SINK=y
|
||||
CONFIG_BT_BAP_BROADCAST_SNK_SUBGROUP_COUNT=1
|
||||
CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT=1
|
||||
|
||||
# VCP support
|
||||
CONFIG_BT_VCP_VOL_REND=y
|
||||
|
||||
# Support an ISO channel per ASE
|
||||
CONFIG_BT_ISO_MAX_CHAN=2
|
||||
|
||||
# Sink PAC Location Support
|
||||
CONFIG_BT_PAC_SNK_LOC=y
|
||||
|
||||
# Generic config
|
||||
CONFIG_BT_GATT_DYNAMIC_DB=y
|
||||
CONFIG_BT_EXT_ADV=y
|
||||
CONFIG_BT_DEVICE_NAME="TMAP BMR"
|
10
samples/bluetooth/tmap_bmr/sample.yaml
Normal file
10
samples/bluetooth/tmap_bmr/sample.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
sample:
|
||||
description: Bluetooth Low Energy Audio TMAP BMR sample
|
||||
name: Bluetooth Low Energy Audio TMAP BMR sample
|
||||
tests:
|
||||
sample.bluetooth.tmap_bmr:
|
||||
harness: bluetooth
|
||||
platform_allow: qemu_cortex_m3 qemu_x86
|
||||
tags: bluetooth
|
||||
integration_platforms:
|
||||
- qemu_cortex_m3
|
413
samples/bluetooth/tmap_bmr/src/bap_broadcast_sink.c
Normal file
413
samples/bluetooth/tmap_bmr/src/bap_broadcast_sink.c
Normal file
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2023 Nordic Semiconductor ASA
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/audio/audio.h>
|
||||
#include <zephyr/bluetooth/audio/bap.h>
|
||||
#include <zephyr/bluetooth/audio/pacs.h>
|
||||
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
|
||||
#include <zephyr/bluetooth/audio/tmap.h>
|
||||
|
||||
#define SEM_TIMEOUT K_SECONDS(10)
|
||||
#define PA_SYNC_SKIP 5
|
||||
#define SYNC_RETRY_COUNT 6 /* similar to retries for connections */
|
||||
#define INVALID_BROADCAST_ID 0xFFFFFFFF
|
||||
|
||||
static bool tmap_bms_found;
|
||||
|
||||
static K_SEM_DEFINE(sem_pa_synced, 0U, 1U);
|
||||
static K_SEM_DEFINE(sem_base_received, 0U, 1U);
|
||||
static K_SEM_DEFINE(sem_sink_created, 0U, 1U);
|
||||
static K_SEM_DEFINE(sem_syncable, 0U, 1U);
|
||||
static K_SEM_DEFINE(sem_pa_sync_lost, 0U, 1U);
|
||||
|
||||
static void broadcast_scan_recv(const struct bt_le_scan_recv_info *info,
|
||||
struct net_buf_simple *ad);
|
||||
static void broadcast_scan_timeout(void);
|
||||
|
||||
static void broadcast_pa_synced(struct bt_le_per_adv_sync *sync,
|
||||
struct bt_le_per_adv_sync_synced_info *info);
|
||||
static void broadcast_pa_recv(struct bt_le_per_adv_sync *sync,
|
||||
const struct bt_le_per_adv_sync_recv_info *info,
|
||||
struct net_buf_simple *buf);
|
||||
|
||||
static struct bt_le_scan_cb broadcast_scan_cb = {
|
||||
.recv = broadcast_scan_recv,
|
||||
.timeout = broadcast_scan_timeout
|
||||
};
|
||||
|
||||
static struct bt_le_per_adv_sync_cb broadcast_sync_cb = {
|
||||
.synced = broadcast_pa_synced,
|
||||
.recv = broadcast_pa_recv,
|
||||
};
|
||||
|
||||
static struct bt_bap_broadcast_sink *broadcast_sink;
|
||||
static uint32_t bcast_id;
|
||||
static struct bt_le_per_adv_sync *bcast_pa_sync;
|
||||
|
||||
static struct bt_bap_stream streams[CONFIG_BT_BAP_BROADCAST_SNK_STREAM_COUNT];
|
||||
struct bt_bap_stream *streams_p[ARRAY_SIZE(streams)];
|
||||
|
||||
static struct bt_audio_codec_cap codec = BT_AUDIO_CODEC_LC3(
|
||||
BT_AUDIO_CODEC_LC3_FREQ_48KHZ,
|
||||
BT_AUDIO_CODEC_LC3_DURATION_10, BT_AUDIO_CODEC_LC3_CHAN_COUNT_SUPPORT(1), 40u, 60u, 1u,
|
||||
(BT_AUDIO_CONTEXT_TYPE_MEDIA));
|
||||
|
||||
/* Create a mask for the maximum BIS we can sync to using the number of streams
|
||||
* we have. We add an additional 1 since the bis indexes start from 1 and not
|
||||
* 0.
|
||||
*/
|
||||
static const uint32_t bis_index_mask = BIT_MASK(ARRAY_SIZE(streams) + 1U);
|
||||
static uint32_t bis_index_bitfield;
|
||||
|
||||
|
||||
static void stream_started_cb(struct bt_bap_stream *stream)
|
||||
{
|
||||
printk("Stream %p started\n", stream);
|
||||
}
|
||||
|
||||
static void stream_stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
|
||||
{
|
||||
printk("Stream %p stopped with reason 0x%02X\n", stream, reason);
|
||||
}
|
||||
|
||||
static void stream_recv_cb(struct bt_bap_stream *stream,
|
||||
const struct bt_iso_recv_info *info,
|
||||
struct net_buf *buf)
|
||||
{
|
||||
static uint32_t recv_cnt;
|
||||
|
||||
recv_cnt++;
|
||||
if ((recv_cnt % 20U) == 0U) {
|
||||
printk("Received %u total ISO packets\n", recv_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
static struct bt_bap_stream_ops stream_ops = {
|
||||
.started = stream_started_cb,
|
||||
.stopped = stream_stopped_cb,
|
||||
.recv = stream_recv_cb
|
||||
};
|
||||
|
||||
static struct bt_pacs_cap cap = {
|
||||
.codec_cap = &codec,
|
||||
};
|
||||
|
||||
static uint16_t interval_to_sync_timeout(uint16_t interval)
|
||||
{
|
||||
uint32_t interval_ms;
|
||||
uint16_t timeout;
|
||||
|
||||
/* Ensure that the following calculation does not overflow silently */
|
||||
__ASSERT(SYNC_RETRY_COUNT < 10, "SYNC_RETRY_COUNT shall be less than 10");
|
||||
|
||||
/* Add retries and convert to unit in 10's of ms */
|
||||
interval_ms = BT_GAP_PER_ADV_INTERVAL_TO_MS(interval);
|
||||
timeout = (interval_ms * SYNC_RETRY_COUNT) / 10;
|
||||
|
||||
/* Enforce restraints */
|
||||
timeout = CLAMP(timeout, BT_GAP_PER_ADV_MIN_TIMEOUT,
|
||||
BT_GAP_PER_ADV_MAX_TIMEOUT);
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static void sync_broadcast_pa(const struct bt_le_scan_recv_info *info,
|
||||
uint32_t broadcast_id)
|
||||
{
|
||||
struct bt_le_per_adv_sync_param param;
|
||||
int err;
|
||||
|
||||
/* Unregister the callbacks to prevent broadcast_scan_recv to be called again */
|
||||
bt_le_scan_cb_unregister(&broadcast_scan_cb);
|
||||
err = bt_le_scan_stop();
|
||||
if (err != 0) {
|
||||
printk("Could not stop scan: %d", err);
|
||||
}
|
||||
|
||||
bt_addr_le_copy(¶m.addr, info->addr);
|
||||
param.options = 0;
|
||||
param.sid = info->sid;
|
||||
param.skip = PA_SYNC_SKIP;
|
||||
param.timeout = interval_to_sync_timeout(info->interval);
|
||||
err = bt_le_per_adv_sync_create(¶m, &bcast_pa_sync);
|
||||
if (err != 0) {
|
||||
printk("Could not sync to PA: %d", err);
|
||||
} else {
|
||||
bcast_id = broadcast_id;
|
||||
}
|
||||
}
|
||||
|
||||
static bool scan_check_and_sync_broadcast(struct bt_data *data, void *user_data)
|
||||
{
|
||||
uint32_t *broadcast_id = user_data;
|
||||
struct bt_uuid_16 adv_uuid;
|
||||
|
||||
if (data->type != BT_DATA_SVC_DATA16) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!bt_uuid_create(&adv_uuid.uuid, data->data, BT_UUID_SIZE_16)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!bt_uuid_cmp(&adv_uuid.uuid, BT_UUID_BROADCAST_AUDIO)) {
|
||||
*broadcast_id = sys_get_le24(data->data + BT_UUID_SIZE_16);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!bt_uuid_cmp(&adv_uuid.uuid, BT_UUID_TMAS)) {
|
||||
struct net_buf_simple tmas_svc_data;
|
||||
uint16_t uuid_val;
|
||||
uint16_t peer_tmap_role = 0;
|
||||
|
||||
net_buf_simple_init_with_data(&tmas_svc_data,
|
||||
(void *)data->data,
|
||||
data->data_len);
|
||||
uuid_val = net_buf_simple_pull_le16(&tmas_svc_data);
|
||||
if (tmas_svc_data.len < sizeof(peer_tmap_role)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
peer_tmap_role = net_buf_simple_pull_le16(&tmas_svc_data);
|
||||
if ((peer_tmap_role & BT_TMAP_ROLE_BMS)) {
|
||||
printk("Found TMAP BMS\n");
|
||||
tmap_bms_found = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void broadcast_scan_recv(const struct bt_le_scan_recv_info *info,
|
||||
struct net_buf_simple *ad)
|
||||
{
|
||||
uint32_t broadcast_id;
|
||||
|
||||
tmap_bms_found = false;
|
||||
|
||||
/* We are only interested in non-connectable periodic advertisers */
|
||||
if ((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) ||
|
||||
info->interval == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
broadcast_id = INVALID_BROADCAST_ID;
|
||||
bt_data_parse(ad, scan_check_and_sync_broadcast, (void *)&broadcast_id);
|
||||
|
||||
if ((broadcast_id != INVALID_BROADCAST_ID) && tmap_bms_found) {
|
||||
sync_broadcast_pa(info, broadcast_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void broadcast_scan_timeout(void)
|
||||
{
|
||||
printk("Broadcast scan timed out\n");
|
||||
}
|
||||
|
||||
static bool pa_decode_base(struct bt_data *data, void *user_data)
|
||||
{
|
||||
uint32_t base_bis_index_bitfield = 0U;
|
||||
struct bt_bap_base base = { 0 };
|
||||
|
||||
if (data->type != BT_DATA_SVC_DATA16) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (data->data_len < BT_BAP_BASE_MIN_SIZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bt_bap_decode_base(data, &base) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0U; i < base.subgroup_count; i++) {
|
||||
for (size_t j = 0U; j < base.subgroups[i].bis_count; j++) {
|
||||
const uint8_t index = base.subgroups[i].bis_data[j].index;
|
||||
|
||||
base_bis_index_bitfield |= BIT(index);
|
||||
}
|
||||
}
|
||||
|
||||
bis_index_bitfield = base_bis_index_bitfield & bis_index_mask;
|
||||
k_sem_give(&sem_base_received);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void broadcast_pa_recv(struct bt_le_per_adv_sync *sync,
|
||||
const struct bt_le_per_adv_sync_recv_info *info,
|
||||
struct net_buf_simple *buf)
|
||||
{
|
||||
bt_data_parse(buf, pa_decode_base, NULL);
|
||||
}
|
||||
|
||||
static void broadcast_pa_synced(struct bt_le_per_adv_sync *sync,
|
||||
struct bt_le_per_adv_sync_synced_info *info)
|
||||
{
|
||||
k_sem_give(&sem_pa_synced);
|
||||
}
|
||||
|
||||
static void pa_synced_cb(struct bt_bap_broadcast_sink *sink,
|
||||
struct bt_le_per_adv_sync *sync,
|
||||
uint32_t broadcast_id)
|
||||
{
|
||||
if (broadcast_sink != NULL) {
|
||||
printk("Unexpected PA sync\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printk("PA synced for broadcast sink %p with broadcast ID 0x%06X\n",
|
||||
sink, broadcast_id);
|
||||
|
||||
broadcast_sink = sink;
|
||||
|
||||
k_sem_give(&sem_sink_created);
|
||||
}
|
||||
|
||||
static void syncable_cb(struct bt_bap_broadcast_sink *sink, bool encrypted)
|
||||
{
|
||||
k_sem_give(&sem_syncable);
|
||||
}
|
||||
|
||||
static void base_recv_cb(struct bt_bap_broadcast_sink *sink, const struct bt_bap_base *base)
|
||||
{
|
||||
k_sem_give(&sem_base_received);
|
||||
}
|
||||
|
||||
static void pa_sync_lost_cb(struct bt_bap_broadcast_sink *sink)
|
||||
{
|
||||
if (broadcast_sink == NULL) {
|
||||
printk("Unexpected PA sync lost\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Sink %p disconnected\n", sink);
|
||||
broadcast_sink = NULL;
|
||||
k_sem_give(&sem_pa_sync_lost);
|
||||
}
|
||||
|
||||
static struct bt_bap_broadcast_sink_cb broadcast_sink_cbs = {
|
||||
.pa_synced = pa_synced_cb,
|
||||
.syncable = syncable_cb,
|
||||
.base_recv = base_recv_cb,
|
||||
.pa_sync_lost = pa_sync_lost_cb
|
||||
};
|
||||
|
||||
static int reset(void)
|
||||
{
|
||||
if (broadcast_sink != NULL) {
|
||||
int err = bt_bap_broadcast_sink_delete(broadcast_sink);
|
||||
|
||||
if (err) {
|
||||
printk("Deleting broadcast sink failed (err %d)\n", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
broadcast_sink = NULL;
|
||||
}
|
||||
|
||||
k_sem_reset(&sem_pa_synced);
|
||||
k_sem_reset(&sem_base_received);
|
||||
k_sem_reset(&sem_sink_created);
|
||||
k_sem_reset(&sem_syncable);
|
||||
k_sem_reset(&sem_pa_sync_lost);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bap_broadcast_sink_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
bt_bap_broadcast_sink_register_cb(&broadcast_sink_cbs);
|
||||
bt_le_per_adv_sync_cb_register(&broadcast_sync_cb);
|
||||
|
||||
err = bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &cap);
|
||||
if (err) {
|
||||
printk("Capability register failed (err %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams); i++) {
|
||||
streams[i].ops = &stream_ops;
|
||||
}
|
||||
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(streams_p); i++) {
|
||||
streams_p[i] = &streams[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bap_broadcast_sink_run(void)
|
||||
{
|
||||
while (true) {
|
||||
int err = reset();
|
||||
|
||||
if (err != 0) {
|
||||
printk("Resetting failed: %d - Aborting\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
bt_le_scan_cb_register(&broadcast_scan_cb);
|
||||
/* Start scanning */
|
||||
err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
|
||||
if (err) {
|
||||
printk("Scan start failed (err %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Wait for PA sync */
|
||||
err = k_sem_take(&sem_pa_synced, SEM_TIMEOUT);
|
||||
if (err != 0) {
|
||||
printk("sem_pa_synced timed out\n");
|
||||
return err;
|
||||
}
|
||||
printk("Broadcast source PA synced, waiting for BASE\n");
|
||||
|
||||
/* Wait for BASE decode */
|
||||
err = k_sem_take(&sem_base_received, SEM_TIMEOUT);
|
||||
if (err != 0) {
|
||||
printk("sem_base_received timed out\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Create broadcast sink */
|
||||
printk("BASE received, creating broadcast sink\n");
|
||||
err = bt_bap_broadcast_sink_create(bcast_pa_sync, bcast_id);
|
||||
err = k_sem_take(&sem_sink_created, SEM_TIMEOUT);
|
||||
if (err != 0) {
|
||||
printk("sem_sink_created timed out\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
k_sem_take(&sem_syncable, SEM_TIMEOUT);
|
||||
if (err != 0) {
|
||||
printk("sem_syncable timed out\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Sync to broadcast source */
|
||||
printk("Syncing to broadcast\n");
|
||||
err = bt_bap_broadcast_sink_sync(broadcast_sink, bis_index_bitfield,
|
||||
streams_p, NULL);
|
||||
if (err != 0) {
|
||||
printk("Unable to sync to broadcast source: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
k_sem_take(&sem_pa_sync_lost, K_FOREVER);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
54
samples/bluetooth/tmap_bmr/src/main.c
Normal file
54
samples/bluetooth/tmap_bmr/src/main.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/audio/tmap.h>
|
||||
|
||||
#include "tmap_bmr.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err != 0) {
|
||||
printk("Bluetooth init failed (err %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
printk("Bluetooth initialized\n");
|
||||
|
||||
printk("Initializing TMAP and setting role\n");
|
||||
err = bt_tmap_register(BT_TMAP_ROLE_BMR);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = vcp_vol_renderer_init();
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
printk("VCP initialized\n");
|
||||
|
||||
printk("Initializing BAP Broadcast Sink\n");
|
||||
err = bap_broadcast_sink_init();
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
printk("Starting BAP Broadcast Sink\n");
|
||||
err = bap_broadcast_sink_run();
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
28
samples/bluetooth/tmap_bmr/src/tmap_bmr.h
Normal file
28
samples/bluetooth/tmap_bmr/src/tmap_bmr.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/types.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize the VCP Volume Renderer role
|
||||
*
|
||||
* @return 0 if success, errno on failure.
|
||||
*/
|
||||
int vcp_vol_renderer_init(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize BAP Broadcast Sink
|
||||
*
|
||||
* @return 0 if success, errno on failure.
|
||||
*/
|
||||
int bap_broadcast_sink_init(void);
|
||||
|
||||
/**
|
||||
* @brief Run BAP Broadcast Sink
|
||||
*
|
||||
* @return 0 if success, errno on failure.
|
||||
*/
|
||||
int bap_broadcast_sink_run(void);
|
68
samples/bluetooth/tmap_bmr/src/vcp_vol_renderer.c
Normal file
68
samples/bluetooth/tmap_bmr/src/vcp_vol_renderer.c
Normal file
|
@ -0,0 +1,68 @@
|
|||
/** @file
|
||||
* @brief Bluetooth Volume Control Profile (VCP) Volume Renderer role.
|
||||
*
|
||||
* Copyright (c) 2020 Bose Corporation
|
||||
* Copyright (c) 2020-2022 Nordic Semiconductor ASA
|
||||
* Copyright (c) 2022 Codecoup
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/audio/vcp.h>
|
||||
|
||||
static struct bt_vcp_included vcp_included;
|
||||
|
||||
static void vcs_state_cb(int err, uint8_t volume, uint8_t mute)
|
||||
{
|
||||
if (err) {
|
||||
printk("VCS state get failed (%d)\n", err);
|
||||
} else {
|
||||
printk("VCS volume %u, mute %u\n", volume, mute);
|
||||
}
|
||||
}
|
||||
|
||||
static void vcs_flags_cb(int err, uint8_t flags)
|
||||
{
|
||||
if (err) {
|
||||
printk("VCS flags get failed (%d)\n", err);
|
||||
} else {
|
||||
printk("VCS flags 0x%02X\n", flags);
|
||||
}
|
||||
}
|
||||
|
||||
static struct bt_vcp_vol_rend_cb vcp_cbs = {
|
||||
.state = vcs_state_cb,
|
||||
.flags = vcs_flags_cb,
|
||||
};
|
||||
|
||||
int vcp_vol_renderer_init(void)
|
||||
{
|
||||
int err;
|
||||
struct bt_vcp_vol_rend_register_param vcp_register_param;
|
||||
|
||||
memset(&vcp_register_param, 0, sizeof(vcp_register_param));
|
||||
|
||||
vcp_register_param.step = 1;
|
||||
vcp_register_param.mute = BT_VCP_STATE_UNMUTED;
|
||||
vcp_register_param.volume = 100;
|
||||
vcp_register_param.cb = &vcp_cbs;
|
||||
|
||||
err = bt_vcp_vol_rend_register(&vcp_register_param);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = bt_vcp_vol_rend_included_get(&vcp_included);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
12
samples/bluetooth/tmap_bms/CMakeLists.txt
Normal file
12
samples/bluetooth/tmap_bms/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(tmap_bms)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/cap_initiator.c
|
||||
)
|
||||
|
||||
zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth)
|
22
samples/bluetooth/tmap_bms/README.rst
Normal file
22
samples/bluetooth/tmap_bms/README.rst
Normal file
|
@ -0,0 +1,22 @@
|
|||
.. _bluetooth_tmap_bms:
|
||||
|
||||
Bluetooth: TMAP BMS
|
||||
###################
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
Application demonstrating the LE Audio TMAP Broadcast Media Sender functionality.
|
||||
Implements the BMS role.
|
||||
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
* A board with Bluetooth Low Energy 5.2 support
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
This sample can be found under :zephyr_file:`samples/bluetooth/tmap_bms` in the Zephyr tree.
|
||||
|
||||
See :ref:`bluetooth samples section <bluetooth-samples>` for details.
|
10
samples/bluetooth/tmap_bms/boards/native_posix.conf
Normal file
10
samples/bluetooth/tmap_bms/boards/native_posix.conf
Normal file
|
@ -0,0 +1,10 @@
|
|||
CONFIG_LOG_MODE_IMMEDIATE=y
|
||||
CONFIG_BT_TINYCRYPT_ECC=y
|
||||
|
||||
CONFIG_LIBLC3=y
|
||||
CONFIG_FPU=y
|
||||
|
||||
# For LE-audio at 10ms intervals we need the tick counter to occur more frequently
|
||||
# than every 10 ms as each PDU for some reason takes 2 ticks to process.
|
||||
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000
|
||||
CONFIG_NATIVE_POSIX_SLOWDOWN_TO_REAL_TIME=y
|
|
@ -0,0 +1,4 @@
|
|||
CONFIG_BT_CTLR_CENTRAL_ISO=y
|
||||
|
||||
# Supports the highest SDU size required by any BAP LC3 presets (155)
|
||||
CONFIG_BT_CTLR_ISO_TX_BUFFER_SIZE=155
|
|
@ -0,0 +1,8 @@
|
|||
# For LC3 the following configs are needed
|
||||
CONFIG_FPU=y
|
||||
CONFIG_LIBLC3=y
|
||||
# The LC3 codec uses a large amount of stack. This app runs the codec in the work-queue, hence
|
||||
# inctease stack size for that thread.
|
||||
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
|
||||
# LC3 lib requires floating point support in the c-lib NEWLIB is one way of getting that.
|
||||
CONFIG_NEWLIB_LIBC=y
|
|
@ -0,0 +1,8 @@
|
|||
# For LC3 the following configs are needed
|
||||
CONFIG_FPU=y
|
||||
CONFIG_LIBLC3=y
|
||||
# The LC3 codec uses a large amount of stack. This app runs the codec in the work-queue, hence
|
||||
# inctease stack size for that thread.
|
||||
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
|
||||
# LC3 lib requires floating point support in the c-lib NEWLIB is one way of getting that.
|
||||
CONFIG_NEWLIB_LIBC=y
|
26
samples/bluetooth/tmap_bms/prj.conf
Normal file
26
samples/bluetooth/tmap_bms/prj.conf
Normal file
|
@ -0,0 +1,26 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_LOG=y
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_AUDIO=y
|
||||
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_KEYS_OVERWRITE_OLDEST=y
|
||||
CONFIG_BT_L2CAP_TX_BUF_COUNT=20
|
||||
|
||||
# TMAP support
|
||||
CONFIG_BT_TMAP=y
|
||||
|
||||
# CAP support
|
||||
CONFIG_BT_CAP_INITIATOR=y
|
||||
|
||||
# BAP support
|
||||
CONFIG_BT_BAP_BROADCAST_SOURCE=y
|
||||
CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT=1
|
||||
CONFIG_BT_BAP_BROADCAST_SRC_SUBGROUP_COUNT=1
|
||||
|
||||
CONFIG_BT_ISO_TX_BUF_COUNT=2
|
||||
CONFIG_BT_ISO_MAX_CHAN=2
|
||||
CONFIG_BT_GATT_DYNAMIC_DB=y
|
||||
|
||||
CONFIG_BT_EXT_ADV=y
|
||||
CONFIG_BT_DEVICE_NAME="TMAP BMS"
|
10
samples/bluetooth/tmap_bms/sample.yaml
Normal file
10
samples/bluetooth/tmap_bms/sample.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
sample:
|
||||
description: Bluetooth Low Energy Audio TMAP BMS sample
|
||||
name: Bluetooth Low Energy Audio TMAP BMS sample
|
||||
tests:
|
||||
sample.bluetooth.tmap_bms:
|
||||
harness: bluetooth
|
||||
platform_allow: qemu_cortex_m3 qemu_x86
|
||||
tags: bluetooth
|
||||
integration_platforms:
|
||||
- qemu_cortex_m3
|
346
samples/bluetooth/tmap_bms/src/cap_initiator.c
Normal file
346
samples/bluetooth/tmap_bms/src/cap_initiator.c
Normal file
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2023 Nordic Semiconductor ASA
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/audio/audio.h>
|
||||
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
|
||||
#include <zephyr/bluetooth/audio/cap.h>
|
||||
#include <zephyr/bluetooth/audio/bap.h>
|
||||
#include <zephyr/bluetooth/audio/tmap.h>
|
||||
|
||||
#define BROADCAST_ENQUEUE_COUNT 2U
|
||||
#define MOCK_CCID 0x1234
|
||||
NET_BUF_POOL_FIXED_DEFINE(tx_pool,
|
||||
(BROADCAST_ENQUEUE_COUNT * CONFIG_BT_BAP_BROADCAST_SRC_STREAM_COUNT),
|
||||
BT_ISO_SDU_BUF_SIZE(CONFIG_BT_ISO_TX_MTU), 8, NULL);
|
||||
|
||||
static K_SEM_DEFINE(sem_broadcast_started, 0, 1);
|
||||
static K_SEM_DEFINE(sem_broadcast_stopped, 0, 1);
|
||||
|
||||
static struct bt_cap_stream broadcast_source_stream;
|
||||
static struct bt_cap_stream *broadcast_stream;
|
||||
|
||||
struct bt_audio_codec_data bis_codec_data = BT_AUDIO_CODEC_DATA(
|
||||
BT_AUDIO_CODEC_CONFIG_LC3_FREQ, BT_AUDIO_CODEC_CONFIG_LC3_FREQ_48KHZ);
|
||||
|
||||
const struct bt_audio_codec_data new_metadata[] = {
|
||||
BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT,
|
||||
(BT_AUDIO_CONTEXT_TYPE_MEDIA & 0xFFU),
|
||||
((BT_AUDIO_CONTEXT_TYPE_MEDIA >> 8) & 0xFFU)),
|
||||
BT_AUDIO_CODEC_DATA(BT_AUDIO_METADATA_TYPE_CCID_LIST,
|
||||
(MOCK_CCID & 0xFFU),
|
||||
((MOCK_CCID >> 8) & 0xFFU))
|
||||
};
|
||||
|
||||
static struct bt_bap_lc3_preset broadcast_preset_48_2_1 =
|
||||
BT_BAP_LC3_UNICAST_PRESET_48_2_1(BT_AUDIO_LOCATION_FRONT_LEFT,
|
||||
BT_AUDIO_CONTEXT_TYPE_MEDIA);
|
||||
|
||||
struct bt_cap_initiator_broadcast_stream_param stream_params;
|
||||
struct bt_cap_initiator_broadcast_subgroup_param subgroup_param;
|
||||
struct bt_cap_initiator_broadcast_create_param create_param;
|
||||
struct bt_cap_broadcast_source *broadcast_source;
|
||||
struct bt_le_ext_adv *adv;
|
||||
|
||||
static uint8_t tmap_addata[] = {
|
||||
BT_UUID_16_ENCODE(BT_UUID_TMAS_VAL), /* TMAS UUID */
|
||||
BT_BYTES_LIST_LE16(BT_TMAP_ROLE_BMS), /* TMAP Role */
|
||||
};
|
||||
|
||||
static void broadcast_started_cb(struct bt_bap_stream *stream)
|
||||
{
|
||||
printk("Stream %p started\n", stream);
|
||||
k_sem_give(&sem_broadcast_started);
|
||||
}
|
||||
|
||||
static void broadcast_stopped_cb(struct bt_bap_stream *stream, uint8_t reason)
|
||||
{
|
||||
printk("Stream %p stopped with reason 0x%02X\n", stream, reason);
|
||||
k_sem_give(&sem_broadcast_stopped);
|
||||
}
|
||||
|
||||
static void broadcast_sent_cb(struct bt_bap_stream *stream)
|
||||
{
|
||||
static uint8_t mock_data[CONFIG_BT_ISO_TX_MTU];
|
||||
static bool mock_data_initialized;
|
||||
static uint32_t seq_num;
|
||||
struct net_buf *buf;
|
||||
int ret;
|
||||
|
||||
if (broadcast_preset_48_2_1.qos.sdu > CONFIG_BT_ISO_TX_MTU) {
|
||||
printk("Invalid SDU %u for the MTU: %d",
|
||||
broadcast_preset_48_2_1.qos.sdu, CONFIG_BT_ISO_TX_MTU);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mock_data_initialized) {
|
||||
for (size_t i = 0U; i < ARRAY_SIZE(mock_data); i++) {
|
||||
/* Initialize mock data */
|
||||
mock_data[i] = (uint8_t)i;
|
||||
}
|
||||
mock_data_initialized = true;
|
||||
}
|
||||
|
||||
buf = net_buf_alloc(&tx_pool, K_FOREVER);
|
||||
if (buf == NULL) {
|
||||
printk("Could not allocate buffer when sending on %p\n", stream);
|
||||
return;
|
||||
}
|
||||
|
||||
net_buf_reserve(buf, BT_ISO_CHAN_SEND_RESERVE);
|
||||
net_buf_add_mem(buf, mock_data, broadcast_preset_48_2_1.qos.sdu);
|
||||
ret = bt_bap_stream_send(stream, buf, seq_num++, BT_ISO_TIMESTAMP_NONE);
|
||||
if (ret < 0) {
|
||||
/* This will end broadcasting on this stream. */
|
||||
net_buf_unref(buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static struct bt_bap_stream_ops broadcast_stream_ops = {
|
||||
.started = broadcast_started_cb,
|
||||
.stopped = broadcast_stopped_cb,
|
||||
.sent = broadcast_sent_cb
|
||||
};
|
||||
|
||||
static int setup_extended_adv(struct bt_le_ext_adv **adv)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Create a non-connectable non-scannable advertising set */
|
||||
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN_NAME, NULL, adv);
|
||||
if (err != 0) {
|
||||
printk("Unable to create extended advertising set: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Set periodic advertising parameters */
|
||||
err = bt_le_per_adv_set_param(*adv, BT_LE_PER_ADV_DEFAULT);
|
||||
if (err) {
|
||||
printk("Failed to set periodic advertising parameters: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_extended_adv_data(struct bt_cap_broadcast_source *source,
|
||||
struct bt_le_ext_adv *adv)
|
||||
{
|
||||
/* Broadcast Audio Streaming Endpoint advertising data */
|
||||
NET_BUF_SIMPLE_DEFINE(ad_buf,
|
||||
BT_UUID_SIZE_16 + BT_AUDIO_BROADCAST_ID_SIZE);
|
||||
NET_BUF_SIMPLE_DEFINE(base_buf, 128);
|
||||
struct bt_data ext_ad[2];
|
||||
struct bt_data per_ad;
|
||||
uint32_t broadcast_id;
|
||||
int err;
|
||||
|
||||
err = bt_cap_initiator_broadcast_get_id(source, &broadcast_id);
|
||||
if (err != 0) {
|
||||
printk("Unable to get broadcast ID: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Setup extended advertising data */
|
||||
ext_ad[0].type = BT_DATA_SVC_DATA16;
|
||||
ext_ad[0].data_len = ARRAY_SIZE(tmap_addata);
|
||||
ext_ad[0].data = tmap_addata;
|
||||
net_buf_simple_add_le16(&ad_buf, BT_UUID_BROADCAST_AUDIO_VAL);
|
||||
net_buf_simple_add_le24(&ad_buf, broadcast_id);
|
||||
ext_ad[1].type = BT_DATA_SVC_DATA16;
|
||||
ext_ad[1].data_len = ad_buf.len + sizeof(ext_ad[1].type);
|
||||
ext_ad[1].data = ad_buf.data;
|
||||
err = bt_le_ext_adv_set_data(adv, ext_ad, ARRAY_SIZE(ext_ad), NULL, 0);
|
||||
if (err != 0) {
|
||||
printk("Failed to set extended advertising data: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Setup periodic advertising data */
|
||||
err = bt_cap_initiator_broadcast_get_base(source, &base_buf);
|
||||
if (err != 0) {
|
||||
printk("Failed to get encoded BASE: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
per_ad.type = BT_DATA_SVC_DATA16;
|
||||
per_ad.data_len = base_buf.len;
|
||||
per_ad.data = base_buf.data;
|
||||
err = bt_le_per_adv_set_data(adv, &per_ad, 1);
|
||||
if (err != 0) {
|
||||
printk("Failed to set periodic advertising data: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_extended_adv(struct bt_le_ext_adv *adv)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Start extended advertising */
|
||||
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
|
||||
if (err) {
|
||||
printk("Failed to start extended advertising: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Enable Periodic Advertising */
|
||||
err = bt_le_per_adv_start(adv);
|
||||
if (err) {
|
||||
printk("Failed to enable periodic advertising: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stop_and_delete_extended_adv(struct bt_le_ext_adv *adv)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Stop extended advertising */
|
||||
err = bt_le_per_adv_stop(adv);
|
||||
if (err) {
|
||||
printk("Failed to stop periodic advertising: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = bt_le_ext_adv_stop(adv);
|
||||
if (err) {
|
||||
printk("Failed to stop extended advertising: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = bt_le_ext_adv_delete(adv);
|
||||
if (err) {
|
||||
printk("Failed to delete extended advertising: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reset(void)
|
||||
{
|
||||
k_sem_reset(&sem_broadcast_started);
|
||||
k_sem_reset(&sem_broadcast_stopped);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cap_initiator_init(void)
|
||||
{
|
||||
broadcast_stream = &broadcast_source_stream;
|
||||
bt_bap_stream_cb_register(&broadcast_stream->bap_stream, &broadcast_stream_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cap_initiator_setup(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
stream_params.stream = &broadcast_source_stream;
|
||||
stream_params.data_count = 1U;
|
||||
stream_params.data = &bis_codec_data;
|
||||
|
||||
subgroup_param.stream_count = 1U;
|
||||
subgroup_param.stream_params = &stream_params;
|
||||
subgroup_param.codec_cfg = &broadcast_preset_48_2_1.codec_cfg;
|
||||
|
||||
create_param.subgroup_count = 1U;
|
||||
create_param.subgroup_params = &subgroup_param;
|
||||
create_param.qos = &broadcast_preset_48_2_1.qos;
|
||||
create_param.packing = BT_ISO_PACKING_SEQUENTIAL;
|
||||
create_param.encryption = false;
|
||||
|
||||
while (true) {
|
||||
err = reset();
|
||||
if (err != 0) {
|
||||
printk("Resetting failed: %d - Aborting\n", err);
|
||||
return;
|
||||
}
|
||||
printk("Creating broadcast source\n");
|
||||
|
||||
err = setup_extended_adv(&adv);
|
||||
if (err != 0) {
|
||||
printk("Unable to setup extended advertiser: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_broadcast_audio_create(&create_param, &broadcast_source);
|
||||
if (err != 0) {
|
||||
printk("Unable to create broadcast source: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
err = bt_cap_initiator_broadcast_audio_start(broadcast_source, adv);
|
||||
if (err != 0) {
|
||||
printk("Unable to start broadcast source: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
err = setup_extended_adv_data(broadcast_source, adv);
|
||||
if (err != 0) {
|
||||
printk("Unable to setup extended advertising data: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
err = start_extended_adv(adv);
|
||||
if (err != 0) {
|
||||
printk("Unable to start extended advertiser: %d\n", err);
|
||||
return;
|
||||
}
|
||||
k_sem_take(&sem_broadcast_started, K_FOREVER);
|
||||
|
||||
/* Initialize sending */
|
||||
for (unsigned int j = 0U; j < BROADCAST_ENQUEUE_COUNT; j++) {
|
||||
broadcast_sent_cb(&broadcast_stream->bap_stream);
|
||||
}
|
||||
|
||||
/* Run for a little while */
|
||||
k_sleep(K_SECONDS(10));
|
||||
|
||||
err = bt_cap_initiator_broadcast_audio_update(broadcast_source,
|
||||
new_metadata,
|
||||
ARRAY_SIZE(new_metadata));
|
||||
if (err != 0) {
|
||||
printk("Failed to update broadcast source metadata: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Run for a little while */
|
||||
k_sleep(K_SECONDS(10));
|
||||
|
||||
err = bt_cap_initiator_broadcast_audio_stop(broadcast_source);
|
||||
if (err != 0) {
|
||||
printk("Failed to stop broadcast source: %d\n", err);
|
||||
return;
|
||||
}
|
||||
k_sem_take(&sem_broadcast_stopped, K_FOREVER);
|
||||
|
||||
err = bt_cap_initiator_broadcast_audio_delete(broadcast_source);
|
||||
if (err != 0) {
|
||||
printk("Failed to stop broadcast source: %d\n", err);
|
||||
return;
|
||||
}
|
||||
broadcast_source = NULL;
|
||||
|
||||
err = stop_and_delete_extended_adv(adv);
|
||||
if (err != 0) {
|
||||
printk("Failed to stop and delete extended advertising: %d\n", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
63
samples/bluetooth/tmap_bms/src/main.c
Normal file
63
samples/bluetooth/tmap_bms/src/main.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/audio/audio.h>
|
||||
#include <zephyr/bluetooth/audio/tmap.h>
|
||||
|
||||
#include "tmap_bms.h"
|
||||
|
||||
static int init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err != 0) {
|
||||
printk("Bluetooth enable failed (err %d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
printk("Bluetooth initialized\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = init();
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
printk("Initializing TMAP and setting role\n");
|
||||
/* Initialize TMAP */
|
||||
err = bt_tmap_register(BT_TMAP_ROLE_BMS);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Initialize CAP Initiator */
|
||||
err = cap_initiator_init();
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
printk("CAP initialized\n");
|
||||
|
||||
/* Configure and start broadcast stream */
|
||||
err = cap_initiator_setup();
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
20
samples/bluetooth/tmap_bms/src/tmap_bms.h
Normal file
20
samples/bluetooth/tmap_bms/src/tmap_bms.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/types.h>
|
||||
/**
|
||||
* @brief Initialize the CAP Initiator role
|
||||
*
|
||||
* @return 0 if success, errno on failure.
|
||||
*/
|
||||
int cap_initiator_init(void);
|
||||
|
||||
/**
|
||||
* @brief Setup streams for CAP Initiator
|
||||
*
|
||||
* @return 0 if success, errno on failure.
|
||||
*/
|
||||
int cap_initiator_setup(void);
|
Loading…
Add table
Add a link
Reference in a new issue