This changes HAS registration to be dynamic and let's the application set Hearing Aid Type and binaural features. Often, devices are flashed with generic firmware with some features stored post factory production, requiring the settings to be moved from compile time to run-time. This change will increase the RAM usage as the GATT service is moved from ROM to RAM. Signed-off-by: Lars Knudsen <larsgk@gmail.com> Co-author: Soren Engquist <soren@engquist.dk>
237 lines
6.1 KiB
C
237 lines
6.1 KiB
C
/*
|
|
* 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_SET_MEMBER)
|
|
BT_DATA(BT_DATA_CSIS_RSI, csis_rsi_addata, ARRAY_SIZE(csis_rsi_addata)),
|
|
#endif /* CONFIG_BT_CSIP_SET_MEMBER */
|
|
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_SET_MEMBER)
|
|
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_SET_MEMBER */
|
|
|
|
static const struct bt_le_ext_adv_cb adv_cb = {
|
|
#if defined(CONFIG_BT_PRIVACY) && defined(CONFIG_BT_CSIP_SET_MEMBER)
|
|
.rpa_expired = adv_rpa_expired_cb,
|
|
#endif /* CONFIG_BT_PRIVACY && CONFIG_BT_CSIP_SET_MEMBER */
|
|
};
|
|
|
|
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_HAP_HA_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_HAP_HA_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);
|
|
}
|