summaryrefslogtreecommitdiffstats
path: root/src/modules/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/bluetooth')
-rw-r--r--src/modules/bluetooth/bluetooth-util.c91
-rw-r--r--src/modules/bluetooth/bluetooth-util.h3
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c49
3 files changed, 107 insertions, 36 deletions
diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 5c7681d4..16c29248 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -122,9 +122,9 @@ static pa_bool_t device_is_audio(pa_bluetooth_device *d) {
return
d->device_info_valid &&
- (d->audio_state != PA_BT_AUDIO_STATE_INVALID ||
- d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID ||
- d->headset_state != PA_BT_AUDIO_STATE_INVALID);
+ (d->audio_state != PA_BT_AUDIO_STATE_INVALID &&
+ (d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID ||
+ d->headset_state != PA_BT_AUDIO_STATE_INVALID));
}
static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessageIter *i) {
@@ -226,10 +226,6 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
node = uuid_new(value);
PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node);
- /* this might eventually be racy if .Audio is not there yet, but the State change will come anyway later, so this call is for cold-detection mostly */
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties"));
- send_and_add_to_pending(y, d, m, get_properties_reply);
-
/* Vudentz said the interfaces are here when the UUIDs are announced */
if (strcasecmp(HSP_HS_UUID, value) == 0 || strcasecmp(HFP_HS_UUID, value) == 0) {
pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Headset", "GetProperties"));
@@ -239,6 +235,10 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
send_and_add_to_pending(y, d, m, get_properties_reply);
}
+ /* this might eventually be racy if .Audio is not there yet, but the State change will come anyway later, so this call is for cold-detection mostly */
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
+
if (!dbus_message_iter_next(&ai))
break;
}
@@ -309,6 +309,17 @@ static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_b
pa_hook_fire(&y->hook, d);
}
+static void remove_all_devices(pa_bluetooth_discovery *y) {
+ pa_bluetooth_device *d;
+
+ pa_assert(y);
+
+ while ((d = pa_hashmap_steal_first(y->devices))) {
+ run_callback(y, d, TRUE);
+ device_free(d);
+ }
+}
+
static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
DBusMessage *r;
DBusMessageIter arg_i, element_i;
@@ -332,6 +343,12 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties"))
d->device_info_valid = valid;
+ if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
+ pa_log_debug("Bluetooth daemon is apparently not available.");
+ remove_all_devices(y);
+ goto finish2;
+ }
+
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
if (!dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD))
@@ -383,6 +400,7 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
finish:
run_callback(y, d, FALSE);
+finish2:
dbus_message_unref(r);
PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
@@ -412,6 +430,9 @@ static void found_device(pa_bluetooth_discovery *y, const char* path) {
pa_assert(y);
pa_assert(path);
+ if (pa_hashmap_get(y->devices, path))
+ return;
+
d = device_new(path);
pa_hashmap_put(y->devices, d->path, d);
@@ -439,9 +460,15 @@ static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
pa_assert_se(y = p->context_data);
pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+ if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
+ pa_log_debug("Bluetooth daemon is apparently not available.");
+ remove_all_devices(y);
+ goto finish;
+ }
+
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r));
- goto end;
+ goto finish;
}
if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
@@ -454,7 +481,7 @@ static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
found_device(y, paths[i]);
}
-end:
+finish:
if (paths)
dbus_free_string_array (paths);
@@ -487,9 +514,15 @@ static void list_adapters_reply(DBusPendingCall *pending, void *userdata) {
pa_assert_se(y = p->context_data);
pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+ if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
+ pa_log_debug("Bluetooth daemon is apparently not available.");
+ remove_all_devices(y);
+ goto finish;
+ }
+
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r));
- goto end;
+ goto finish;
}
if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
@@ -502,7 +535,7 @@ static void list_adapters_reply(DBusPendingCall *pending, void *userdata) {
found_adapter(y, paths[i]);
}
-end:
+finish:
if (paths)
dbus_free_string_array (paths);
@@ -616,6 +649,32 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ } else if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
+ const char *name, *old_owner, *new_owner;
+
+ if (!dbus_message_get_args(m, &err,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old_owner,
+ DBUS_TYPE_STRING, &new_owner,
+ DBUS_TYPE_INVALID)) {
+ pa_log("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message);
+ goto fail;
+ }
+
+ if (pa_streq(name, "org.bluez")) {
+ if (old_owner && *old_owner) {
+ pa_log_debug("Bluetooth daemon disappeared.");
+ remove_all_devices(y);
+ }
+
+ if (new_owner && *new_owner) {
+ pa_log_debug("Bluetooth daemon appeared.");
+ list_adapters(y);
+ }
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
fail:
@@ -699,6 +758,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
if (pa_dbus_add_matches(
pa_dbus_connection_get(y->connection), &err,
+ "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'",
"type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
@@ -734,8 +794,6 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
}
void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
- pa_bluetooth_device *d;
-
pa_assert(y);
pa_assert(PA_REFCNT_VALUE(y) > 0);
@@ -745,16 +803,13 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
pa_dbus_free_pending_list(&y->pending);
if (y->devices) {
- while ((d = pa_hashmap_steal_first(y->devices))) {
- run_callback(y, d, TRUE);
- device_free(d);
- }
-
+ remove_all_devices(y);
pa_hashmap_free(y->devices, NULL, NULL);
}
if (y->connection) {
pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
+ "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'",
"type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
"type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'",
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 265caf40..f15f2170 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -59,8 +59,7 @@ typedef enum pa_bt_audio_state {
PA_BT_AUDIO_STATE_DISCONNECTED,
PA_BT_AUDIO_STATE_CONNECTING,
PA_BT_AUDIO_STATE_CONNECTED,
- PA_BT_AUDIO_STATE_PLAYING,
- PA_BT_AUDIO_STATE_LAST
+ PA_BT_AUDIO_STATE_PLAYING
} pa_bt_audio_state_t;
struct pa_bluetooth_device {
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index e7c6d5e4..d6321fc4 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -881,7 +881,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
*((pa_usec_t*) data) = wi > ri ? wi - ri : 0;
}
- *((pa_usec_t*) data) += u->sink->fixed_latency;
+ *((pa_usec_t*) data) += u->sink->thread_info.fixed_latency;
return 0;
}
}
@@ -943,7 +943,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
- *((pa_usec_t*) data) = (wi > ri ? wi - ri : 0) + u->source->fixed_latency;
+ *((pa_usec_t*) data) = (wi > ri ? wi - ri : 0) + u->source->thread_info.fixed_latency;
return 0;
}
@@ -1262,11 +1262,11 @@ static void thread_func(void *userdata) {
if (u->core->realtime_scheduling)
pa_make_realtime(u->core->realtime_priority);
+ pa_thread_mq_install(&u->thread_mq);
+
if (start_stream_fd(u) < 0)
goto fail;
- pa_thread_mq_install(&u->thread_mq);
-
for (;;) {
struct pollfd *pollfd;
int ret;
@@ -1319,18 +1319,21 @@ static void thread_func(void *userdata) {
if (u->write_index > 0 && audio_to_send > MAX_PLAYBACK_CATCH_UP_USEC) {
pa_usec_t skip_usec;
uint64_t skip_bytes;
- pa_memchunk tmp;
skip_usec = audio_to_send - MAX_PLAYBACK_CATCH_UP_USEC;
skip_bytes = pa_usec_to_bytes(skip_usec, &u->sample_spec);
- pa_log_warn("Skipping %llu us (= %llu bytes) in audio stream",
- (unsigned long long) skip_usec,
- (unsigned long long) skip_bytes);
+ if (skip_bytes > 0) {
+ pa_memchunk tmp;
+
+ pa_log_warn("Skipping %llu us (= %llu bytes) in audio stream",
+ (unsigned long long) skip_usec,
+ (unsigned long long) skip_bytes);
- pa_sink_render_full(u->sink, skip_bytes, &tmp);
- pa_memblock_unref(tmp.memblock);
- u->write_index += skip_bytes;
+ pa_sink_render_full(u->sink, skip_bytes, &tmp);
+ pa_memblock_unref(tmp.memblock);
+ u->write_index += skip_bytes;
+ }
}
do_write = 1;
@@ -1351,11 +1354,14 @@ static void thread_func(void *userdata) {
goto fail;
}
+ if (n_written == 0)
+ pa_log("Broken kernel: we got EAGAIN on write() after POLLOUT!");
+
do_write -= n_written;
writable = FALSE;
}
- if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && do_write <= 0) {
+ if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && do_write <= 0 && writable) {
pa_usec_t time_passed, next_write_at, sleep_for;
/* Hmm, there is no input stream we could synchronize
@@ -1443,12 +1449,12 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
if (u->sink && dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged")) {
pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
- pa_sink_volume_changed(u->sink, &v, TRUE);
+ pa_sink_volume_changed(u->sink, &v);
} else if (u->source && dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) {
pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
- pa_source_volume_changed(u->source, &v, TRUE);
+ pa_source_volume_changed(u->source, &v);
}
}
}
@@ -1733,7 +1739,8 @@ static void shutdown_bt(struct userdata *u) {
if (u->service_fd >= 0) {
pa_close(u->service_fd);
u->service_fd = -1;
- u->service_write_type = u->service_write_type = 0;
+ u->service_write_type = 0;
+ u->service_read_type = 0;
}
if (u->write_memchunk.memblock) {
@@ -1749,7 +1756,8 @@ static int init_bt(struct userdata *u) {
shutdown_bt(u);
u->stream_write_type = 0;
- u->service_write_type = u->service_write_type = 0;
+ u->service_write_type = 0;
+ u->service_read_type = 0;
if ((u->service_fd = bt_audio_service_open()) < 0) {
pa_log_error("Couldn't connect to bluetooth audio service");
@@ -2083,6 +2091,15 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) {
u->card->set_profile = card_set_profile;
d = PA_CARD_PROFILE_DATA(u->card->active_profile);
+
+ if ((device->headset_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HSP) ||
+ (device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP)) {
+ pa_log_warn("Default profile not connected, selecting off profile");
+ u->card->active_profile = pa_hashmap_get(u->card->profiles, "off");
+ u->card->save_profile = FALSE;
+ }
+
+ d = PA_CARD_PROFILE_DATA(u->card->active_profile);
u->profile = *d;
return 0;