There is only one sink prototype and only one source prototype, because regardless of the chosen profile, the sink and source will always use the same name, therefore they should be treated as the same entity. In the "SCO over PCM" mode we have to manage the sink/source pointer lifecycle in the prototypes ourselves, because the SCO sink and source are actually ALSA devices that exist all the time. They are not removed from the system when changing to the A2DP profile. --- src/modules/bluetooth/module-bluetooth-device.c | 52 +++++++++++++++++++++---- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index bfd0f99..cfa8b8d 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -156,6 +156,8 @@ struct userdata { char *input_port_name; pa_card *card; + pa_device_prototype *sink_prototype; + pa_device_prototype *source_prototype; pa_sink *sink; pa_source *source; @@ -1581,6 +1583,8 @@ static int add_sink(struct userdata *u) { pa_assert_not_reached(); } + pa_sink_new_data_set_prototype(&data, u->sink_prototype); + u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); pa_sink_new_data_done(&data); @@ -1653,6 +1657,8 @@ static int add_source(struct userdata *u) { pa_assert_not_reached(); } + pa_source_new_data_set_prototype(&data, u->source_prototype); + u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); pa_source_new_data_done(&data); @@ -1876,11 +1882,15 @@ static void stop_thread(struct userdata *u) { pa_assert(u); - if (u->sink && !USE_SCO_OVER_PCM(u)) - pa_sink_unlink(u->sink); + if (u->sink) { + if (!USE_SCO_OVER_PCM(u)) + pa_sink_unlink(u->sink); + } - if (u->source && !USE_SCO_OVER_PCM(u)) - pa_source_unlink(u->source); + if (u->source) { + if (!USE_SCO_OVER_PCM(u)) + pa_source_unlink(u->source); + } if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); @@ -2182,6 +2192,7 @@ fail: /* Run from main thread */ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid, pa_hashmap *ports) { + enum profile profile = PROFILE_OFF; pa_device_port *input_port, *output_port; pa_card_profile *p = NULL; enum profile *d; @@ -2191,9 +2202,29 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid pa_assert_se(input_port = pa_hashmap_get(ports, u->input_port_name)); pa_assert_se(output_port = pa_hashmap_get(ports, u->output_port_name)); - if (pa_streq(uuid, A2DP_SINK_UUID)) { + if (pa_streq(uuid, A2DP_SINK_UUID)) + profile = PROFILE_A2DP; + else if (pa_streq(uuid, A2DP_SOURCE_UUID)) + profile = PROFILE_A2DP_SOURCE; + else if (pa_streq(uuid, HSP_HS_UUID) || pa_streq(uuid, HFP_HS_UUID)) + profile = PROFILE_HSP; + else if (pa_streq(uuid, HFP_AG_UUID)) + profile = PROFILE_HFGW; + + if (profile == PROFILE_A2DP || (!USE_SCO_OVER_PCM(u) && (profile == PROFILE_HSP || profile == PROFILE_HFGW))) { + if (!u->sink_prototype) + u->sink_prototype = pa_device_prototype_new(PA_DIRECTION_OUTPUT); + } + + if (profile == PROFILE_A2DP_SOURCE || (!USE_SCO_OVER_PCM(u) && (profile == PROFILE_HSP || profile == PROFILE_HFGW))) { + if (!u->source_prototype) + u->source_prototype = pa_device_prototype_new(PA_DIRECTION_INPUT); + } + + if (profile == PROFILE_A2DP) { p = pa_card_profile_new("a2dp", _("High Fidelity Playback (A2DP)"), sizeof(enum profile)); p->priority = 10; + pa_hashmap_put(p->sink_prototypes, u->sink_prototype, u->sink_prototype); p->n_sinks = 1; p->n_sources = 0; p->max_sink_channels = 2; @@ -2202,9 +2233,10 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid d = PA_CARD_PROFILE_DATA(p); *d = PROFILE_A2DP; - } else if (pa_streq(uuid, A2DP_SOURCE_UUID)) { + } else if (profile == PROFILE_A2DP_SOURCE) { p = pa_card_profile_new("a2dp_source", _("High Fidelity Capture (A2DP)"), sizeof(enum profile)); p->priority = 10; + pa_hashmap_put(p->source_prototypes, u->source_prototype, u->source_prototype); p->n_sinks = 0; p->n_sources = 1; p->max_sink_channels = 0; @@ -2213,11 +2245,13 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid d = PA_CARD_PROFILE_DATA(p); *d = PROFILE_A2DP_SOURCE; - } else if (pa_streq(uuid, HSP_HS_UUID) || pa_streq(uuid, HFP_HS_UUID)) { + } else if (profile == PROFILE_HSP) { p = pa_card_profile_new("hsp", _("Telephony Duplex (HSP/HFP)"), sizeof(enum profile)); p->priority = 20; if (!USE_SCO_OVER_PCM(u)) { + pa_hashmap_put(p->sink_prototypes, u->sink_prototype, u->sink_prototype); + pa_hashmap_put(p->source_prototypes, u->source_prototype, u->source_prototype); p->n_sinks = 1; p->n_sources = 1; p->max_sink_channels = 1; @@ -2229,11 +2263,13 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid d = PA_CARD_PROFILE_DATA(p); *d = PROFILE_HSP; - } else if (pa_streq(uuid, HFP_AG_UUID)) { + } else if (profile == PROFILE_HFGW) { p = pa_card_profile_new("hfgw", _("Handsfree Gateway"), sizeof(enum profile)); p->priority = 20; if (!USE_SCO_OVER_PCM(u)) { + pa_hashmap_put(p->sink_prototypes, u->sink_prototype, u->sink_prototype); + pa_hashmap_put(p->source_prototypes, u->source_prototype, u->source_prototype); p->n_sinks = 1; p->n_sources = 1; p->max_sink_channels = 1; -- 1.8.3.1