blob: 88ab2fd7164e026a6483d3ad1e9a91d95cacffc9 [file] [log] [blame]
/*
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(CONFIG_BT_BAP_UNICAST_SERVER)
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/bap.h>
#include <zephyr/bluetooth/audio/pacs.h>
#include "common.h"
#include "bap_unicast_common.h"
extern enum bst_result_t bst_result;
#define CHANNEL_COUNT_1 BIT(0)
#define PREF_CONTEXT \
(BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL | BT_AUDIO_CONTEXT_TYPE_MEDIA)
static struct bt_codec lc3_codec = {
.path_id = BT_ISO_DATA_PATH_HCI,
.id = BT_CODEC_LC3_ID,
.cid = 0x0000U,
.vid = 0x0000U,
.data_count = 5U,
.data = {
BT_CODEC_DATA(BT_CODEC_LC3_FREQ,
(BT_CODEC_LC3_FREQ_16KHZ & 0xFFU),
((BT_CODEC_LC3_FREQ_16KHZ >> 8) & 0xFFU)),
BT_CODEC_DATA(BT_CODEC_LC3_DURATION, BT_CODEC_LC3_DURATION_10),
BT_CODEC_DATA(BT_CODEC_LC3_CHAN_COUNT, CHANNEL_COUNT_1),
BT_CODEC_DATA(BT_CODEC_LC3_FRAME_LEN,
(40U & 0xFFU), ((40U >> 8) & 0xFFU),
(40U & 0xFFU), ((40U >> 8) & 0xFFU)),
BT_CODEC_DATA(BT_CODEC_LC3_FRAME_COUNT, 1U)
},
.meta_count = 2,
.meta = {
BT_CODEC_DATA(BT_AUDIO_METADATA_TYPE_PREF_CONTEXT,
(PREF_CONTEXT & 0xFFU),
((PREF_CONTEXT >> 8) & 0xFFU)),
BT_CODEC_DATA(BT_AUDIO_METADATA_TYPE_VENDOR,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f)
}
};
static struct bt_bap_stream streams[CONFIG_BT_ASCS_ASE_SNK_COUNT + CONFIG_BT_ASCS_ASE_SRC_COUNT];
static const struct bt_codec_qos_pref qos_pref =
BT_CODEC_QOS_PREF(true, BT_GAP_LE_PHY_2M, 0x02, 10, 40000, 40000, 40000, 40000);
/* TODO: Expand with BAP data */
static const struct bt_data unicast_server_ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_ASCS_VAL)),
};
CREATE_FLAG(flag_stream_configured);
static void print_ase_info(struct bt_bap_ep *ep, void *user_data)
{
struct bt_bap_ep_info info;
bt_bap_ep_get_info(ep, &info);
printk("ASE info: id %u state %u dir %u\n", info.id, info.state, info.dir);
}
static struct bt_bap_stream *stream_alloc(void)
{
for (size_t i = 0; i < ARRAY_SIZE(streams); i++) {
struct bt_bap_stream *stream = &streams[i];
if (!stream->conn) {
return stream;
}
}
return NULL;
}
static int lc3_config(struct bt_conn *conn, const struct bt_bap_ep *ep, enum bt_audio_dir dir,
const struct bt_codec *codec, struct bt_bap_stream **stream,
struct bt_codec_qos_pref *const pref, struct bt_bap_ascs_rsp *rsp)
{
printk("ASE Codec Config: conn %p ep %p dir %u\n", conn, ep, dir);
print_codec(codec);
*stream = stream_alloc();
if (*stream == NULL) {
printk("No streams available\n");
*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_NO_MEM, BT_BAP_ASCS_REASON_NONE);
return -ENOMEM;
}
printk("ASE Codec Config stream %p\n", *stream);
bt_bap_unicast_server_foreach_ep(conn, print_ase_info, NULL);
SET_FLAG(flag_stream_configured);
*pref = qos_pref;
return 0;
}
static int lc3_reconfig(struct bt_bap_stream *stream, enum bt_audio_dir dir,
const struct bt_codec *codec, struct bt_codec_qos_pref *const pref,
struct bt_bap_ascs_rsp *rsp)
{
printk("ASE Codec Reconfig: stream %p\n", stream);
print_codec(codec);
*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_CONF_UNSUPPORTED, BT_BAP_ASCS_REASON_NONE);
/* We only support one QoS at the moment, reject changes */
return -ENOEXEC;
}
static int lc3_qos(struct bt_bap_stream *stream, const struct bt_codec_qos *qos,
struct bt_bap_ascs_rsp *rsp)
{
printk("QoS: stream %p qos %p\n", stream, qos);
print_qos(qos);
return 0;
}
static int lc3_enable(struct bt_bap_stream *stream, const struct bt_codec_data *meta,
size_t meta_count, struct bt_bap_ascs_rsp *rsp)
{
printk("Enable: stream %p meta_count %zu\n", stream, meta_count);
return 0;
}
static int lc3_start(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
{
printk("Start: stream %p\n", stream);
return 0;
}
static bool valid_metadata_type(uint8_t type, uint8_t len)
{
switch (type) {
case BT_AUDIO_METADATA_TYPE_PREF_CONTEXT:
case BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT:
if (len != 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_STREAM_LANG:
if (len != 3) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_PARENTAL_RATING:
if (len != 1) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_EXTENDED: /* 1 - 255 octets */
case BT_AUDIO_METADATA_TYPE_VENDOR: /* 1 - 255 octets */
if (len < 1) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_CCID_LIST: /* 2 - 254 octets */
if (len < 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO: /* 0 - 255 octets */
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: /* 0 - 255 octets */
return true;
default:
return false;
}
}
static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_codec_data *meta,
size_t meta_count, struct bt_bap_ascs_rsp *rsp)
{
printk("Metadata: stream %p meta_count %zu\n", stream, meta_count);
for (size_t i = 0; i < meta_count; i++) {
const struct bt_codec_data *data = &meta[i];
if (!valid_metadata_type(data->data.type, data->data.data_len)) {
printk("Invalid metadata type %u or length %u\n", data->data.type,
data->data.data_len);
*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_REJECTED,
data->data.type);
return -EINVAL;
}
}
return 0;
}
static int lc3_disable(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
{
printk("Disable: stream %p\n", stream);
return 0;
}
static int lc3_stop(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
{
printk("Stop: stream %p\n", stream);
return 0;
}
static int lc3_release(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
{
printk("Release: stream %p\n", stream);
return 0;
}
static const struct bt_bap_unicast_server_cb unicast_server_cb = {
.config = lc3_config,
.reconfig = lc3_reconfig,
.qos = lc3_qos,
.enable = lc3_enable,
.start = lc3_start,
.metadata = lc3_metadata,
.disable = lc3_disable,
.stop = lc3_stop,
.release = lc3_release,
};
static void stream_enabled_cb(struct bt_bap_stream *stream)
{
struct bt_bap_ep_info ep_info;
int err;
printk("Enabled: stream %p\n", stream);
err = bt_bap_ep_get_info(stream->ep, &ep_info);
if (err != 0) {
FAIL("Failed to get ep info: %d\n", err);
return;
}
if (ep_info.dir == BT_AUDIO_DIR_SINK) {
/* Automatically do the receiver start ready operation */
err = bt_bap_stream_start(stream);
if (err != 0) {
FAIL("Failed to start stream: %d\n", err);
return;
}
}
}
static void stream_recv(struct bt_bap_stream *stream, const struct bt_iso_recv_info *info,
struct net_buf *buf)
{
printk("Incoming audio on stream %p len %u\n", stream, buf->len);
}
static struct bt_bap_stream_ops stream_ops = {
.enabled = stream_enabled_cb,
.recv = stream_recv
};
static void init(void)
{
static struct bt_pacs_cap cap = {
.codec = &lc3_codec,
};
int err;
err = bt_enable(NULL);
if (err != 0) {
FAIL("Bluetooth enable failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
bt_bap_unicast_server_register_cb(&unicast_server_cb);
err = bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &cap);
if (err != 0) {
FAIL("Failed to register capabilities: %d", err);
return;
}
err = bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, &cap);
if (err != 0) {
FAIL("Failed to register capabilities: %d", err);
return;
}
for (size_t i = 0; i < ARRAY_SIZE(streams); i++) {
bt_bap_stream_cb_register(&streams[i], &stream_ops);
}
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, unicast_server_ad, ARRAY_SIZE(unicast_server_ad),
NULL, 0);
if (err != 0) {
FAIL("Advertising failed to start (err %d)\n", err);
return;
}
}
static void set_location(void)
{
int err;
if (IS_ENABLED(CONFIG_BT_PAC_SNK_LOC)) {
err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, BT_AUDIO_LOCATION_FRONT_CENTER);
if (err != 0) {
FAIL("Failed to set sink location (err %d)\n", err);
return;
}
}
if (IS_ENABLED(CONFIG_BT_PAC_SRC_LOC)) {
err = bt_pacs_set_location(BT_AUDIO_DIR_SOURCE, (BT_AUDIO_LOCATION_FRONT_LEFT |
BT_AUDIO_LOCATION_FRONT_RIGHT));
if (err != 0) {
FAIL("Failed to set source location (err %d)\n", err);
return;
}
}
printk("Location successfully set\n");
}
static void set_available_contexts(void)
{
int err;
err = bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SINK,
BT_AUDIO_CONTEXT_TYPE_MEDIA |
BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL);
if (IS_ENABLED(CONFIG_BT_PAC_SNK) && err != 0) {
FAIL("Failed to set sink supported contexts (err %d)\n", err);
return;
}
err = bt_pacs_set_available_contexts(BT_AUDIO_DIR_SINK,
BT_AUDIO_CONTEXT_TYPE_MEDIA |
BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL);
if (IS_ENABLED(CONFIG_BT_PAC_SNK) && err != 0) {
FAIL("Failed to set sink available contexts (err %d)\n", err);
return;
}
err = bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SOURCE,
BT_AUDIO_CONTEXT_TYPE_NOTIFICATIONS);
if (IS_ENABLED(CONFIG_BT_PAC_SRC) && err != 0) {
FAIL("Failed to set source supported contexts (err %d)\n", err);
return;
}
err = bt_pacs_set_available_contexts(BT_AUDIO_DIR_SOURCE,
BT_AUDIO_CONTEXT_TYPE_NOTIFICATIONS);
if (IS_ENABLED(CONFIG_BT_PAC_SRC) && err != 0) {
FAIL("Failed to set source available contexts (err %d)\n", err);
return;
}
printk("Available contexts successfully set\n");
}
static void test_main(void)
{
init();
set_location();
set_available_contexts();
/* TODO: When babblesim supports ISO, wait for audio stream to pass */
WAIT_FOR_FLAG(flag_connected);
WAIT_FOR_FLAG(flag_stream_configured);
PASS("Unicast server passed\n");
}
static const struct bst_test_instance test_unicast_server[] = {{.test_id = "unicast_server",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main},
BSTEST_END_MARKER};
struct bst_test_list *test_unicast_server_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_unicast_server);
}
#else /* !(CONFIG_BT_BAP_UNICAST_SERVER) */
struct bst_test_list *test_unicast_server_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_BAP_UNICAST_SERVER */