mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-05-13 23:50:29 -04:00
bluez5: bap: Advertise ASCS properties
BAP/USR/ADV/BV-01-C test requires to advertise Audio Stream Control Service properties with General Announcement type and supported contexts values for sink and source which should be in sync with the PACS values.
This commit is contained in:
parent
5e9ecc3ce5
commit
27cdac3cd6
3 changed files with 232 additions and 1 deletions
|
|
@ -146,6 +146,9 @@
|
|||
|
||||
#define BAP_CONTEXT_ALL 0x0fff
|
||||
|
||||
#define BAP_ANNOUNCEMENT_GENERAL 0x00
|
||||
#define BAP_ANNOUNCEMENT_TARGETED 0x01
|
||||
|
||||
#define BT_ISO_QOS_CIG_UNSET 0xff
|
||||
#define BT_ISO_QOS_CIS_UNSET 0xff
|
||||
|
||||
|
|
@ -154,6 +157,8 @@
|
|||
#define BT_ISO_QOS_TARGET_LATENCY_RELIABILITY 0x03
|
||||
|
||||
|
||||
#define BT_ASCS_UUID "0000184e-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define BT_TMAP_UUID "00001855-0000-1000-8000-00805f9b34fb"
|
||||
|
||||
#define BT_TMAP_ROLE_CG_STR "cg"
|
||||
|
|
@ -285,4 +290,12 @@ static const struct {
|
|||
{ BAP_CHANNEL_RS, SPA_AUDIO_CHANNEL_SR }, /* is it the right mapping? */
|
||||
};
|
||||
|
||||
struct bap_ascs_adv {
|
||||
uint8_t announcement_type;
|
||||
uint16_t available_sink_contexts;
|
||||
uint16_t available_source_contexts;
|
||||
uint8_t metadata_length;
|
||||
uint8_t metadata[];
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1697,8 +1697,10 @@ static void adapter_free(struct spa_bt_adapter *adapter)
|
|||
}
|
||||
}
|
||||
|
||||
SPA_FOR_EACH_ELEMENT_VAR(adapter->apps, app)
|
||||
SPA_FOR_EACH_ELEMENT_VAR(adapter->apps, app) {
|
||||
cancel_and_unref(&app->register_call);
|
||||
cancel_and_unref(&app->register_adv_call);
|
||||
}
|
||||
|
||||
spa_bt_player_destroy(adapter->dummy_player);
|
||||
|
||||
|
|
@ -5779,6 +5781,160 @@ static DBusHandlerResult object_manager_handler_bap(DBusConnection *c, DBusMessa
|
|||
return object_manager_handler(c, m, user_data, true);
|
||||
}
|
||||
|
||||
static int append_le_service_data(struct spa_bt_monitor *monitor, DBusMessageIter *iter)
|
||||
{
|
||||
struct bap_ascs_adv *adv;
|
||||
int adv_len;
|
||||
struct ltv_writer writer;
|
||||
DBusMessageIter array_it;
|
||||
|
||||
adv_len = sizeof(struct bap_ascs_adv) + 4;
|
||||
adv = malloc(adv_len);
|
||||
if (!adv)
|
||||
return -1;
|
||||
|
||||
adv->announcement_type = BAP_ANNOUNCEMENT_GENERAL;
|
||||
adv->available_sink_contexts = htobs(monitor->bap_sink_qos.supported_context);
|
||||
adv->available_source_contexts = htobs(monitor->bap_source_qos.supported_context);
|
||||
adv->metadata_length = 4;
|
||||
writer = LTV_WRITER(adv->metadata, 4);
|
||||
ltv_writer_uint16(&writer, BAP_META_TYPE_PREFERRED_CONTEXT, htobs(BAP_CONTEXT_UNSPECIFIED));
|
||||
|
||||
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &array_it);
|
||||
append_basic_array_variant_dict_entry(&array_it, BT_ASCS_UUID, "ay", "y", DBUS_TYPE_BYTE, adv, adv_len);
|
||||
dbus_message_iter_close_container(iter, &array_it);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DBusHandlerResult object_manager_handler_bap_adv(DBusConnection *c, DBusMessage *m, void *user_data)
|
||||
{
|
||||
struct spa_bt_monitor *monitor = user_data;
|
||||
const char *path, *interface, *member;
|
||||
DBusHandlerResult res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
|
||||
path = dbus_message_get_path(m);
|
||||
interface = dbus_message_get_interface(m);
|
||||
member = dbus_message_get_member(m);
|
||||
|
||||
spa_log_debug(monitor->log, "dbus: path=%s, interface=%s, member=%s", path, interface, member);
|
||||
|
||||
if (dbus_message_is_method_call(m, DBUS_INTERFACE_PROPERTIES, "Get")) {
|
||||
const char *iface, *name;
|
||||
DBusMessage *r;
|
||||
DBusMessageIter i, v;
|
||||
|
||||
if (!dbus_message_get_args(m, NULL,
|
||||
DBUS_TYPE_STRING, &iface,
|
||||
DBUS_TYPE_STRING, &name,
|
||||
DBUS_TYPE_INVALID))
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
||||
if (!spa_streq(iface, BLUEZ_LE_ADVERTISEMENT_INTERFACE)) {
|
||||
r = dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS,
|
||||
"No such interface");
|
||||
if (!dbus_connection_send(c, r, NULL))
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
r = dbus_message_new_method_return(m);
|
||||
if (r == NULL)
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
||||
if (spa_streq(name, "Type")) {
|
||||
const char *str = "peripheral";
|
||||
|
||||
dbus_message_iter_init_append(r, &i);
|
||||
dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &str);
|
||||
} else if (spa_streq(name, "ServiceUUIDs")) {
|
||||
const char *str = BT_ASCS_UUID;
|
||||
|
||||
dbus_message_iter_init_append(r, &i);
|
||||
dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, "s", &v);
|
||||
dbus_message_iter_append_basic(&v, DBUS_TYPE_STRING, &str);
|
||||
dbus_message_iter_close_container(&i, &v);
|
||||
} else if (spa_streq(name, "ServiceData")) {
|
||||
dbus_message_iter_init_append(r, &i);
|
||||
if (append_le_service_data(monitor, &i))
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
} else if (spa_streq(name, "SecondaryChannel")) {
|
||||
const char *str = "2M";
|
||||
|
||||
dbus_message_iter_init_append(r, &i);
|
||||
dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &str);
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!dbus_connection_send(monitor->conn, r, NULL))
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
res = DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (dbus_message_is_method_call(m, DBUS_INTERFACE_PROPERTIES, "GetAll")) {
|
||||
const char *iface;
|
||||
DBusMessage *r;
|
||||
DBusMessageIter i, v;
|
||||
DBusMessageIter dict_entry_it, variant_it, array_it;
|
||||
char *str;
|
||||
|
||||
if (!dbus_message_get_args(m, NULL, DBUS_TYPE_STRING, &iface, DBUS_TYPE_INVALID))
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
||||
if (!spa_streq(iface, BLUEZ_LE_ADVERTISEMENT_INTERFACE)) {
|
||||
r = dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS,
|
||||
"No such interface");
|
||||
if (!dbus_connection_send(c, r, NULL))
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
r = dbus_message_new_method_return(m);
|
||||
if (r == NULL)
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
||||
dbus_message_iter_init_append(r, &i);
|
||||
dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, "{sv}", &v);
|
||||
str = "peripheral";
|
||||
append_basic_variant_dict_entry(&v, "Type", DBUS_TYPE_STRING, "s", &str);
|
||||
str = "2M";
|
||||
append_basic_variant_dict_entry(&v, "SecondaryChannel", DBUS_TYPE_STRING, "s", &str);
|
||||
|
||||
dbus_message_iter_open_container(&v, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_it);
|
||||
str = "ServiceUUIDs";
|
||||
dbus_message_iter_append_basic(&dict_entry_it, DBUS_TYPE_STRING, &str);
|
||||
dbus_message_iter_open_container(&dict_entry_it, DBUS_TYPE_VARIANT, "as", &variant_it);
|
||||
dbus_message_iter_open_container(&variant_it, DBUS_TYPE_ARRAY, "s", &array_it);
|
||||
str = BT_ASCS_UUID;
|
||||
dbus_message_iter_append_basic(&array_it, DBUS_TYPE_STRING, &str);
|
||||
dbus_message_iter_close_container(&variant_it, &array_it);
|
||||
dbus_message_iter_close_container(&dict_entry_it, &variant_it);
|
||||
dbus_message_iter_close_container(&v, &dict_entry_it);
|
||||
|
||||
dbus_message_iter_open_container(&v, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_it);
|
||||
str = "ServiceData";
|
||||
dbus_message_iter_append_basic(&dict_entry_it, DBUS_TYPE_STRING, &str);
|
||||
dbus_message_iter_open_container(&dict_entry_it, DBUS_TYPE_VARIANT, "a{sv}", &variant_it);
|
||||
if (append_le_service_data(monitor, &variant_it))
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
dbus_message_iter_close_container(&dict_entry_it, &variant_it);
|
||||
dbus_message_iter_close_container(&v, &dict_entry_it);
|
||||
|
||||
dbus_message_iter_close_container(&i, &v);
|
||||
|
||||
if (!dbus_connection_send(monitor->conn, r, NULL))
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
res = DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (dbus_message_is_method_call(m, DBUS_INTERFACE_PROPERTIES, "Set")) {
|
||||
spa_log_info(monitor->log, "Set property is not supported");
|
||||
res = DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (dbus_message_is_method_call(m, BLUEZ_LE_ADVERTISEMENT_INTERFACE, "Release")) {
|
||||
spa_log_info(monitor->log, "Release property is not supported");
|
||||
res = DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void bluez_register_application_a2dp_reply(DBusPendingCall *pending, void *user_data)
|
||||
{
|
||||
struct spa_bt_adapter *adapter = user_data;
|
||||
|
|
@ -5804,11 +5960,35 @@ static void bluez_register_application_a2dp_reply(DBusPendingCall *pending, void
|
|||
app->registered = true;
|
||||
}
|
||||
|
||||
static void bluez_register_le_advertisement_reply(DBusPendingCall *pending, void *user_data)
|
||||
{
|
||||
struct spa_bt_adapter *adapter = user_data;
|
||||
struct spa_bt_monitor *monitor = adapter->monitor;
|
||||
struct bluez_app *app = &adapter->apps[BLUEZ_APP_BAP];
|
||||
|
||||
spa_assert(app->register_adv_call == pending);
|
||||
spa_autoptr(DBusMessage) r = steal_reply_and_unref(&app->register_adv_call);
|
||||
if (r == NULL)
|
||||
return;
|
||||
|
||||
if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
|
||||
spa_log_warn(monitor->log, "BlueZ D-Bus ObjectManager not available");
|
||||
return;
|
||||
}
|
||||
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
|
||||
spa_log_error(monitor->log, "RegisterAdvertisement() failed: %s",
|
||||
dbus_message_get_error_name(r));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void bluez_register_application_bap_reply(DBusPendingCall *pending, void *user_data)
|
||||
{
|
||||
struct spa_bt_adapter *adapter = user_data;
|
||||
struct spa_bt_monitor *monitor = adapter->monitor;
|
||||
struct bluez_app *app = &adapter->apps[BLUEZ_APP_BAP];
|
||||
DBusMessageIter object_it, dict_it;
|
||||
const char *object_path = BAP_ADVERTISEMENT_PATH;
|
||||
|
||||
spa_assert(app->register_call == pending);
|
||||
spa_autoptr(DBusMessage) r = steal_reply_and_unref(&app->register_call);
|
||||
|
|
@ -5822,6 +6002,22 @@ static void bluez_register_application_bap_reply(DBusPendingCall *pending, void
|
|||
}
|
||||
|
||||
app->registered = true;
|
||||
|
||||
spa_autoptr(DBusMessage) m = dbus_message_new_method_call(BLUEZ_SERVICE,
|
||||
adapter->path,
|
||||
BLUEZ_LE_ADVERTISING_MANAGER_INTERFACE,
|
||||
"RegisterAdvertisement");
|
||||
if (m == NULL)
|
||||
return;
|
||||
|
||||
dbus_message_iter_init_append(m, &object_it);
|
||||
dbus_message_iter_append_basic(&object_it, DBUS_TYPE_OBJECT_PATH, &object_path);
|
||||
dbus_message_iter_open_container(&object_it, DBUS_TYPE_ARRAY, "{sv}", &dict_it);
|
||||
dbus_message_iter_close_container(&object_it, &dict_it);
|
||||
|
||||
app->register_adv_call = send_with_reply(monitor->conn, m, bluez_register_le_advertisement_reply, adapter);
|
||||
if (!app->register_adv_call)
|
||||
return;
|
||||
}
|
||||
|
||||
static int register_media_endpoint(struct spa_bt_monitor *monitor,
|
||||
|
|
@ -5859,6 +6055,10 @@ static int register_media_application(struct spa_bt_monitor * monitor)
|
|||
static const DBusObjectPathVTable vtable_object_manager_bap = {
|
||||
.message_function = object_manager_handler_bap,
|
||||
};
|
||||
static const DBusObjectPathVTable vtable_object_manager_bap_adv = {
|
||||
.message_function = object_manager_handler_bap_adv,
|
||||
};
|
||||
bool le_audio;
|
||||
|
||||
spa_log_info(monitor->log, "Registering DBus media object manager: %s",
|
||||
A2DP_OBJECT_MANAGER_PATH);
|
||||
|
|
@ -5884,9 +6084,21 @@ static int register_media_application(struct spa_bt_monitor * monitor)
|
|||
if (codec->kind == MEDIA_CODEC_BAP) {
|
||||
register_media_endpoint(monitor, codec, SPA_BT_MEDIA_SOURCE_BROADCAST);
|
||||
register_media_endpoint(monitor, codec, SPA_BT_MEDIA_SINK_BROADCAST);
|
||||
le_audio = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!le_audio)
|
||||
return 0;
|
||||
|
||||
spa_log_info(monitor->log, "Registering DBus BAP advertisement : %s",
|
||||
BAP_ADVERTISEMENT_PATH);
|
||||
|
||||
if (!dbus_connection_register_object_path(monitor->conn,
|
||||
BAP_ADVERTISEMENT_PATH,
|
||||
&vtable_object_manager_bap_adv, monitor))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -5923,6 +6135,7 @@ static void unregister_media_application(struct spa_bt_monitor * monitor)
|
|||
}
|
||||
}
|
||||
|
||||
dbus_connection_unregister_object_path(monitor->conn, BAP_ADVERTISEMENT_PATH);
|
||||
dbus_connection_unregister_object_path(monitor->conn, BAP_OBJECT_MANAGER_PATH);
|
||||
dbus_connection_unregister_object_path(monitor->conn, A2DP_OBJECT_MANAGER_PATH);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ extern "C" {
|
|||
#define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
|
||||
#define BLUEZ_INTERFACE_BATTERY_PROVIDER BLUEZ_SERVICE ".BatteryProvider1"
|
||||
#define BLUEZ_INTERFACE_BATTERY_PROVIDER_MANAGER BLUEZ_SERVICE ".BatteryProviderManager1"
|
||||
#define BLUEZ_LE_ADVERTISING_MANAGER_INTERFACE BLUEZ_SERVICE ".LEAdvertisingManager1"
|
||||
#define BLUEZ_LE_ADVERTISEMENT_INTERFACE BLUEZ_SERVICE ".LEAdvertisement1"
|
||||
|
||||
#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
|
||||
#define DBUS_SIGNAL_INTERFACES_ADDED "InterfacesAdded"
|
||||
|
|
@ -154,6 +156,8 @@ extern "C" {
|
|||
#define BAP_BROADCAST_SOURCE_ENDPOINT BAP_OBJECT_MANAGER_PATH "/BAPBroadcastSource"
|
||||
#define BAP_BROADCAST_SINK_ENDPOINT BAP_OBJECT_MANAGER_PATH "/BAPBroadcastSink"
|
||||
|
||||
#define BAP_ADVERTISEMENT_PATH "/MediaLEAdvertisement"
|
||||
|
||||
#define SPA_BT_UNKNOWN_DELAY 0
|
||||
|
||||
#define SPA_BT_NO_BATTERY ((uint8_t)255)
|
||||
|
|
@ -376,6 +380,7 @@ struct spa_bt_adapter {
|
|||
struct bluez_app {
|
||||
DBusPendingCall *register_call;
|
||||
bool registered;
|
||||
DBusPendingCall *register_adv_call;
|
||||
} apps[BLUEZ_APP_LAST];
|
||||
|
||||
unsigned int has_msbc:1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue