blob: 4a1a2e837a2f529af3ee5d76b988445dba85d918 [file] [log] [blame]
/*
* Copyright (c) 2022 Codecoup
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/printk.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/audio/audio.h>
#include <zephyr/bluetooth/audio/pacs.h>
#include <zephyr/bluetooth/audio/csip.h>
#include <zephyr/bluetooth/services/ias.h>
#include "hap_ha.h"
#define MANDATORY_SINK_CONTEXT (BT_AUDIO_CONTEXT_TYPE_UNSPECIFIED | \
BT_AUDIO_CONTEXT_TYPE_CONVERSATIONAL | \
BT_AUDIO_CONTEXT_TYPE_MEDIA | \
BT_AUDIO_CONTEXT_TYPE_LIVE)
#define AVAILABLE_SINK_CONTEXT CONFIG_BT_PACS_SNK_CONTEXT
#define AVAILABLE_SOURCE_CONTEXT CONFIG_BT_PACS_SRC_CONTEXT
BUILD_ASSERT((CONFIG_BT_PACS_SNK_CONTEXT & MANDATORY_SINK_CONTEXT) == MANDATORY_SINK_CONTEXT,
"Need to support mandatory Supported_Sink_Contexts");
static uint8_t unicast_server_addata[] = {
BT_UUID_16_ENCODE(BT_UUID_ASCS_VAL), /* ASCS UUID */
BT_AUDIO_UNICAST_ANNOUNCEMENT_TARGETED, /* Target Announcement */
(((AVAILABLE_SINK_CONTEXT) >> 0) & 0xFF),
(((AVAILABLE_SINK_CONTEXT) >> 8) & 0xFF),
(((AVAILABLE_SOURCE_CONTEXT) >> 0) & 0xFF),
(((AVAILABLE_SOURCE_CONTEXT) >> 8) & 0xFF),
0x00, /* Metadata length */
};
static uint8_t csis_rsi_addata[BT_CSIP_RSI_SIZE];
/* TODO: Expand with BAP data */
static const struct bt_data 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)),
#if defined(CONFIG_BT_CSIP)
BT_DATA(BT_DATA_CSIS_RSI, csis_rsi_addata, ARRAY_SIZE(csis_rsi_addata)),
#endif /* CONFIG_BT_CSIP */
BT_DATA(BT_DATA_SVC_DATA16, unicast_server_addata, ARRAY_SIZE(unicast_server_addata)),
};
static struct k_work_delayable adv_work;
static struct bt_le_ext_adv *adv;
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
/* Restart advertising after disconnection */
k_work_schedule(&adv_work, K_SECONDS(1));
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.disconnected = disconnected,
};
#if defined(CONFIG_BT_PRIVACY) && defined(CONFIG_BT_CSIP)
static bool adv_rpa_expired_cb(struct bt_le_ext_adv *adv)
{
char rsi_str[13];
int err;
err = csip_generate_rsi(csis_rsi_addata);
if (err != 0) {
printk("Failed to generate RSI (err %d)\n", err);
return false;
}
snprintk(rsi_str, ARRAY_SIZE(rsi_str), "%02x%02x%02x%02x%02x%02x",
csis_rsi_addata[0], csis_rsi_addata[1], csis_rsi_addata[2],
csis_rsi_addata[3], csis_rsi_addata[4], csis_rsi_addata[5]);
printk("PRSI: 0x%s\n", rsi_str);
err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
printk("Failed to set advertising data (err %d)\n", err);
return false;
}
return true;
}
#endif /* CONFIG_BT_PRIVACY && CONFIG_BT_CSIP */
static const struct bt_le_ext_adv_cb adv_cb = {
#if defined(CONFIG_BT_PRIVACY) && defined(CONFIG_BT_CSIP)
.rpa_expired = adv_rpa_expired_cb,
#endif /* CONFIG_BT_PRIVACY && CONFIG_BT_CSIP */
};
static void adv_work_handler(struct k_work *work)
{
int err;
if (adv == NULL) {
/* Create a non-connectable non-scannable advertising set */
err = bt_le_ext_adv_create(BT_LE_EXT_ADV_CONN_NAME, &adv_cb, &adv);
if (err) {
printk("Failed to create advertising set (err %d)\n", err);
}
err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
printk("Failed to set advertising data (err %d)\n", err);
}
__ASSERT_NO_MSG(err == 0);
}
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err) {
printk("Failed to start advertising set (err %d)\n", err);
} else {
printk("Advertising successfully started\n");
}
}
#if defined(CONFIG_BT_IAS)
static void alert_stop(void)
{
printk("Alert stopped\n");
}
static void alert_start(void)
{
printk("Mild alert started\n");
}
static void alert_high_start(void)
{
printk("High alert started\n");
}
BT_IAS_CB_DEFINE(ias_callbacks) = {
.no_alert = alert_stop,
.mild_alert = alert_start,
.high_alert = alert_high_start,
};
#endif /* CONFIG_BT_IAS */
void main(void)
{
int err;
err = bt_enable(NULL);
if (err != 0) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
err = has_server_init();
if (err != 0) {
printk("HAS Server init failed (err %d)\n", err);
return;
}
err = bap_unicast_sr_init();
if (err != 0) {
printk("BAP Unicast Server init failed (err %d)\n", err);
return;
}
if (IS_ENABLED(CONFIG_BT_HAS_HEARING_AID_BINAURAL)) {
err = csip_set_member_init();
if (err != 0) {
printk("CSIP Set Member init failed (err %d)\n", err);
return;
}
err = csip_generate_rsi(csis_rsi_addata);
if (err != 0) {
printk("Failed to generate RSI (err %d)\n", err);
return;
}
}
err = vcp_vol_renderer_init();
if (err != 0) {
printk("VCP Volume Renderer init failed (err %d)\n", err);
return;
}
if (IS_ENABLED(CONFIG_BT_ASCS_ASE_SRC)) {
err = micp_mic_dev_init();
if (err != 0) {
printk("MICP Microphone Device init failed (err %d)\n", err);
return;
}
}
if (IS_ENABLED(CONFIG_BT_TBS_CLIENT)) {
err = ccp_call_ctrl_init();
if (err != 0) {
printk("MICP Microphone Device init failed (err %d)\n", err);
return;
}
}
if (IS_ENABLED(CONFIG_BT_HAS_HEARING_AID_BANDED)) {
/* HAP_d1.0r00; 3.7 BAP Unicast Server role requirements
* A Banded Hearing Aid in the HA role shall set the
* Front Left and the Front Right bits to a value of 0b1
* in the Sink Audio Locations characteristic value.
*/
bt_pacs_set_location(BT_AUDIO_DIR_SINK,
(BT_AUDIO_LOCATION_FRONT_LEFT |
BT_AUDIO_LOCATION_FRONT_RIGHT));
} else {
bt_pacs_set_location(BT_AUDIO_DIR_SINK,
BT_AUDIO_LOCATION_FRONT_LEFT);
}
bt_pacs_set_available_contexts(BT_AUDIO_DIR_SINK,
AVAILABLE_SINK_CONTEXT);
if (IS_ENABLED(CONFIG_BT_ASCS_ASE_SRC)) {
bt_pacs_set_location(BT_AUDIO_DIR_SOURCE,
BT_AUDIO_LOCATION_FRONT_LEFT);
bt_pacs_set_available_contexts(BT_AUDIO_DIR_SOURCE,
AVAILABLE_SOURCE_CONTEXT);
}
k_work_init_delayable(&adv_work, adv_work_handler);
k_work_schedule(&adv_work, K_NO_WAIT);
}