From: Tanu Kaskinen <ext-tanu.kaskinen@xxxxxxxxx> --- src/modules/bluetooth/module-bluetooth-device.c | 126 +++++++++++++++++------ 1 files changed, 93 insertions(+), 33 deletions(-) diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 540d48c..1154651 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -185,6 +185,8 @@ struct userdata { #define MAX_PLAYBACK_CATCH_UP_USEC (100*PA_USEC_PER_MSEC) +#define CURRENT_HSP_DEVICE_KEY "current-hsp-device" /* Key to core->shared. */ + #define USE_SCO_OVER_PCM(u) (u->profile == PROFILE_HSP && (u->hsp.sco_sink && u->hsp.sco_source)) static int init_bt(struct userdata *u); @@ -1961,6 +1963,8 @@ static pa_hook_result_t source_state_changed_cb(pa_core *c, pa_source *s, struct /* Run from main thread */ static int add_sink(struct userdata *u) { + char *k; + if (USE_SCO_OVER_PCM(u)) { pa_proplist *p; @@ -2014,6 +2018,10 @@ static int add_sink(struct userdata *u) { if (u->profile == PROFILE_HSP) { u->sink->set_volume = sink_set_volume_cb; u->sink->n_volume_steps = 16; + + k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->sink); + pa_shared_set(u->core, k, u); + pa_xfree(k); } return 0; @@ -2021,6 +2029,8 @@ static int add_sink(struct userdata *u) { /* Run from main thread */ static int add_source(struct userdata *u) { + char *k; + if (USE_SCO_OVER_PCM(u)) { u->source = u->hsp.sco_source; pa_proplist_sets(u->source->proplist, "bluetooth.protocol", "hsp"); @@ -2079,6 +2089,10 @@ static int add_source(struct userdata *u) { if (u->profile == PROFILE_HSP) { u->source->set_volume = source_set_volume_cb; u->source->n_volume_steps = 16; + + k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->source); + pa_shared_set(u->core, k, u); + pa_xfree(k); } return 0; @@ -2325,6 +2339,8 @@ static int init_profile(struct userdata *u) { /* Run from main thread */ static void stop_thread(struct userdata *u) { + char *k; + pa_assert(u); if (u->thread) { @@ -2349,11 +2365,23 @@ static void stop_thread(struct userdata *u) { } if (u->sink) { + if (u->profile == PROFILE_HSP) { + k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->sink); + pa_shared_remove(u->core, k); + pa_xfree(k); + } + pa_sink_unref(u->sink); u->sink = NULL; } if (u->source) { + if (u->profile == PROFILE_HSP) { + k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->source); + pa_shared_remove(u->core, k); + pa_xfree(k); + } + pa_source_unref(u->source); u->source = NULL; } @@ -2383,8 +2411,20 @@ static int start_thread(struct userdata *u) { if (USE_SCO_OVER_PCM(u)) { if (sco_over_pcm_state_update(u) < 0) { - u->sink = NULL; - u->source = NULL; + char *k; + + if (u->sink) { + k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->sink); + pa_shared_remove(u->core, k); + pa_xfree(k); + u->sink = NULL; + } + if (u->source) { + k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->source); + pa_shared_remove(u->core, k); + pa_xfree(k); + u->source = NULL; + } return -1; } @@ -2421,9 +2461,25 @@ static int start_thread(struct userdata *u) { return 0; } +static void save_sco_volume_callbacks(struct userdata *u) { + pa_assert(u); + pa_assert(USE_SCO_OVER_PCM(u)); + + u->hsp.sco_sink_set_volume = u->hsp.sco_sink->set_volume; + u->hsp.sco_source_set_volume = u->hsp.sco_source->set_volume; +} + +static void restore_sco_volume_callbacks(struct userdata *u) { + pa_assert(u); + pa_assert(USE_SCO_OVER_PCM(u)); + + u->hsp.sco_sink->set_volume = u->hsp.sco_sink_set_volume; + u->hsp.sco_source->set_volume = u->hsp.sco_source_set_volume; +} + /* Run from main thread */ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { - struct userdata *u; + struct userdata *u, *other_hsp_device; enum profile *d; pa_queue *inputs = NULL, *outputs = NULL; const pa_bluetooth_device *device; @@ -2439,6 +2495,9 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { return -PA_ERR_IO; } + if (u->profile == PROFILE_HSP) + pa_shared_remove(u->core, CURRENT_HSP_DEVICE_KEY); + /* The state signal is sent by bluez, so it is racy to check strictly for CONNECTED, we should also accept STREAMING state as being good enough. However, if the profile is used @@ -2448,6 +2507,10 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { pa_log_warn("HSP is not connected, refused to switch profile"); return -PA_ERR_IO; } + else if (*d == PROFILE_HSP && (other_hsp_device = pa_shared_get(u->core, CURRENT_HSP_DEVICE_KEY))) { + pa_log_warn("Another HSP device (%s) is already active, refused to switch profile.", other_hsp_device->card->name); + return -1; + } else if (device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP) { pa_log_warn("A2DP is not connected, refused to switch profile"); return -PA_ERR_IO; @@ -2474,9 +2537,18 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { stop_thread(u); shutdown_bt(u); + if (USE_SCO_OVER_PCM(u)) + restore_sco_volume_callbacks(u); + u->profile = *d; u->sample_spec = u->requested_sample_spec; + if (USE_SCO_OVER_PCM(u)) + save_sco_volume_callbacks(u); + + if (u->profile == PROFILE_HSP) + pa_shared_set(u->core, CURRENT_HSP_DEVICE_KEY, u); + init_bt(u); if (u->profile != PROFILE_OFF) @@ -2511,6 +2583,7 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) { const char *ff; char *n; const char *default_profile; + struct userdata *other_hsp_device = NULL; pa_assert(u); pa_assert(device); @@ -2636,11 +2709,21 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) { 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; + } else if (*d == PROFILE_HSP && (other_hsp_device = pa_shared_get(u->core, CURRENT_HSP_DEVICE_KEY))) { + pa_log_warn("Another HSP device (%s) is already active, selecting off profile.", other_hsp_device->card->name); + 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; + if (USE_SCO_OVER_PCM(u)) + save_sco_volume_callbacks(u); + + if (u->profile == PROFILE_HSP) + pa_shared_set(u->core, CURRENT_HSP_DEVICE_KEY, u); + return 0; } @@ -2704,7 +2787,7 @@ int pa__init(pa_module* m) { struct userdata *u; const char *address, *path; DBusError err; - char *mike, *speaker, *transport, *k; + char *mike, *speaker, *transport; const pa_bluetooth_device *device; pa_assert(m); @@ -2805,20 +2888,6 @@ int pa__init(pa_module* m) { /* Connect to the BT service */ init_bt(u); - if (u->hsp.sco_sink) { - u->hsp.sco_sink_set_volume = u->hsp.sco_sink->set_volume; - k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->hsp.sco_sink); - pa_shared_set(u->core, k, u); - pa_xfree(k); - } - - if (u->hsp.sco_source) { - u->hsp.sco_source_set_volume = u->hsp.sco_source->set_volume; - k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->hsp.sco_source); - pa_shared_set(u->core, k, u); - pa_xfree(k); - } - if (u->profile != PROFILE_OFF) if (init_profile(u) < 0) goto fail; @@ -2851,7 +2920,6 @@ int pa__get_n_used(pa_module *m) { void pa__done(pa_module *m) { struct userdata *u; - char *k; pa_assert(m); @@ -2866,6 +2934,12 @@ void pa__done(pa_module *m) { stop_thread(u); + if (USE_SCO_OVER_PCM(u)) + restore_sco_volume_callbacks(u); + + if (u->profile == PROFILE_HSP) + pa_shared_remove(u->core, CURRENT_HSP_DEVICE_KEY); + if (u->connection) { if (u->path) { @@ -2893,20 +2967,6 @@ void pa__done(pa_module *m) { shutdown_bt(u); - if (u->hsp.sco_sink) { - u->hsp.sco_sink->set_volume = u->hsp.sco_sink_set_volume; - k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->hsp.sco_sink); - pa_shared_remove(u->core, k); - pa_xfree(k); - } - - if (u->hsp.sco_source) { - u->hsp.sco_source->set_volume = u->hsp.sco_source_set_volume; - k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->hsp.sco_source); - pa_shared_remove(u->core, k); - pa_xfree(k); - } - if (u->a2dp.buffer) pa_xfree(u->a2dp.buffer); -- 1.7.4.1