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 | 59 +++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index e51da4d..db2c5ff 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -35,6 +35,7 @@ #include <pulse/timeval.h> #include <pulse/xmalloc.h> +#include <pulsecore/device-prototype.h> #include <pulsecore/i18n.h> #include <pulsecore/module.h> #include <pulsecore/modargs.h> @@ -156,6 +157,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 +1584,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 +1658,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 +1883,19 @@ 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_device_prototype_set_sink(u->sink_prototype, NULL); + else + 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_device_prototype_set_source(u->source_prototype, NULL); + else + pa_source_unlink(u->source); + } if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); @@ -1951,19 +1966,25 @@ static int start_thread(struct userdata *u) { k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->sink); pa_shared_remove(u->core, k); pa_xfree(k); + pa_device_prototype_set_sink(u->sink_prototype, NULL); 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); + pa_device_prototype_set_source(u->source_prototype, NULL); u->source = NULL; } return -1; } pa_sink_ref(u->sink); + pa_device_prototype_set_sink(u->sink_prototype, u->sink); + pa_source_ref(u->source); + pa_device_prototype_set_source(u->source_prototype, u->source); + /* FIXME: monitor stream_fd error */ return 0; } @@ -2192,8 +2213,12 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid pa_assert_se(output_port = pa_hashmap_get(ports, u->output_port_name)); if (pa_streq(uuid, A2DP_SINK_UUID)) { + if (!u->sink_prototype) + u->sink_prototype = pa_device_prototype_new(); + p = pa_card_profile_new("a2dp", _("High Fidelity Playback (A2DP)"), sizeof(enum profile)); p->priority = 10; + pa_hashmap_put(p->device_prototypes, u->sink_prototype, u->sink_prototype); p->n_sinks = 1; p->n_sources = 0; p->max_sink_channels = 2; @@ -2203,8 +2228,12 @@ 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)) { + if (!u->source_prototype) + u->source_prototype = pa_device_prototype_new(); + p = pa_card_profile_new("a2dp_source", _("High Fidelity Capture (A2DP)"), sizeof(enum profile)); p->priority = 10; + pa_hashmap_put(p->device_prototypes, u->source_prototype, u->source_prototype); p->n_sinks = 0; p->n_sources = 1; p->max_sink_channels = 0; @@ -2214,8 +2243,16 @@ 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)) { + if (!u->sink_prototype) + u->sink_prototype = pa_device_prototype_new(); + + if (!u->source_prototype) + u->source_prototype = pa_device_prototype_new(); + p = pa_card_profile_new("hsp", _("Telephony Duplex (HSP/HFP)"), sizeof(enum profile)); p->priority = 20; + pa_hashmap_put(p->device_prototypes, u->sink_prototype, u->sink_prototype); + pa_hashmap_put(p->device_prototypes, u->source_prototype, u->source_prototype); p->n_sinks = 1; p->n_sources = 1; p->max_sink_channels = 1; @@ -2226,8 +2263,16 @@ 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)) { + if (!u->sink_prototype) + u->sink_prototype = pa_device_prototype_new(); + + if (!u->source_prototype) + u->source_prototype = pa_device_prototype_new(); + p = pa_card_profile_new("hfgw", _("Handsfree Gateway"), sizeof(enum profile)); p->priority = 20; + pa_hashmap_put(p->device_prototypes, u->sink_prototype, u->sink_prototype); + pa_hashmap_put(p->device_prototypes, u->source_prototype, u->source_prototype); p->n_sinks = 1; p->n_sources = 1; p->max_sink_channels = 1; @@ -2628,6 +2673,12 @@ void pa__done(pa_module *m) { if (u->card) pa_card_free(u->card); + if (u->source_prototype) + pa_device_prototype_free(u->source_prototype); + + if (u->sink_prototype) + pa_device_prototype_free(u->sink_prototype); + if (u->a2dp.buffer) pa_xfree(u->a2dp.buffer); -- 1.8.3.1