Hi Tanu, On Mon, Mar 28, 2011 at 3:35 PM, Tanu Kaskinen <tanu.kaskinen at digia.com> wrote: > From: Tanu Kaskinen <ext-tanu.kaskinen at nokia.com> > > --- > ?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); > > -- There is a similar policy in BlueZ already for this and it is configurable via /etc/bluetooth/audio.conf, so I don't think hardcoding this on PA would do any better. -- Luiz Augusto von Dentz Computer Engineer