From: Jo?o Paulo Rechi Vita <jprvita@xxxxxxxxxxxxx> --- src/modules/bluetooth/module-bluez5-device.c | 182 +++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index 4bd7f34..1867de9 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -24,6 +24,8 @@ #include <config.h> #endif +#include <pulsecore/core-util.h> +#include <pulsecore/i18n.h> #include <pulsecore/module.h> #include <pulsecore/modargs.h> @@ -50,8 +52,182 @@ struct userdata { pa_bluetooth_discovery *discovery; pa_bluetooth_device *device; + + pa_card *card; + pa_bluetooth_profile_t profile; }; +typedef enum pa_bluetooth_form_factor { + PA_BLUETOOTH_FORM_FACTOR_UNKNOWN, + PA_BLUETOOTH_FORM_FACTOR_HEADSET, + PA_BLUETOOTH_FORM_FACTOR_HANDSFREE, + PA_BLUETOOTH_FORM_FACTOR_MICROPHONE, + PA_BLUETOOTH_FORM_FACTOR_SPEAKER, + PA_BLUETOOTH_FORM_FACTOR_HEADPHONE, + PA_BLUETOOTH_FORM_FACTOR_PORTABLE, + PA_BLUETOOTH_FORM_FACTOR_CAR, + PA_BLUETOOTH_FORM_FACTOR_HIFI, + PA_BLUETOOTH_FORM_FACTOR_PHONE, +} pa_bluetooth_form_factor_t; + +/* Run from main thread */ +static pa_bluetooth_form_factor_t form_factor_from_class(uint32_t class_of_device) { + unsigned major, minor; + pa_bluetooth_form_factor_t r; + + static const pa_bluetooth_form_factor_t table[] = { + [1] = PA_BLUETOOTH_FORM_FACTOR_HEADSET, + [2] = PA_BLUETOOTH_FORM_FACTOR_HANDSFREE, + [4] = PA_BLUETOOTH_FORM_FACTOR_MICROPHONE, + [5] = PA_BLUETOOTH_FORM_FACTOR_SPEAKER, + [6] = PA_BLUETOOTH_FORM_FACTOR_HEADPHONE, + [7] = PA_BLUETOOTH_FORM_FACTOR_PORTABLE, + [8] = PA_BLUETOOTH_FORM_FACTOR_CAR, + [10] = PA_BLUETOOTH_FORM_FACTOR_HIFI + }; + + /* + * See Bluetooth Assigned Numbers: + * https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm + */ + major = (class_of_device >> 8) & 0x1F; + minor = (class_of_device >> 2) & 0x3F; + + switch (major) { + case 2: + return PA_BLUETOOTH_FORM_FACTOR_PHONE; + case 4: + break; + default: + pa_log_debug("Unknown Bluetooth major device class %u", major); + return PA_BLUETOOTH_FORM_FACTOR_UNKNOWN; + } + + r = minor < PA_ELEMENTSOF(table) ? table[minor] : PA_BLUETOOTH_FORM_FACTOR_UNKNOWN; + + if (!r) + pa_log_debug("Unknown Bluetooth minor device class %u", minor); + + return r; +} + +/* Run from main thread */ +static const char *form_factor_to_string(pa_bluetooth_form_factor_t ff) { + switch (ff) { + case PA_BLUETOOTH_FORM_FACTOR_UNKNOWN: + return "unknown"; + case PA_BLUETOOTH_FORM_FACTOR_HEADSET: + return "headset"; + case PA_BLUETOOTH_FORM_FACTOR_HANDSFREE: + return "hands-free"; + case PA_BLUETOOTH_FORM_FACTOR_MICROPHONE: + return "microphone"; + case PA_BLUETOOTH_FORM_FACTOR_SPEAKER: + return "speaker"; + case PA_BLUETOOTH_FORM_FACTOR_HEADPHONE: + return "headphone"; + case PA_BLUETOOTH_FORM_FACTOR_PORTABLE: + return "portable"; + case PA_BLUETOOTH_FORM_FACTOR_CAR: + return "car"; + case PA_BLUETOOTH_FORM_FACTOR_HIFI: + return "hifi"; + case PA_BLUETOOTH_FORM_FACTOR_PHONE: + return "phone"; + } + + pa_assert_not_reached(); +} + +/* Run from main thread */ +static char *cleanup_name(const char *name) { + char *t, *s, *d; + bool space = false; + + pa_assert(name); + + while ((*name >= 1 && *name <= 32) || *name >= 127) + name++; + + t = pa_xstrdup(name); + + for (s = d = t; *s; s++) { + + if (*s <= 32 || *s >= 127 || *s == '_') { + space = true; + continue; + } + + if (space) { + *(d++) = ' '; + space = false; + } + + *(d++) = *s; + } + + *d = 0; + + return t; +} + +/* Run from main thread */ +static int add_card(struct userdata *u) { + const pa_bluetooth_device *d; + pa_card_new_data data; + char *alias; + pa_bluetooth_form_factor_t ff; + pa_card_profile *cp; + pa_bluetooth_profile_t *p; + + pa_assert(u); + pa_assert(u->device); + + d = u->device; + + pa_card_new_data_init(&data); + data.driver = __FILE__; + data.module = u->module; + + alias = cleanup_name(d->alias); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, alias); + pa_xfree(alias); + + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, d->address); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "bluez"); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound"); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_BUS, "bluetooth"); + + if ((ff = form_factor_from_class(d->class_of_device)) != PA_BLUETOOTH_FORM_FACTOR_UNKNOWN) + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, form_factor_to_string(ff)); + + pa_proplist_sets(data.proplist, "bluez.path", d->path); + pa_proplist_setf(data.proplist, "bluez.class", "0x%06x", d->class_of_device); + pa_proplist_sets(data.proplist, "bluez.alias", d->alias); + data.name = pa_sprintf_malloc("bluez_card.%s", d->address); + data.namereg_fail = false; + + cp = pa_card_profile_new("off", _("Off"), sizeof(pa_bluetooth_profile_t)); + cp->available = PA_AVAILABLE_YES; + p = PA_CARD_PROFILE_DATA(cp); + *p = PA_BLUETOOTH_PROFILE_OFF; + pa_hashmap_put(data.profiles, cp->name, cp); + + u->card = pa_card_new(u->core, &data); + pa_card_new_data_done(&data); + if (!u->card) { + pa_log("Failed to allocate card."); + return -1; + } + + u->card->userdata = u; + + p = PA_CARD_PROFILE_DATA(u->card->active_profile); + u->profile = *p; + + return 0; +} + /* Run from main thread */ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) { pa_assert(d); @@ -101,6 +277,9 @@ int pa__init(pa_module* m) { pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) device_connection_changed_cb, u); + if (add_card(u) < 0) + goto fail; + return 0; fail: @@ -124,6 +303,9 @@ void pa__done(pa_module *m) { if (u->device_connection_changed_slot) pa_hook_slot_free(u->device_connection_changed_slot); + if (u->card) + pa_card_free(u->card); + if (u->discovery) pa_bluetooth_discovery_unref(u->discovery); -- 1.8.3.1