From: Mikel Astiz <mikel.as...@bmw-carit.de>

Handle the Playing->Connected transition gracefully by releasing the
transport and setting the sink and sources as suspended.

We also need to take care of the HUP case in the IO thread, since we
don't known which of both events (HUP or D-Bus state transition) will be
triggered first.
---
 src/modules/bluetooth/module-bluetooth-device.c |   84 +++++++++++++++++++----
 1 files changed, 70 insertions(+), 14 deletions(-)

diff --git a/src/modules/bluetooth/module-bluetooth-device.c 
b/src/modules/bluetooth/module-bluetooth-device.c
index 93bdd1b..b228b37 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -191,6 +191,7 @@ struct userdata {
 
 enum {
     BLUETOOTH_MESSAGE_IO_THREAD_FAILED,
+    BLUETOOTH_MESSAGE_IO_THREAD_HUP,
     BLUETOOTH_MESSAGE_MAX
 };
 
@@ -287,11 +288,23 @@ static bool bt_transport_is_acquired(struct userdata *u) {
         pa_assert(u->stream_fd < 0);
         return FALSE;
     } else {
-        pa_assert(u->stream_fd >= 0);
+        /* During IO thread HUP stream_fd can be -1 */
         return TRUE;
     }
 }
 
+static void bt_transport_close_stream(struct userdata *u) {
+    if (u->rtpoll_item) {
+        pa_rtpoll_item_free(u->rtpoll_item);
+        u->rtpoll_item = NULL;
+    }
+
+    if (u->stream_fd >= 0) {
+        pa_close(u->stream_fd);
+        u->stream_fd = -1;
+    }
+}
+
 static void bt_transport_release(struct userdata *u) {
     const char *accesstype = "rw";
     const pa_bluetooth_transport *t;
@@ -309,15 +322,7 @@ static void bt_transport_release(struct userdata *u) {
     pa_xfree(u->accesstype);
     u->accesstype = NULL;
 
-    if (u->rtpoll_item) {
-        pa_rtpoll_item_free(u->rtpoll_item);
-        u->rtpoll_item = NULL;
-    }
-
-    if (u->stream_fd >= 0) {
-        pa_close(u->stream_fd);
-        u->stream_fd = -1;
-    }
+    bt_transport_close_stream(u);
 
     if (u->read_smoother) {
         pa_smoother_free(u->read_smoother);
@@ -551,8 +556,25 @@ static int source_process_msg(pa_msgobject *o, int code, 
void *data, int64_t off
 }
 
 /* Called from main thread context */
+static void bt_release_transport_after_hup(struct userdata *u) {
+    if (!bt_transport_is_acquired(u))
+        return;
+
+    /* FIXME: this release is racy, since the audio stream might have
+       been set up again in the meantime (but not processed yet by PA).
+       BlueZ should probably release the transport automatically, and
+       in that case we would just mark the transport as released */
+    if (u->source)
+        pa_source_suspend(u->source, TRUE, PA_SUSPEND_IDLE);
+
+    if (u->sink)
+        pa_sink_suspend(u->sink, TRUE, PA_SUSPEND_IDLE);
+}
+
+/* Called from main thread context */
 static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t 
offset, pa_memchunk *chunk) {
     struct bluetooth_msg *u = BLUETOOTH_MSG(obj);
+    struct userdata *d = data;
 
     switch (code) {
         case BLUETOOTH_MESSAGE_IO_THREAD_FAILED: {
@@ -565,6 +587,19 @@ static int device_process_msg(pa_msgobject *obj, int code, 
void *data, int64_t o
                 pa_log_debug("Failed to switch profile to off");
             break;
         }
+
+        case BLUETOOTH_MESSAGE_IO_THREAD_HUP: {
+            if (u->card->module->unload_requested)
+                break;
+
+            /* Make sure stream was not resumed */
+            if (d->stream_fd >= 0)
+                break;
+
+            pa_log_debug("Suspending audio due to IO HUP.");
+            bt_release_transport_after_hup(d);
+            break;
+        }
     }
     return 0;
 }
@@ -1047,7 +1082,7 @@ static void thread_func(void *userdata) {
                     n_read = a2dp_process_push(u);
 
                 if (n_read < 0)
-                    goto fail;
+                    goto io_fail;
 
                 /* We just read something, so we are supposed to write 
something, too */
                 pending_read_bytes += n_read;
@@ -1115,10 +1150,10 @@ static void thread_func(void *userdata) {
 
                     if (u->profile == PROFILE_A2DP) {
                         if ((n_written = a2dp_process_render(u)) < 0)
-                            goto fail;
+                            goto io_fail;
                     } else {
                         if ((n_written = hsp_process_render(u)) < 0)
-                            goto fail;
+                            goto io_fail;
                     }
 
                     if (n_written == 0)
@@ -1175,8 +1210,21 @@ static void thread_func(void *userdata) {
                         pollfd->revents & POLLHUP ? "POLLHUP " :"",
                         pollfd->revents & POLLPRI ? "POLLPRI " :"",
                         pollfd->revents & POLLNVAL ? "POLLNVAL " :"");
-            goto fail;
+            goto io_fail;
         }
+
+        continue;
+
+io_fail:
+        /* In case of HUP, just suspend the streams */
+        if (!pollfd || (pollfd->revents & POLLHUP) == 0)
+            goto fail;
+
+        pa_log_debug("IO thread HUP");
+
+        bt_transport_close_stream(u);
+
+        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u->msg), 
BLUETOOTH_MESSAGE_IO_THREAD_HUP, u, 0, NULL, NULL);
     }
 
 fail:
@@ -1249,6 +1297,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, 
DBusMessage *m, void *us
     DBusError err;
     struct userdata *u;
     bool acquire = FALSE;
+    bool release = FALSE;
 
     pa_assert(bus);
     pa_assert(m);
@@ -1327,6 +1376,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, 
DBusMessage *m, void *us
             pa_device_port_set_available(port, available);
 
             acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == 
PROFILE_HFGW);
+            release = (available != PA_PORT_AVAILABLE_YES && u->profile == 
PROFILE_HFGW);
         }
     } else if (dbus_message_is_signal(m, "org.bluez.Headset", 
"PropertyChanged")) {
         pa_bt_audio_state_t state = parse_state_property_change(m);
@@ -1342,6 +1392,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, 
DBusMessage *m, void *us
             pa_device_port_set_available(port, available);
 
             acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == 
PROFILE_HSP);
+            release = (available != PA_PORT_AVAILABLE_YES && u->profile == 
PROFILE_HSP);
         }
     } else if (dbus_message_is_signal(m, "org.bluez.AudioSource", 
"PropertyChanged")) {
         pa_bt_audio_state_t state = parse_state_property_change(m);
@@ -1354,6 +1405,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, 
DBusMessage *m, void *us
             pa_device_port_set_available(port, available);
 
             acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == 
PROFILE_A2DP_SOURCE);
+            release = (available != PA_PORT_AVAILABLE_YES && u->profile == 
PROFILE_A2DP_SOURCE);
         }
     } else if (dbus_message_is_signal(m, "org.bluez.AudioSink", 
"PropertyChanged")) {
         pa_bt_audio_state_t state = parse_state_property_change(m);
@@ -1366,6 +1418,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, 
DBusMessage *m, void *us
             pa_device_port_set_available(port, available);
 
             acquire = (available == PA_PORT_AVAILABLE_YES && u->profile == 
PROFILE_A2DP);
+            release = (available != PA_PORT_AVAILABLE_YES && u->profile == 
PROFILE_A2DP);
         }
     }
 
@@ -1378,6 +1431,9 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, 
DBusMessage *m, void *us
                 pa_sink_suspend(u->sink, FALSE, PA_SUSPEND_IDLE);
         }
 
+    if (release)
+        bt_release_transport_after_hup(u);
+
 fail:
     dbus_error_free(&err);
 
-- 
1.7.7.6

_______________________________________________
pulseaudio-discuss mailing list
pulseaudio-discuss@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss

Reply via email to