summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/modules/bluetooth/backend-ofono.c4
-rw-r--r--src/modules/bluetooth/module-bluez5-device.c101
2 files changed, 86 insertions, 19 deletions
diff --git a/src/modules/bluetooth/backend-ofono.c b/src/modules/bluetooth/backend-ofono.c
index c39c6ce2e..6e9a3664e 100644
--- a/src/modules/bluetooth/backend-ofono.c
+++ b/src/modules/bluetooth/backend-ofono.c
@@ -161,7 +161,7 @@ static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool opti
DBusError derr;
if (card->connecting)
- return -1;
+ return -EAGAIN;
card->connecting = true;
@@ -172,7 +172,7 @@ static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool opti
return -1;
if (card->connecting)
- return -1;
+ return -EAGAIN;
}
/* The correct block size should take into account the SCO MTU from
diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
index 2f0ec9762..867def742 100644
--- a/src/modules/bluetooth/module-bluez5-device.c
+++ b/src/modules/bluetooth/module-bluez5-device.c
@@ -80,6 +80,14 @@ enum {
BLUETOOTH_MESSAGE_MAX
};
+enum {
+ PA_SOURCE_MESSAGE_SETUP_STREAM = PA_SOURCE_MESSAGE_MAX,
+};
+
+enum {
+ PA_SINK_MESSAGE_SETUP_STREAM = PA_SINK_MESSAGE_MAX,
+};
+
typedef struct bluetooth_msg {
pa_msgobject parent;
pa_card *card;
@@ -112,6 +120,7 @@ struct userdata {
pa_bluetooth_device *device;
pa_bluetooth_transport *transport;
bool transport_acquired;
+ bool stream_setup_done;
pa_card *card;
pa_sink *sink;
@@ -731,6 +740,7 @@ static void teardown_stream(struct userdata *u) {
}
pa_log_debug("Audio stream torn down");
+ u->stream_setup_done = false;
}
static int transport_acquire(struct userdata *u, bool optional) {
@@ -743,7 +753,7 @@ static int transport_acquire(struct userdata *u, bool optional) {
u->stream_fd = u->transport->acquire(u->transport, optional, &u->read_link_mtu, &u->write_link_mtu);
if (u->stream_fd < 0)
- return -1;
+ return u->stream_fd;
u->transport_acquired = true;
pa_log_info("Transport %s acquired: fd %d", u->transport->path, u->stream_fd);
@@ -765,6 +775,11 @@ static void transport_release(struct userdata *u) {
u->transport_acquired = false;
teardown_stream(u);
+
+ /* Set transport state to idle if this was not already done by the remote end closing
+ * the file descriptor. Only do this when called from the I/O thread */
+ if (pa_thread_mq_get() != NULL && u->transport->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING)
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), BLUETOOTH_MESSAGE_STREAM_FD_HUP, NULL, 0, NULL, NULL);
}
/* Run from I/O thread */
@@ -802,6 +817,10 @@ static void setup_stream(struct userdata *u) {
struct pollfd *pollfd;
int one;
+ /* return if stream is already set up */
+ if (u->stream_setup_done)
+ return;
+
pa_log_info("Transport %s resuming", u->transport->path);
transport_config_mtu(u);
@@ -825,11 +844,26 @@ static void setup_stream(struct userdata *u) {
u->read_index = u->write_index = 0;
u->started_at = 0;
+ u->stream_setup_done = true;
if (u->source)
u->read_smoother = pa_smoother_new(PA_USEC_PER_SEC, 2*PA_USEC_PER_SEC, true, true, 10, pa_rtclock_now(), true);
}
+/* Called from I/O thread, returns true if the transport was acquired or
+ * a connection was requested successfully. */
+static bool setup_transport_and_stream(struct userdata *u) {
+ int transport_error;
+
+ transport_error = transport_acquire(u, false);
+ if (transport_error < 0) {
+ if (transport_error != -EAGAIN)
+ return false;
+ } else
+ setup_stream(u);
+ return true;
+}
+
/* Run from IO thread */
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SOURCE(o)->userdata;
@@ -865,12 +899,8 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
break;
/* Resume the device if the sink was suspended as well */
- if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
- if (transport_acquire(u, false) < 0)
- failed = true;
- else
- setup_stream(u);
- }
+ if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ failed = !setup_transport_and_stream(u);
/* We don't resume the smoother here. Instead we
* wait until the first packet arrives */
@@ -898,6 +928,11 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
return 0;
}
+
+ case PA_SOURCE_MESSAGE_SETUP_STREAM:
+ setup_stream(u);
+ return 0;
+
}
r = pa_source_process_msg(o, code, data, offset, chunk);
@@ -970,8 +1005,15 @@ static int add_source(struct userdata *u) {
case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
data.suspend_cause = PA_SUSPEND_USER;
break;
- case PA_BLUETOOTH_PROFILE_A2DP_SINK:
case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+ /* u->stream_fd contains the error returned by the last transport_acquire()
+ * EAGAIN means we are waiting for a NewConnection signal */
+ if (u->stream_fd == -EAGAIN)
+ data.suspend_cause = PA_SUSPEND_USER;
+ else
+ pa_assert_not_reached();
+ break;
+ case PA_BLUETOOTH_PROFILE_A2DP_SINK:
case PA_BLUETOOTH_PROFILE_OFF:
pa_assert_not_reached();
break;
@@ -1029,12 +1071,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
break;
/* Resume the device if the source was suspended as well */
- if (!u->source || !PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
- if (transport_acquire(u, false) < 0)
- failed = true;
- else
- setup_stream(u);
- }
+ if (!u->source || !PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+ failed = !setup_transport_and_stream(u);
break;
@@ -1061,6 +1099,10 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return 0;
}
+
+ case PA_SINK_MESSAGE_SETUP_STREAM:
+ setup_stream(u);
+ return 0;
}
r = pa_sink_process_msg(o, code, data, offset, chunk);
@@ -1132,10 +1174,17 @@ static int add_sink(struct userdata *u) {
case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
data.suspend_cause = PA_SUSPEND_USER;
break;
+ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
+ /* u->stream_fd contains the error returned by the last transport_acquire()
+ * EAGAIN means we are waiting for a NewConnection signal */
+ if (u->stream_fd == -EAGAIN)
+ data.suspend_cause = PA_SUSPEND_USER;
+ else
+ pa_assert_not_reached();
+ break;
case PA_BLUETOOTH_PROFILE_A2DP_SINK:
/* Profile switch should have failed */
case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
- case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
case PA_BLUETOOTH_PROFILE_OFF:
pa_assert_not_reached();
break;
@@ -1292,8 +1341,13 @@ static int setup_transport(struct userdata *u) {
if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)
transport_acquire(u, true); /* In case of error, the sink/sources will be created suspended */
- else if (transport_acquire(u, false) < 0)
- return -1; /* We need to fail here until the interactions with module-suspend-on-idle and alike get improved */
+ else {
+ int transport_error;
+
+ transport_error = transport_acquire(u, false);
+ if (transport_error < 0 && transport_error != -EAGAIN)
+ return -1; /* We need to fail here until the interactions with module-suspend-on-idle and alike get improved */
+ }
transport_config(u);
@@ -2001,6 +2055,14 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot
if (u->source) {
pa_log_debug("Resuming source %s because its transport state changed to playing", u->source->name);
+ /* When the ofono backend resumes source or sink when in the audio gateway role, the
+ * state of source or sink may already be RUNNING before the transport is acquired via
+ * hf_audio_agent_new_connection(), so the pa_source_suspend() call will not lead to a
+ * state change message. In this case we explicitely need to signal the I/O thread to
+ * set up the stream. */
+ if (PA_SOURCE_IS_OPENED(u->source->state))
+ pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_SETUP_STREAM, NULL, 0, NULL);
+
/* We remove the IDLE suspend cause, because otherwise
* module-loopback doesn't uncork its streams. FIXME: Messing with
* the IDLE suspend cause here is wrong, the correct way to handle
@@ -2014,6 +2076,10 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot
if (u->sink) {
pa_log_debug("Resuming sink %s because its transport state changed to playing", u->sink->name);
+ /* Same comment as above */
+ if (PA_SINK_IS_OPENED(u->sink->state))
+ pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_SETUP_STREAM, NULL, 0, NULL);
+
/* FIXME: See the previous comment. */
pa_sink_suspend(u->sink, false, PA_SUSPEND_IDLE|PA_SUSPEND_USER);
}
@@ -2215,6 +2281,7 @@ int pa__init(pa_module* m) {
u->msg->parent.process_msg = device_process_msg;
u->msg->card = u->card;
+ u->stream_setup_done = false;
if (u->profile != PA_BLUETOOTH_PROFILE_OFF)
if (init_profile(u) < 0)