mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-08-31 06:10:03 +00:00
nimble/audio: Add LE Audio event listener
This adds dedicated event listener for LE Audio events.
This commit is contained in:
committed by
Łukasz Rymanowski
parent
b3cd0b6280
commit
a3cef58817
@@ -21,9 +21,147 @@
|
||||
#define H_BLE_AUDIO_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include "host/ble_audio_common.h"
|
||||
|
||||
/** @brief Public Broadcast Announcement features bits */
|
||||
enum ble_audio_pub_bcst_announcement_feat {
|
||||
/** Broadcast Stream Encryption */
|
||||
BLE_AUDIO_PUB_BCST_ANNOUNCEMENT_FEAT_ENCRYPTION = 1 << 0,
|
||||
|
||||
/** Standard Quality Public Broadcast Audio */
|
||||
BLE_AUDIO_PUB_BCST_ANNOUNCEMENT_FEAT_SQ = 1 << 1,
|
||||
|
||||
/** High Quality Public Broadcast Audio */
|
||||
BLE_AUDIO_PUB_BCST_ANNOUNCEMENT_FEAT_HQ = 1 << 2,
|
||||
};
|
||||
|
||||
/** @brief Public Broadcast Announcement structure */
|
||||
struct ble_audio_pub_bcst_announcement {
|
||||
/** Public Broadcast Announcement features bitfield */
|
||||
enum ble_audio_pub_bcst_announcement_feat features;
|
||||
|
||||
/** Metadata length */
|
||||
uint8_t metadata_len;
|
||||
|
||||
/** Metadata */
|
||||
const uint8_t *metadata;
|
||||
};
|
||||
|
||||
struct ble_audio_bcst_name {
|
||||
/** Broadcast Name length */
|
||||
uint8_t name_len;
|
||||
|
||||
/** Broadcast Name */
|
||||
const char *name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @defgroup ble_audio_events Bluetooth Low Energy Audio Events
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** BLE Audio event: Broadcast Announcement */
|
||||
#define BLE_AUDIO_EVENT_BCST_ANNOUNCEMENT 0
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @brief Broadcast Announcement */
|
||||
struct ble_audio_event_bcst_announcement {
|
||||
/** Extended advertising report */
|
||||
const struct ble_gap_ext_disc_desc *ext_disc;
|
||||
|
||||
/** Broadcast ID */
|
||||
uint32_t broadcast_id;
|
||||
|
||||
/** Additional service data included in Broadcast Audio Announcement */
|
||||
const uint8_t *svc_data;
|
||||
|
||||
/** Additional service data length */
|
||||
uint16_t svc_data_len;
|
||||
|
||||
/** Optional Public Broadcast Announcement data */
|
||||
struct ble_audio_pub_bcst_announcement *pub_announcement_data;
|
||||
|
||||
/** Optional Broadcast Name */
|
||||
struct ble_audio_bcst_name *name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a BLE Audio related event. When such an event occurs, the host
|
||||
* notifies the application by passing an instance of this structure to an
|
||||
* application-specified callback.
|
||||
*/
|
||||
struct ble_audio_event {
|
||||
/**
|
||||
* Indicates the type of BLE Audio event that occurred. This is one of the
|
||||
* BLE_AUDIO_EVENT codes.
|
||||
*/
|
||||
uint8_t type;
|
||||
|
||||
/**
|
||||
* A discriminated union containing additional details concerning the event.
|
||||
* The 'type' field indicates which member of the union is valid.
|
||||
*/
|
||||
union {
|
||||
/**
|
||||
* @ref BLE_AUDIO_EVENT_BCST_ANNOUNCEMENT
|
||||
*
|
||||
* Represents a received Broadcast Announcement.
|
||||
*/
|
||||
struct ble_audio_event_bcst_announcement bcst_announcement;
|
||||
};
|
||||
};
|
||||
|
||||
/** Callback function type for handling BLE Audio events. */
|
||||
typedef int ble_audio_event_fn(struct ble_audio_event *event, void *arg);
|
||||
|
||||
/**
|
||||
* Event listener structure
|
||||
*
|
||||
* This should be used as an opaque structure and not modified manually.
|
||||
*/
|
||||
struct ble_audio_event_listener {
|
||||
/** The function to call when a BLE Audio event occurs. */
|
||||
ble_audio_event_fn *fn;
|
||||
|
||||
/** An optional argument to pass to the event handler function. */
|
||||
void *arg;
|
||||
|
||||
/** Singly-linked list entry. */
|
||||
SLIST_ENTRY(ble_audio_event_listener) next;
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers listener for BLE Audio events
|
||||
*
|
||||
* On success listener structure will be initialized automatically and does not
|
||||
* need to be initialized prior to calling this function. To change callback
|
||||
* and/or argument unregister listener first and register it again.
|
||||
*
|
||||
* @param[in] listener Listener structure
|
||||
* @param[in] event_mask Optional event mask
|
||||
* @param[in] fn Callback function
|
||||
* @param[in] arg Optional callback argument
|
||||
*
|
||||
* @return 0 on success
|
||||
* BLE_HS_EINVAL if no callback is specified
|
||||
* BLE_HS_EALREADY if listener is already registered
|
||||
*/
|
||||
int ble_audio_event_listener_register(struct ble_audio_event_listener *listener,
|
||||
ble_audio_event_fn *fn, void *arg);
|
||||
|
||||
/**
|
||||
* Unregisters listener for BLE Audio events
|
||||
*
|
||||
* @param[in] listener Listener structure
|
||||
*
|
||||
* @return 0 on success
|
||||
* BLE_HS_ENOENT if listener was not registered
|
||||
*/
|
||||
int ble_audio_event_listener_unregister(struct ble_audio_event_listener *listener);
|
||||
|
||||
/**
|
||||
* BASE iterator
|
||||
*
|
||||
|
||||
@@ -23,6 +23,238 @@
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/audio/ble_audio.h"
|
||||
|
||||
#include "ble_audio_priv.h"
|
||||
|
||||
static struct ble_gap_event_listener ble_audio_gap_event_listener;
|
||||
static SLIST_HEAD(, ble_audio_event_listener) ble_audio_event_listener_list =
|
||||
SLIST_HEAD_INITIALIZER(ble_audio_event_listener_list);
|
||||
|
||||
struct ble_audio_adv_parse_bcst_announcement_data {
|
||||
struct ble_audio_event event;
|
||||
struct ble_audio_pub_bcst_announcement pub;
|
||||
struct ble_audio_bcst_name name;
|
||||
bool success;
|
||||
};
|
||||
|
||||
static int
|
||||
ble_audio_adv_parse_bcst_announcement(const struct ble_hs_adv_field *field,
|
||||
void *user_data)
|
||||
{
|
||||
struct ble_audio_adv_parse_bcst_announcement_data *data = user_data;
|
||||
struct ble_audio_event_bcst_announcement *event;
|
||||
const uint8_t value_len = field->length - sizeof(field->length);
|
||||
ble_uuid16_t uuid16 = BLE_UUID16_INIT(0);
|
||||
uint8_t offset = 0;
|
||||
|
||||
event = &data->event.bcst_announcement;
|
||||
|
||||
data->success = false;
|
||||
|
||||
switch (field->type) {
|
||||
case BLE_HS_ADV_TYPE_SVC_DATA_UUID16:
|
||||
if (value_len < 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
uuid16.value = get_le16(&field->value[offset]);
|
||||
offset += 2;
|
||||
|
||||
switch (uuid16.value) {
|
||||
case BLE_BROADCAST_AUDIO_ANNOUNCEMENT_SVC_UUID:
|
||||
if ((value_len - offset) < 3) {
|
||||
/* Stop parsing */
|
||||
data->success = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
event->broadcast_id = get_le24(&field->value[offset]);
|
||||
offset += 3;
|
||||
|
||||
if (value_len > offset) {
|
||||
event->svc_data = &field->value[offset];
|
||||
event->svc_data_len = value_len - offset;
|
||||
}
|
||||
|
||||
data->success = true;
|
||||
break;
|
||||
|
||||
case BLE_BROADCAST_PUB_ANNOUNCEMENT_SVC_UUID:
|
||||
if (event->pub_announcement_data != NULL) {
|
||||
/* Ignore */
|
||||
break;
|
||||
}
|
||||
|
||||
if ((value_len - offset) < 2) {
|
||||
/* Stop parsing */
|
||||
data->success = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
data->pub.features = field->value[offset++];
|
||||
data->pub.metadata_len = field->value[offset++];
|
||||
|
||||
if ((value_len - offset) < data->pub.metadata_len) {
|
||||
break;
|
||||
}
|
||||
|
||||
data->pub.metadata = &field->value[offset];
|
||||
|
||||
event->pub_announcement_data = &data->pub;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BLE_HS_ADV_TYPE_BROADCAST_NAME:
|
||||
if (event->name != NULL) {
|
||||
/* Ignore */
|
||||
break;
|
||||
}
|
||||
|
||||
if (value_len < 4 || value_len > 32) {
|
||||
/* Stop parsing */
|
||||
data->success = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
data->name.name = (char *)field->value;
|
||||
data->name.name_len = value_len;
|
||||
|
||||
event->name = &data->name;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Continue parsing */
|
||||
return BLE_HS_ENOENT;
|
||||
}
|
||||
|
||||
static int
|
||||
ble_audio_gap_event(struct ble_gap_event *gap_event, void *arg)
|
||||
{
|
||||
switch (gap_event->type) {
|
||||
case BLE_GAP_EVENT_EXT_DISC: {
|
||||
struct ble_audio_adv_parse_bcst_announcement_data data = { 0 };
|
||||
int rc;
|
||||
|
||||
rc = ble_hs_adv_parse(gap_event->ext_disc.data,
|
||||
gap_event->ext_disc.length_data,
|
||||
ble_audio_adv_parse_bcst_announcement, &data);
|
||||
if (rc == 0 && data.success) {
|
||||
data.event.type = BLE_AUDIO_EVENT_BCST_ANNOUNCEMENT;
|
||||
data.event.bcst_announcement.ext_disc = &gap_event->ext_disc;
|
||||
|
||||
(void)ble_audio_event_listener_call(&data.event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ble_audio_event_listener_register(struct ble_audio_event_listener *listener,
|
||||
ble_audio_event_fn *fn, void *arg)
|
||||
{
|
||||
struct ble_audio_event_listener *evl = NULL;
|
||||
int rc;
|
||||
|
||||
if (listener == NULL) {
|
||||
BLE_HS_LOG_ERROR("NULL listener!\n");
|
||||
return BLE_HS_EINVAL;
|
||||
}
|
||||
|
||||
if (fn == NULL) {
|
||||
BLE_HS_LOG_ERROR("NULL fn!\n");
|
||||
return BLE_HS_EINVAL;
|
||||
}
|
||||
|
||||
SLIST_FOREACH(evl, &ble_audio_event_listener_list, next) {
|
||||
if (evl == listener) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (evl) {
|
||||
return BLE_HS_EALREADY;
|
||||
}
|
||||
|
||||
if (SLIST_EMPTY(&ble_audio_event_listener_list)) {
|
||||
rc = ble_gap_event_listener_register(
|
||||
&ble_audio_gap_event_listener,
|
||||
ble_audio_gap_event, NULL);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
memset(listener, 0, sizeof(*listener));
|
||||
listener->fn = fn;
|
||||
listener->arg = arg;
|
||||
SLIST_INSERT_HEAD(&ble_audio_event_listener_list, listener, next);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ble_audio_event_listener_unregister(struct ble_audio_event_listener *listener)
|
||||
{
|
||||
struct ble_audio_event_listener *evl = NULL;
|
||||
int rc;
|
||||
|
||||
if (listener == NULL) {
|
||||
BLE_HS_LOG_ERROR("NULL listener!\n");
|
||||
return BLE_HS_EINVAL;
|
||||
}
|
||||
|
||||
/* We check if element exists on the list only for sanity to let caller
|
||||
* know whether it registered its listener before.
|
||||
*/
|
||||
SLIST_FOREACH(evl, &ble_audio_event_listener_list, next) {
|
||||
if (evl == listener) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!evl) {
|
||||
return BLE_HS_ENOENT;
|
||||
}
|
||||
|
||||
SLIST_REMOVE(&ble_audio_event_listener_list, listener,
|
||||
ble_audio_event_listener, next);
|
||||
|
||||
if (SLIST_EMPTY(&ble_audio_event_listener_list)) {
|
||||
rc = ble_gap_event_listener_unregister(
|
||||
&ble_audio_gap_event_listener);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ble_audio_event_listener_call(struct ble_audio_event *event)
|
||||
{
|
||||
struct ble_audio_event_listener *evl = NULL;
|
||||
|
||||
SLIST_FOREACH(evl, &ble_audio_event_listener_list, next) {
|
||||
evl->fn(event, evl->arg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the next subgroup data pointer */
|
||||
static const uint8_t *
|
||||
ble_audio_base_subgroup_next(uint8_t num_bis, const uint8_t *data,
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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_AUDIO_PRIV_
|
||||
#define H_BLE_AUDIO_PRIV_
|
||||
|
||||
#include "host/audio/ble_audio.h"
|
||||
|
||||
int ble_audio_event_listener_call(struct ble_audio_event *event);
|
||||
|
||||
#endif /* H_BLE_AUDIO_PRIV_ */
|
||||
@@ -21,10 +21,12 @@
|
||||
#include "testutil/testutil.h"
|
||||
|
||||
TEST_SUITE_DECL(ble_audio_base_parse_test_suite);
|
||||
TEST_CASE_DECL(ble_audio_listener_register_test);
|
||||
|
||||
TEST_SUITE(ble_audio_test)
|
||||
{
|
||||
ble_audio_base_parse_test_suite();
|
||||
ble_audio_listener_register_test();
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 "testutil/testutil.h"
|
||||
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/audio/ble_audio.h"
|
||||
|
||||
static struct ble_audio_event_listener event_listener;
|
||||
|
||||
static int
|
||||
event_handler(struct ble_audio_event *event, void *arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST_CASE_SELF(ble_audio_listener_register_test)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ble_audio_event_listener_register(&event_listener, event_handler,
|
||||
NULL);
|
||||
TEST_ASSERT(rc == 0);
|
||||
|
||||
rc = ble_audio_event_listener_register(&event_listener, event_handler,
|
||||
NULL);
|
||||
TEST_ASSERT(rc != 0);
|
||||
|
||||
rc = ble_audio_event_listener_unregister(&event_listener);
|
||||
TEST_ASSERT(rc == 0);
|
||||
|
||||
rc = ble_audio_event_listener_register(NULL, event_handler, NULL);
|
||||
TEST_ASSERT(rc != 0);
|
||||
|
||||
rc = ble_audio_event_listener_register(&event_listener, NULL, NULL);
|
||||
TEST_ASSERT(rc != 0);
|
||||
|
||||
rc = ble_audio_event_listener_unregister(NULL);
|
||||
TEST_ASSERT(rc != 0);
|
||||
|
||||
rc = ble_audio_event_listener_unregister(&event_listener);
|
||||
TEST_ASSERT(rc != 0);
|
||||
}
|
||||
@@ -27,3 +27,5 @@ syscfg.vals:
|
||||
|
||||
BLE_VERSION: 54
|
||||
BLE_HS_DEBUG: 1
|
||||
|
||||
BLE_EXT_ADV: 1
|
||||
|
||||
@@ -67,7 +67,9 @@
|
||||
|
||||
/** Broadcast Audio Announcement Service UUID. */
|
||||
#define BLE_BROADCAST_AUDIO_ANNOUNCEMENT_SVC_UUID 0x1852
|
||||
#define BLE_BROADCAST_PUBLIC_BROADCAST_ANNOUNCEMENT_SVC_UUID 0x1856
|
||||
|
||||
/** Public Broadcast Announcement Service UUID. */
|
||||
#define BLE_BROADCAST_PUB_ANNOUNCEMENT_SVC_UUID 0x1856
|
||||
|
||||
/**
|
||||
* @defgroup ble_audio_sampling_rates Bluetooth Low Energy Audio Sampling Rates
|
||||
|
||||
@@ -62,7 +62,7 @@ ble_svc_auracast_create(const struct ble_svc_auracast_create_params *params,
|
||||
auracast_svc_data[data_offset] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16;
|
||||
data_offset++;
|
||||
put_le16(&auracast_svc_data[data_offset],
|
||||
BLE_BROADCAST_PUBLIC_BROADCAST_ANNOUNCEMENT_SVC_UUID);
|
||||
BLE_BROADCAST_PUB_ANNOUNCEMENT_SVC_UUID);
|
||||
data_offset += 2;
|
||||
auracast_svc_data[data_offset] = features;
|
||||
data_offset++;
|
||||
|
||||
Reference in New Issue
Block a user