mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
bluez5: create device handle before profile negotiation started so that profile handler can retrieve per-device settings
This commit is contained in:
parent
e7cecaaea6
commit
af8272fe08
5 changed files with 140 additions and 80 deletions
|
|
@ -108,6 +108,7 @@ extern "C" {
|
|||
#define SPA_KEY_API_BLUEZ5 "api.bluez5" /**< key for the bluez5 api */
|
||||
#define SPA_KEY_API_BLUEZ5_PATH "api.bluez5.path" /**< a bluez5 path */
|
||||
#define SPA_KEY_API_BLUEZ5_DEVICE "api.bluez5.device" /**< an internal bluez5 device */
|
||||
#define SPA_KEY_API_BLUEZ5_CONNECTION "api.bluez5.connection" /**< bluez5 device connection status */
|
||||
#define SPA_KEY_API_BLUEZ5_TRANSPORT "api.bluez5.transport" /**< an internal bluez5 transport */
|
||||
#define SPA_KEY_API_BLUEZ5_PROFILE "api.bluez5.profile" /**< a bluetooth profile */
|
||||
#define SPA_KEY_API_BLUEZ5_ADDRESS "api.bluez5.address" /**< a bluetooth address */
|
||||
|
|
|
|||
|
|
@ -136,6 +136,10 @@ struct spa_bt_a2dp_codec_switch {
|
|||
size_t num_paths;
|
||||
};
|
||||
|
||||
#define BT_DEVICE_DISCONNECTED 0
|
||||
#define BT_DEVICE_CONNECTED 1
|
||||
#define BT_DEVICE_INIT -1
|
||||
|
||||
/*
|
||||
* SCO socket connect may fail with ECONNABORTED if it is done too soon after
|
||||
* previous close. To avoid this in cases where nodes are toggled between
|
||||
|
|
@ -640,8 +644,6 @@ static struct spa_bt_device *device_create(struct spa_bt_monitor *monitor, const
|
|||
|
||||
static int device_stop_timer(struct spa_bt_device *device);
|
||||
|
||||
static int device_remove(struct spa_bt_monitor *monitor, struct spa_bt_device *device);
|
||||
|
||||
static void a2dp_codec_switch_free(struct spa_bt_a2dp_codec_switch *sw);
|
||||
|
||||
static void device_free(struct spa_bt_device *device)
|
||||
|
|
@ -662,7 +664,10 @@ static void device_free(struct spa_bt_device *device)
|
|||
|
||||
battery_remove(device);
|
||||
device_stop_timer(device);
|
||||
device_remove(monitor, device);
|
||||
|
||||
if (device->added) {
|
||||
spa_device_emit_object_info(&monitor->hooks, device->id, NULL);
|
||||
}
|
||||
|
||||
spa_list_for_each_safe(ep, tep, &device->remote_endpoint_list, device_link) {
|
||||
if (ep->device == device) {
|
||||
|
|
@ -691,15 +696,38 @@ static void device_free(struct spa_bt_device *device)
|
|||
free(device);
|
||||
}
|
||||
|
||||
static int device_add(struct spa_bt_monitor *monitor, struct spa_bt_device *device)
|
||||
static int device_connected(struct spa_bt_monitor *monitor, struct spa_bt_device *device, int status)
|
||||
{
|
||||
struct spa_device_object_info info;
|
||||
char dev[32], name[128], class[16];
|
||||
struct spa_dict_item items[20];
|
||||
uint32_t n_items = 0;
|
||||
bool connection_changed, init = status == BT_DEVICE_INIT;
|
||||
|
||||
if (device->connected_profiles == 0 || device->added)
|
||||
status = init ? false : status;
|
||||
connection_changed = status ^ device->connected;
|
||||
device->connected = status;
|
||||
|
||||
if (init) {
|
||||
device->added = true;
|
||||
} else if (!device->added || !connection_changed) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((device->connected_profiles != 0) ^ device->connected) {
|
||||
spa_log_error(monitor->log,
|
||||
"unexpected call, connected_profiles:%08x connected:%d",
|
||||
device->connected_profiles, device->connected);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!init) {
|
||||
spa_bt_device_emit_connected(device, device->connected);
|
||||
if (!device->connected) {
|
||||
battery_remove(device);
|
||||
spa_bt_device_release_transports(device);
|
||||
}
|
||||
}
|
||||
|
||||
info = SPA_DEVICE_OBJECT_INFO_INIT();
|
||||
info.type = SPA_TYPE_INTERFACE_Device;
|
||||
|
|
@ -724,29 +752,20 @@ static int device_add(struct spa_bt_monitor *monitor, struct spa_bt_device *devi
|
|||
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_DEVICE, dev);
|
||||
snprintf(class, sizeof(class), "0x%06x", device->bluetooth_class);
|
||||
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_CLASS, class);
|
||||
items[n_items++] = SPA_DICT_ITEM_INIT(
|
||||
SPA_KEY_API_BLUEZ5_CONNECTION,
|
||||
device->connected ? "connected": "disconnected");
|
||||
|
||||
info.props = &SPA_DICT_INIT(items, n_items);
|
||||
|
||||
device->added = true;
|
||||
spa_device_emit_object_info(&monitor->hooks, device->id, &info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_remove(struct spa_bt_monitor *monitor, struct spa_bt_device *device)
|
||||
{
|
||||
if (!device->added)
|
||||
return 0;
|
||||
|
||||
battery_remove(device);
|
||||
|
||||
device->added = false;
|
||||
spa_device_emit_object_info(&monitor->hooks, device->id, NULL);
|
||||
if(!init && !device->connected) {
|
||||
spa_device_emit_object_info(&monitor->hooks, device->id, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define DEVICE_PROFILE_TIMEOUT_SEC 3
|
||||
|
||||
static void device_timer_event(struct spa_source *source)
|
||||
|
|
@ -761,7 +780,7 @@ static void device_timer_event(struct spa_source *source)
|
|||
spa_log_debug(monitor->log, "device %p: timeout %08x %08x",
|
||||
device, device->profiles, device->connected_profiles);
|
||||
|
||||
device_add(device->monitor, device);
|
||||
device_connected(device->monitor, device, BT_DEVICE_CONNECTED);
|
||||
}
|
||||
|
||||
static int device_start_timer(struct spa_bt_device *device)
|
||||
|
|
@ -821,13 +840,11 @@ int spa_bt_device_check_profiles(struct spa_bt_device *device, bool force)
|
|||
device, device->profiles, connected_profiles, device->added);
|
||||
|
||||
if (connected_profiles == 0 && spa_list_is_empty(&device->codec_switch_list)) {
|
||||
if (device->added) {
|
||||
device_stop_timer(device);
|
||||
device_remove(monitor, device);
|
||||
}
|
||||
device_stop_timer(device);
|
||||
device_connected(monitor, device, BT_DEVICE_DISCONNECTED);
|
||||
} else if (force || (device->profiles & connected_profiles) == device->profiles) {
|
||||
device_stop_timer(device);
|
||||
device_add(monitor, device);
|
||||
device_connected(monitor, device, BT_DEVICE_CONNECTED);
|
||||
} else {
|
||||
device_start_timer(device);
|
||||
}
|
||||
|
|
@ -841,14 +858,11 @@ static void device_set_connected(struct spa_bt_device *device, int connected)
|
|||
if (device->connected && !connected)
|
||||
device->connected_profiles = 0;
|
||||
|
||||
device->connected = connected;
|
||||
|
||||
if (connected)
|
||||
spa_bt_device_check_profiles(device, false);
|
||||
else {
|
||||
device_stop_timer(device);
|
||||
if (device->added)
|
||||
device_remove(monitor, device);
|
||||
device_connected(monitor, device, connected);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1158,6 +1172,8 @@ static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_en
|
|||
else if (strcmp(key, "Device") == 0) {
|
||||
struct spa_bt_device *device;
|
||||
device = spa_bt_device_find(monitor, value);
|
||||
if (device == NULL)
|
||||
goto next;
|
||||
spa_log_debug(monitor->log, "remote_endpoint %p: device -> %p", remote_endpoint, device);
|
||||
|
||||
if (remote_endpoint->device != device) {
|
||||
|
|
@ -2737,16 +2753,25 @@ static void interface_added(struct spa_bt_monitor *monitor,
|
|||
else if (strcmp(interface_name, BLUEZ_DEVICE_INTERFACE) == 0) {
|
||||
struct spa_bt_device *d;
|
||||
|
||||
d = spa_bt_device_find(monitor, object_path);
|
||||
spa_assert(spa_bt_device_find(monitor, object_path) == NULL);
|
||||
|
||||
d = device_create(monitor, object_path);
|
||||
if (d == NULL) {
|
||||
d = device_create(monitor, object_path);
|
||||
if (d == NULL) {
|
||||
spa_log_warn(monitor->log, "can't create Bluetooth device %s: %m",
|
||||
object_path);
|
||||
return;
|
||||
}
|
||||
spa_log_warn(monitor->log, "can't create Bluetooth device %s: %m",
|
||||
object_path);
|
||||
return;
|
||||
}
|
||||
|
||||
device_update_props(d, props_iter, NULL);
|
||||
/* We only care about audio devices. */
|
||||
if (d->profiles == 0) {
|
||||
device_free(d);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Trigger bluez device creation before bluez profile negotiation started so that
|
||||
* profile connection handlers can receive per-device settings during profile negotiation. */
|
||||
device_connected(monitor, d, BT_DEVICE_INIT);
|
||||
}
|
||||
else if (strcmp(interface_name, BLUEZ_MEDIA_ENDPOINT_INTERFACE) == 0) {
|
||||
struct spa_bt_remote_endpoint *ep;
|
||||
|
|
|
|||
|
|
@ -410,8 +410,20 @@ static void profiles_changed(void *userdata, uint32_t prev_profiles, uint32_t pr
|
|||
emit_info(this, false);
|
||||
}
|
||||
|
||||
static void set_initial_profile(struct impl *this);
|
||||
|
||||
static void device_connected(void *userdata, bool connected) {
|
||||
struct impl *this = userdata;
|
||||
|
||||
spa_log_debug(this->log, "connected: %d", connected);
|
||||
|
||||
if (connected ^ (this->profile != 0))
|
||||
set_initial_profile(this);
|
||||
}
|
||||
|
||||
static const struct spa_bt_device_events bt_dev_events = {
|
||||
SPA_VERSION_BT_DEVICE_EVENTS,
|
||||
.connected = device_connected,
|
||||
.codec_switched = codec_switched,
|
||||
.profiles_changed = profiles_changed,
|
||||
};
|
||||
|
|
@ -540,6 +552,11 @@ static void set_initial_profile(struct impl *this)
|
|||
struct spa_bt_transport *t;
|
||||
int i;
|
||||
|
||||
if (this->supported_codecs)
|
||||
free(this->supported_codecs);
|
||||
this->supported_codecs = spa_bt_device_get_supported_a2dp_codecs(
|
||||
this->bt_dev, &this->supported_codec_count);
|
||||
|
||||
/* Prefer A2DP, then HFP, then null */
|
||||
|
||||
for (i = SPA_BT_PROFILE_A2DP_SINK; i <= SPA_BT_PROFILE_A2DP_SOURCE; i <<= 1) {
|
||||
|
|
@ -1248,8 +1265,6 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
this->info.params = this->params;
|
||||
this->info.n_params = 4;
|
||||
|
||||
this->supported_codecs = spa_bt_device_get_supported_a2dp_codecs(this->bt_dev, &this->supported_codec_count);
|
||||
|
||||
spa_bt_device_add_listener(this->bt_dev, &this->bt_dev_listener, &bt_dev_events, this);
|
||||
|
||||
set_initial_profile(this);
|
||||
|
|
|
|||
|
|
@ -375,6 +375,9 @@ struct spa_bt_device_events {
|
|||
#define SPA_VERSION_BT_DEVICE_EVENTS 0
|
||||
uint32_t version;
|
||||
|
||||
/** Device connection status */
|
||||
void (*connected) (void *data, bool connected);
|
||||
|
||||
/** Codec switching completed */
|
||||
void (*codec_switched) (void *data, int status);
|
||||
|
||||
|
|
@ -430,6 +433,7 @@ int spa_bt_device_report_battery_level(struct spa_bt_device *device, uint8_t per
|
|||
#define spa_bt_device_emit(d,m,v,...) spa_hook_list_call(&(d)->listener_list, \
|
||||
struct spa_bt_device_events, \
|
||||
m, v, ##__VA_ARGS__)
|
||||
#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_add_listener(d,listener,events,data) \
|
||||
|
|
|
|||
|
|
@ -344,18 +344,6 @@ static int update_device_props(struct device *device)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void bluez5_update_device(struct impl *impl, struct device *dev,
|
||||
const struct spa_device_object_info *info)
|
||||
{
|
||||
pw_log_debug("update device %u", dev->id);
|
||||
|
||||
if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG))
|
||||
spa_debug_dict(0, info->props);
|
||||
|
||||
pw_properties_update(dev->props, info->props);
|
||||
update_device_props(dev);
|
||||
}
|
||||
|
||||
static void device_destroy(void *data)
|
||||
{
|
||||
struct device *device = data;
|
||||
|
|
@ -363,27 +351,15 @@ static void device_destroy(void *data)
|
|||
|
||||
pw_log_debug("device %p destroy", device);
|
||||
|
||||
if (device->appeared) {
|
||||
device->appeared = false;
|
||||
spa_hook_remove(&device->device_listener);
|
||||
}
|
||||
|
||||
spa_list_consume(node, &device->node_list, link)
|
||||
bluez5_remove_node(device, node);
|
||||
}
|
||||
|
||||
static void device_free(void *data)
|
||||
{
|
||||
struct device *device = data;
|
||||
|
||||
pw_log_debug("remove device %u", device->id);
|
||||
spa_list_remove(&device->link);
|
||||
spa_hook_remove(&device->listener);
|
||||
if (device->appeared)
|
||||
spa_hook_remove(&device->device_listener);
|
||||
|
||||
sm_object_discard(&device->sdevice->obj);
|
||||
|
||||
pw_unload_spa_handle(device->handle);
|
||||
pw_properties_free(device->props);
|
||||
free(device);
|
||||
}
|
||||
|
||||
static void device_update(void *data)
|
||||
{
|
||||
struct device *device = data;
|
||||
|
|
@ -406,7 +382,6 @@ static void device_update(void *data)
|
|||
static const struct sm_object_events device_events = {
|
||||
SM_VERSION_OBJECT_EVENTS,
|
||||
.destroy = device_destroy,
|
||||
.free = device_free,
|
||||
.update = device_update,
|
||||
};
|
||||
|
||||
|
|
@ -460,16 +435,6 @@ static struct device *bluez5_create_device(struct impl *impl, uint32_t id,
|
|||
|
||||
device->handle = handle;
|
||||
device->device = iface;
|
||||
device->sdevice = sm_media_session_export_device(impl->session,
|
||||
&device->props->dict, device->device);
|
||||
if (device->sdevice == NULL) {
|
||||
res = -errno;
|
||||
goto unload_handle;
|
||||
}
|
||||
|
||||
sm_object_add_listener(&device->sdevice->obj,
|
||||
&device->listener,
|
||||
&device_events, device);
|
||||
|
||||
spa_list_append(&impl->device_list, &device->link);
|
||||
|
||||
|
|
@ -487,7 +452,53 @@ exit:
|
|||
|
||||
static void bluez5_remove_device(struct impl *impl, struct device *device)
|
||||
{
|
||||
sm_object_destroy(&device->sdevice->obj);
|
||||
|
||||
pw_log_debug("remove device %u", device->id);
|
||||
|
||||
if (device->sdevice) {
|
||||
sm_object_destroy(&device->sdevice->obj);
|
||||
device->sdevice = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void bluez5_update_device(struct impl *impl, struct device *device,
|
||||
const struct spa_device_object_info *info)
|
||||
{
|
||||
bool connected;
|
||||
const char *str;
|
||||
if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG))
|
||||
spa_debug_dict(0, info->props);
|
||||
|
||||
pw_log_debug("update device %u", device->id);
|
||||
|
||||
pw_properties_update(device->props, info->props);
|
||||
update_device_props(device);
|
||||
|
||||
str = spa_dict_lookup(info->props, SPA_KEY_API_BLUEZ5_CONNECTION);
|
||||
connected = str != NULL && strcmp(str, "connected") == 0;
|
||||
|
||||
/* Export device after bluez profiles get connected */
|
||||
if (device->sdevice == NULL && connected) {
|
||||
device->sdevice = sm_media_session_export_device(impl->session,
|
||||
&device->props->dict, device->device);
|
||||
if (device->sdevice == NULL) {
|
||||
bluez5_remove_device(impl, device);
|
||||
return;
|
||||
}
|
||||
|
||||
sm_object_add_listener(&device->sdevice->obj,
|
||||
&device->listener,
|
||||
&device_events, device);
|
||||
}
|
||||
}
|
||||
|
||||
static void bluez5_device_free(struct impl *impl, struct device *device)
|
||||
{
|
||||
bluez5_remove_device(impl, device);
|
||||
spa_list_remove(&device->link);
|
||||
pw_unload_spa_handle(device->handle);
|
||||
pw_properties_free(device->props);
|
||||
free(device);
|
||||
}
|
||||
|
||||
static void bluez5_enum_object_info(void *data, uint32_t id,
|
||||
|
|
@ -519,6 +530,10 @@ static const struct spa_device_events bluez5_enum_callbacks =
|
|||
static void session_destroy(void *data)
|
||||
{
|
||||
struct impl *impl = data;
|
||||
struct device *device;
|
||||
spa_list_consume(device, &impl->device_list, link)
|
||||
bluez5_device_free(impl, device);
|
||||
|
||||
spa_hook_remove(&impl->session_listener);
|
||||
spa_hook_remove(&impl->listener);
|
||||
pw_unload_spa_handle(impl->handle);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue