mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
bluez5: parse device set information via dbus
Parse device set information from DBus, and link devices to each other according to it. Add signal for notifying about device set changes.
This commit is contained in:
parent
eca13ec230
commit
d642569394
2 changed files with 335 additions and 1 deletions
|
|
@ -10,6 +10,7 @@
|
|||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
|
||||
|
|
@ -1268,6 +1269,7 @@ static struct spa_bt_device *device_create(struct spa_bt_monitor *monitor, const
|
|||
spa_list_init(&d->remote_endpoint_list);
|
||||
spa_list_init(&d->transport_list);
|
||||
spa_list_init(&d->codec_switch_list);
|
||||
spa_list_init(&d->set_membership_list);
|
||||
|
||||
spa_hook_list_init(&d->listener_list);
|
||||
|
||||
|
|
@ -1294,6 +1296,7 @@ static void device_free(struct spa_bt_device *device)
|
|||
struct spa_bt_media_codec_switch *sw;
|
||||
struct spa_bt_transport *t, *tt;
|
||||
struct spa_bt_monitor *monitor = device->monitor;
|
||||
struct spa_bt_set_membership *s;
|
||||
|
||||
spa_log_debug(monitor->log, "%p", device);
|
||||
|
||||
|
|
@ -1323,6 +1326,13 @@ static void device_free(struct spa_bt_device *device)
|
|||
spa_list_consume(sw, &device->codec_switch_list, device_link)
|
||||
media_codec_switch_free(sw);
|
||||
|
||||
spa_list_consume(s, &device->set_membership_list, link) {
|
||||
spa_list_remove(&s->link);
|
||||
spa_list_remove(&s->others);
|
||||
free(s->path);
|
||||
free(s);
|
||||
}
|
||||
|
||||
spa_list_remove(&device->link);
|
||||
free(device->path);
|
||||
free(device->alias);
|
||||
|
|
@ -1334,6 +1344,84 @@ static void device_free(struct spa_bt_device *device)
|
|||
free(device);
|
||||
}
|
||||
|
||||
static struct spa_bt_set_membership *device_set_find(struct spa_bt_monitor *monitor, const char *path)
|
||||
{
|
||||
struct spa_bt_device *d;
|
||||
|
||||
spa_list_for_each(d, &monitor->device_list, link) {
|
||||
struct spa_bt_set_membership *s;
|
||||
|
||||
spa_list_for_each(s, &d->set_membership_list, link) {
|
||||
if (spa_streq(s->path, path))
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int device_add_device_set(struct spa_bt_device *device, const char *path, uint8_t rank)
|
||||
{
|
||||
struct spa_bt_monitor *monitor = device->monitor;
|
||||
struct spa_bt_set_membership *s, *set;
|
||||
|
||||
spa_list_for_each(s, &device->set_membership_list, link) {
|
||||
if (spa_streq(s->path, path)) {
|
||||
if (rank)
|
||||
s->rank = rank;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
s = calloc(1, sizeof(struct spa_bt_set_membership));
|
||||
if (s == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
s->path = strdup(path);
|
||||
if (!s->path) {
|
||||
free(s);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
s->device = device;
|
||||
s->rank = rank;
|
||||
|
||||
spa_list_init(&s->others);
|
||||
|
||||
/* Join with other set members, if any */
|
||||
set = device_set_find(monitor, path);
|
||||
if (set)
|
||||
spa_list_append(&set->others, &s->others);
|
||||
|
||||
spa_list_append(&device->set_membership_list, &s->link);
|
||||
|
||||
spa_log_debug(monitor->log, "device %p: add %s to device set %s", device,
|
||||
device->path, path);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool device_remove_device_set(struct spa_bt_device *device, const char *path)
|
||||
{
|
||||
struct spa_bt_monitor *monitor = device->monitor;
|
||||
struct spa_bt_set_membership *s;
|
||||
|
||||
spa_list_for_each(s, &device->set_membership_list, link) {
|
||||
if (spa_streq(s->path, path)) {
|
||||
spa_log_debug(monitor->log,
|
||||
"device %p: remove %s from device set %s", device,
|
||||
device->path, path);
|
||||
spa_list_remove(&s->link);
|
||||
spa_list_remove(&s->others);
|
||||
free(s->path);
|
||||
free(s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int spa_bt_format_vendor_product_id(uint16_t source_id, uint16_t vendor_id, uint16_t product_id,
|
||||
char *vendor_str, int vendor_str_size, char *product_str, int product_str_size)
|
||||
{
|
||||
|
|
@ -1736,10 +1824,14 @@ static void device_set_connected(struct spa_bt_device *device, int connected)
|
|||
}
|
||||
}
|
||||
|
||||
static void device_update_set_status(struct spa_bt_device *device, bool force, const char *path);
|
||||
|
||||
int spa_bt_device_connect_profile(struct spa_bt_device *device, enum spa_bt_profile profile)
|
||||
{
|
||||
uint32_t prev_connected = device->connected_profiles;
|
||||
device->connected_profiles |= profile;
|
||||
if ((prev_connected ^ device->connected_profiles) & SPA_BT_PROFILE_BAP_DUPLEX)
|
||||
device_update_set_status(device, true, NULL);
|
||||
spa_bt_device_check_profiles(device, false);
|
||||
if (device->connected_profiles != prev_connected)
|
||||
spa_bt_device_emit_profiles_changed(device, device->profiles, prev_connected);
|
||||
|
|
@ -1763,6 +1855,212 @@ static void device_update_hw_volume_profiles(struct spa_bt_device *device)
|
|||
spa_log_debug(monitor->log, "hw-volume-profiles:%08x", (int)device->hw_volume_profiles);
|
||||
}
|
||||
|
||||
static bool device_set_update_leader(struct spa_bt_set_membership *set)
|
||||
{
|
||||
struct spa_bt_set_membership *s, *leader;
|
||||
int min_rank = INT_MAX;
|
||||
int leader_rank = INT_MAX;
|
||||
|
||||
leader = NULL;
|
||||
|
||||
spa_bt_for_each_set_member(s, set) {
|
||||
min_rank = SPA_MIN(min_rank, s->rank);
|
||||
if (s->leader) {
|
||||
leader_rank = s->rank;
|
||||
leader = s;
|
||||
}
|
||||
}
|
||||
|
||||
if (min_rank >= leader_rank && leader)
|
||||
return false;
|
||||
|
||||
spa_bt_for_each_set_member(s, set) {
|
||||
if (leader == NULL && s->rank == min_rank &&
|
||||
(s->device->connected_profiles & SPA_BT_PROFILE_BAP_DUPLEX)) {
|
||||
s->leader = true;
|
||||
leader = s;
|
||||
} else {
|
||||
s->leader = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (leader) {
|
||||
struct spa_bt_monitor *monitor = leader->device->monitor;
|
||||
|
||||
spa_log_debug(monitor->log, "device set %s: leader is %s",
|
||||
leader->path, leader->device->path);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void device_update_set_status(struct spa_bt_device *device, bool force, const char *path)
|
||||
{
|
||||
struct spa_bt_set_membership *s, *set;
|
||||
|
||||
spa_list_for_each(set, &device->set_membership_list, link) {
|
||||
if (path && !spa_streq(set->path, path))
|
||||
continue;
|
||||
|
||||
if (device_set_update_leader(set) || force) {
|
||||
spa_bt_for_each_set_member(s, set)
|
||||
if (!s->leader)
|
||||
spa_bt_device_emit_device_set_changed(s->device);
|
||||
spa_bt_for_each_set_member(s, set)
|
||||
if (s->leader)
|
||||
spa_bt_device_emit_device_set_changed(s->device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int device_set_update_props(struct spa_bt_monitor *monitor,
|
||||
const char *path, DBusMessageIter *props_iter, DBusMessageIter *invalidated_iter)
|
||||
{
|
||||
struct spa_bt_device *old[256];
|
||||
struct spa_bt_device *new[256];
|
||||
struct spa_bt_set_membership *set;
|
||||
size_t num_old = 0, num_new = 0;
|
||||
size_t i;
|
||||
|
||||
if (!props_iter)
|
||||
goto done;
|
||||
|
||||
/* Find current devices */
|
||||
while (dbus_message_iter_get_arg_type(props_iter) != DBUS_TYPE_INVALID) {
|
||||
DBusMessageIter it[2];
|
||||
const char *key;
|
||||
|
||||
dbus_message_iter_recurse(props_iter, &it[0]);
|
||||
dbus_message_iter_get_basic(&it[0], &key);
|
||||
dbus_message_iter_next(&it[0]);
|
||||
dbus_message_iter_recurse(&it[0], &it[1]);
|
||||
|
||||
if (spa_streq(key, "Devices")) {
|
||||
DBusMessageIter iter;
|
||||
int i = 0;
|
||||
|
||||
if (!check_iter_signature(&it[1], "ao"))
|
||||
goto next;
|
||||
|
||||
dbus_message_iter_recurse(&it[1], &iter);
|
||||
|
||||
while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
|
||||
struct spa_bt_device *d;
|
||||
const char *dev_path;
|
||||
|
||||
dbus_message_iter_get_basic(&iter, &dev_path);
|
||||
|
||||
spa_log_debug(monitor->log, "device set %s: Devices[%d]=%s",
|
||||
path, i++, dev_path);
|
||||
|
||||
if (num_new >= SPA_N_ELEMENTS(new))
|
||||
break;
|
||||
d = spa_bt_device_find(monitor, dev_path);
|
||||
if (d)
|
||||
new[num_new++] = d;
|
||||
|
||||
dbus_message_iter_next(&iter);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
spa_log_debug(monitor->log, "device set %s: unhandled key %s",
|
||||
path, key);
|
||||
|
||||
next:
|
||||
dbus_message_iter_next(props_iter);
|
||||
}
|
||||
|
||||
done:
|
||||
/* Find devices to remove */
|
||||
set = device_set_find(monitor, path);
|
||||
if (set) {
|
||||
struct spa_bt_set_membership *s;
|
||||
|
||||
spa_bt_for_each_set_member(s, set) {
|
||||
for (i = 0; i < num_new; ++i)
|
||||
if (s->device == new[i])
|
||||
break;
|
||||
if (i == num_new) {
|
||||
if (num_old >= SPA_N_ELEMENTS(old))
|
||||
break;
|
||||
old[num_old++] = s->device;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove old devices */
|
||||
for (i = 0; i < num_old; ++i)
|
||||
device_remove_device_set(old[i], path);
|
||||
|
||||
/* Add new devices */
|
||||
for (i = 0; i < num_new; ++i)
|
||||
device_add_device_set(new[i], path, 0);
|
||||
|
||||
/* Emit signals & update set leader */
|
||||
for (i = 0; i < num_old; ++i)
|
||||
spa_bt_device_emit_device_set_changed(old[i]);
|
||||
|
||||
if (num_new > 0)
|
||||
device_update_set_status(new[0], true, path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_update_device_sets_prop(struct spa_bt_device *device,
|
||||
DBusMessageIter *iter)
|
||||
{
|
||||
struct spa_bt_monitor *monitor = device->monitor;
|
||||
DBusMessageIter it[5];
|
||||
bool changed = false;
|
||||
|
||||
if (!check_iter_signature(iter, "a{oa{sv}}"))
|
||||
return -EINVAL;
|
||||
|
||||
dbus_message_iter_recurse(iter, &it[0]);
|
||||
|
||||
while (dbus_message_iter_get_arg_type(&it[0]) != DBUS_TYPE_INVALID) {
|
||||
uint8_t rank = 0;
|
||||
const char *set_path;
|
||||
|
||||
dbus_message_iter_recurse(&it[0], &it[1]);
|
||||
dbus_message_iter_get_basic(&it[1], &set_path);
|
||||
dbus_message_iter_next(&it[1]);
|
||||
dbus_message_iter_recurse(&it[1], &it[2]);
|
||||
|
||||
while (dbus_message_iter_get_arg_type(&it[2]) != DBUS_TYPE_INVALID) {
|
||||
const char *key;
|
||||
int type;
|
||||
|
||||
dbus_message_iter_recurse(&it[2], &it[3]);
|
||||
dbus_message_iter_get_basic(&it[3], &key);
|
||||
dbus_message_iter_next(&it[3]);
|
||||
dbus_message_iter_recurse(&it[3], &it[4]);
|
||||
|
||||
type = dbus_message_iter_get_arg_type(&it[4]);
|
||||
|
||||
if (spa_streq(key, "Rank") && type == DBUS_TYPE_BYTE)
|
||||
dbus_message_iter_get_basic(&it[4], &rank);
|
||||
|
||||
dbus_message_iter_next(&it[2]);
|
||||
}
|
||||
|
||||
spa_log_debug(monitor->log, "device %p: path %s device set %s rank %d",
|
||||
device, device->path, set_path, (int)rank);
|
||||
|
||||
/* Only add. Removals are handled in device set updates. */
|
||||
if (device_add_device_set(device, set_path, rank) == 1)
|
||||
changed = true;
|
||||
|
||||
dbus_message_iter_next(&it[0]);
|
||||
}
|
||||
|
||||
/* Emit change signals */
|
||||
device_update_set_status(device, changed, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_update_props(struct spa_bt_device *device,
|
||||
DBusMessageIter *props_iter,
|
||||
DBusMessageIter *invalidated_iter)
|
||||
|
|
@ -1911,6 +2209,9 @@ static int device_update_props(struct spa_bt_device *device,
|
|||
spa_bt_device_emit_profiles_changed(
|
||||
device, prev_profiles, device->connected_profiles);
|
||||
}
|
||||
else if (spa_streq(key, "Sets")) {
|
||||
device_update_device_sets_prop(device, &it[1]);
|
||||
}
|
||||
else
|
||||
spa_log_debug(monitor->log, "device %p: unhandled key %s type %d", device, key, type);
|
||||
|
||||
|
|
@ -2334,8 +2635,11 @@ void spa_bt_transport_free(struct spa_bt_transport *transport)
|
|||
spa_list_remove(&transport->device_link);
|
||||
}
|
||||
|
||||
if (device && device->connected_profiles != prev_connected)
|
||||
if (device && device->connected_profiles != prev_connected) {
|
||||
if ((prev_connected ^ device->connected_profiles) & SPA_BT_PROFILE_BAP_DUPLEX)
|
||||
device_update_set_status(device, true, NULL);
|
||||
spa_bt_device_emit_profiles_changed(device, device->profiles, prev_connected);
|
||||
}
|
||||
|
||||
spa_list_remove(&transport->bap_transport_linked);
|
||||
|
||||
|
|
@ -4774,6 +5078,9 @@ static void interface_added(struct spa_bt_monitor *monitor,
|
|||
* profile connection handlers can receive per-device settings during profile negotiation. */
|
||||
spa_bt_device_add_profile(d, SPA_BT_PROFILE_NULL);
|
||||
}
|
||||
else if (spa_streq(interface_name, BLUEZ_DEVICE_SET_INTERFACE)) {
|
||||
device_set_update_props(monitor, object_path, props_iter, NULL);
|
||||
}
|
||||
else if (spa_streq(interface_name, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) {
|
||||
struct spa_bt_remote_endpoint *ep;
|
||||
struct spa_bt_device *d;
|
||||
|
|
@ -4841,6 +5148,8 @@ static void interfaces_removed(struct spa_bt_monitor *monitor, DBusMessageIter *
|
|||
d = spa_bt_device_find(monitor, object_path);
|
||||
if (d != NULL)
|
||||
device_free(d);
|
||||
} else if (spa_streq(interface_name, BLUEZ_DEVICE_SET_INTERFACE)) {
|
||||
device_set_update_props(monitor, object_path, NULL, NULL);
|
||||
} else if (spa_streq(interface_name, BLUEZ_ADAPTER_INTERFACE) ||
|
||||
spa_streq(interface_name, BLUEZ_MEDIA_INTERFACE)) {
|
||||
struct spa_bt_adapter *a;
|
||||
|
|
@ -5083,6 +5392,9 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
|
|||
|
||||
spa_bt_device_add_profile(d, SPA_BT_PROFILE_NULL);
|
||||
}
|
||||
else if (spa_streq(iface, BLUEZ_DEVICE_SET_INTERFACE)) {
|
||||
device_set_update_props(monitor, path, &it[1], NULL);
|
||||
}
|
||||
else if (spa_streq(iface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) {
|
||||
struct spa_bt_remote_endpoint *ep;
|
||||
struct spa_bt_device *d;
|
||||
|
|
@ -5171,6 +5483,10 @@ static void add_filters(struct spa_bt_monitor *this)
|
|||
"type='signal',sender='" BLUEZ_SERVICE "',"
|
||||
"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
|
||||
"arg0='" BLUEZ_DEVICE_INTERFACE "'", &err);
|
||||
dbus_bus_add_match(this->conn,
|
||||
"type='signal',sender='" BLUEZ_SERVICE "',"
|
||||
"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
|
||||
"arg0='" BLUEZ_DEVICE_SET_INTERFACE "'", &err);
|
||||
dbus_bus_add_match(this->conn,
|
||||
"type='signal',sender='" BLUEZ_SERVICE "',"
|
||||
"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ extern "C" {
|
|||
#define BLUEZ_PROFILE_INTERFACE BLUEZ_SERVICE ".Profile1"
|
||||
#define BLUEZ_ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter1"
|
||||
#define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device1"
|
||||
#define BLUEZ_DEVICE_SET_INTERFACE BLUEZ_SERVICE ".DeviceSet1"
|
||||
#define BLUEZ_MEDIA_INTERFACE BLUEZ_SERVICE ".Media1"
|
||||
#define BLUEZ_MEDIA_ENDPOINT_INTERFACE BLUEZ_SERVICE ".MediaEndpoint1"
|
||||
#define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
|
||||
|
|
@ -440,12 +441,27 @@ struct spa_bt_device_events {
|
|||
/** Profile configuration changed */
|
||||
void (*profiles_changed) (void *data, uint32_t prev_profiles, uint32_t prev_connected);
|
||||
|
||||
/** Device set configuration changed */
|
||||
void (*device_set_changed) (void *data);
|
||||
|
||||
/** Device freed */
|
||||
void (*destroy) (void *data);
|
||||
};
|
||||
|
||||
struct media_codec;
|
||||
|
||||
struct spa_bt_set_membership {
|
||||
struct spa_list link;
|
||||
struct spa_list others;
|
||||
struct spa_bt_device *device;
|
||||
char *path;
|
||||
uint8_t rank;
|
||||
bool leader;
|
||||
};
|
||||
|
||||
#define spa_bt_for_each_set_member(s, set) \
|
||||
for ((s) = (set); (s); (s) = spa_list_next((s), others), (s) = (s) != (set) ? (s) : NULL)
|
||||
|
||||
struct spa_bt_device {
|
||||
struct spa_list link;
|
||||
struct spa_bt_monitor *monitor;
|
||||
|
|
@ -477,6 +493,7 @@ struct spa_bt_device {
|
|||
struct spa_list remote_endpoint_list;
|
||||
struct spa_list transport_list;
|
||||
struct spa_list codec_switch_list;
|
||||
struct spa_list set_membership_list;
|
||||
uint8_t battery;
|
||||
int has_battery;
|
||||
|
||||
|
|
@ -518,6 +535,7 @@ void spa_bt_device_update_last_bluez_action_time(struct spa_bt_device *device);
|
|||
#define spa_bt_device_emit_connected(d,...) spa_bt_device_emit(d, connected, 0, __VA_ARGS__)
|
||||
#define spa_bt_device_emit_codec_switched(d,...) spa_bt_device_emit(d, codec_switched, 0, __VA_ARGS__)
|
||||
#define spa_bt_device_emit_profiles_changed(d,...) spa_bt_device_emit(d, profiles_changed, 0, __VA_ARGS__)
|
||||
#define spa_bt_device_emit_device_set_changed(d) spa_bt_device_emit(d, device_set_changed, 0)
|
||||
#define spa_bt_device_emit_destroy(d) spa_bt_device_emit(d, destroy, 0)
|
||||
#define spa_bt_device_add_listener(d,listener,events,data) \
|
||||
spa_hook_list_append(&(d)->listener_list, listener, events, data)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue