From: Tanu Kaskinen <tanu.kaskinen@xxxxxxxxx> --- Here is some unfinished work related to the upcoming module called module-wl1273-support. The module will want to use the org.bluez.FMRadio interface and get notifications when the WL1273 chip appears and disappears in bluetoothd. The module will know the "hciX" identifier of the chip, thus the fmradios_by_adapter_name hashmap is very convenient for looking up the right proxy object. I'd like to develop bluetooth-util to the direction that * it will hide D-Bus entirely from the modules (except for the async nature of function calls, which can't be hidden) * objects (like Transports and Devices) are directly available in the pa_bluetooth_core struct instead of through the IMO clumsy functions like pa_bluetooth_discover_get_transport() * hooks have very narrow and well-defined semantics, instead of the vague "discovery hook" that seems to be called in strange situations (like as part of error handling when receiving properties...) * objects have clear separation between "public" and "private" variables * things are documented :) Please comment if that plan doesn't please you... src/modules/bluetooth/bluetooth-util.c | 118 ++++++++++++++++++++++++++++++++ src/modules/bluetooth/bluetooth-util.h | 87 +++++++++++++++++++++++ 2 files changed, 205 insertions(+), 0 deletions(-) diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c index c5befe3..8d3739d 100644 --- a/src/modules/bluetooth/bluetooth-util.c +++ b/src/modules/bluetooth/bluetooth-util.c @@ -66,6 +66,7 @@ struct pa_bluetooth_discovery { PA_REFCNT_DECLARE; pa_core *core; + pa_bluetooth_core *bt_core; pa_dbus_connection *connection; PA_LLIST_HEAD(pa_dbus_pending, pending); pa_hashmap *devices; @@ -1434,6 +1435,113 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi return DBUS_HANDLER_RESULT_HANDLED; } +void adapter_init(pa_bluetooth_adapter *a, const char *hci_name) { + unsigned i; + + pa_assert(a); + + a->hci_name = pa_xstrdup(hci_name); + + for (i = 0; i < PA_BLUETOOTH_ADAPTER_HOOK_MAX; i++) + pa_hook_init(&a->hooks[i], a); +} + +pa_bluetooth_adapter *adapter_new(const char *hci_name) { + pa_bluetooth_adapter *a; + + pa_assert(hci_name); + + a = pa_xnew0(pa_bluetooth_adapter, 1); + adapter_init(a, hci_name); + + return a; +} + +void adapter_done(pa_bluetooth_adapter *a) { + unsigned i; + + pa_assert(a); + + for (i = 0; i < PA_BLUETOOTH_ADAPTER_HOOK_MAX; i++) + pa_hook_done(&a->hooks[i]); + + pa_xfree(a->hci_name); +} + +void adapter_free(pa_bluetooth_adapter *a) { + pa_assert(a); + + adapter_done(a); + pa_xfree(a); +} + +pa_bluetooth_fmradio *fmradio_new(const char *hci_name) { + pa_bluetooth_fmradio *f; + unsigned i; + + pa_assert(hci_name); + + f = pa_xnew0(pa_bluetooth_fmradio, 1); + adapter_init(&f->adapter, hci_name); + f->mode = PA_BLUETOOTH_FMRADIO_MODE_INVALID; + + for (i = 0; i < PA_BLUETOOTH_FMRADIO_HOOK_MAX; i++) + pa_hook_init(&f->hooks[i], f); + + return f; +} + +void fmradio_free(pa_bluetooth_fmradio *f) { + unsigned i; + + pa_assert(f); + + for (i = 0; i < PA_BLUETOOTH_FMRADIO_HOOK_MAX; i++) + pa_hook_done(&f->hooks[i]); + + adapter_done(&f->adapter); + pa_xfree(f); +} + +pa_bluetooth_core *core_new(void) { + pa_bluetooth_core *c; + unsigned i; + + c = pa_xnew0(pa_bluetooth_core, 1); + c->adapters_by_name = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + c->fmradios_by_adapter_name = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + for (i = 0; i < PA_BLUETOOTH_CORE_HOOK_MAX; i++) + pa_hook_init(&c->hooks[i], c); + + return c; +} + +void core_free(pa_bluetooth_core *c) { + unsigned i; + pa_bluetooth_fmradio *fmradio; + + pa_assert(c); + + for (i = 0; i < PA_BLUETOOTH_CORE_HOOK_MAX; i++) + pa_hook_done(&c->hooks[i]); + + while ((fmradio = pa_hashmap_steal_first(c->fmradios_by_adapter_name))) { + pa_assert_se(pa_hashmap_remove(c->adapters_by_name, fmradio->adapter.hci_name)); + pa_hook_fire(&fmradio->adapter.hooks[PA_BLUETOOTH_ADAPTER_HOOK_UNLINKED], NULL); + fmradio_free(fmradio); + } + pa_hashmap_free(c->fmradios_by_adapter_name, NULL, NULL); + + while ((adapter = pa_hashmap_steal_first(c->adapters_by_name))) { + pa_hook_fire(&adapter->hooks[PA_BLUETOOTH_ADAPTER_HOOK_UNLINKED], NULL); + adapter_free(adapter); + } + pa_hashmap_free(c->adapters_by_name, NULL, NULL); + + pa_xfree(c); +} + pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { DBusError err; pa_bluetooth_discovery *y; @@ -1451,6 +1559,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { y = pa_xnew0(pa_bluetooth_discovery, 1); PA_REFCNT_INIT(y); y->core = c; + y->bt_core = core_new(); y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending); pa_hook_init(&y->hook, y); @@ -1556,12 +1665,21 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { pa_hook_done(&y->hook); + if (y->bt_core) + core_free(y->bt_core); + if (y->core) pa_shared_remove(y->core, "bluetooth-discovery"); pa_xfree(y); } +pa_bluetooth_core *pa_bluetooth_discovery_get_core(pa_bluetooth_discovery *d) { + pa_assert(d); + + return d->bt_core; +} + void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *y) { pa_assert(y); pa_assert(PA_REFCNT_VALUE(y) > 0); diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h index 2752a69..6722d43 100644 --- a/src/modules/bluetooth/bluetooth-util.h +++ b/src/modules/bluetooth/bluetooth-util.h @@ -43,6 +43,9 @@ #define A2DP_SOURCE_UUID "0000110a-0000-1000-8000-00805f9b34fb" #define A2DP_SINK_UUID "0000110b-0000-1000-8000-00805f9b34fb" +typedef struct pa_bluetooth_core pa_bluetooth_core; +typedef struct pa_bluetooth_adapter pa_bluetooth_adapter; +typedef struct pa_bluetooth_fmradio pa_bluetooth_fmradio; typedef struct pa_bluetooth_uuid pa_bluetooth_uuid; typedef struct pa_bluetooth_device pa_bluetooth_device; typedef struct pa_bluetooth_discovery pa_bluetooth_discovery; @@ -115,10 +118,94 @@ struct pa_bluetooth_device { pa_bt_audio_state_t hfgw_state; }; +/* Hook data: pa_bluetooth_adapter pointer. */ +typedef enum pa_bluetooth_adapter_hook { + PA_BLUETOOTH_ADAPTER_HOOK_UNLINKED = 0, /* Call data: NULL. */ + PA_BLUETOOTH_ADAPTER_HOOK_MAX +} pa_bluetooth_adapter_hook_t; + +/* Proxy object for org.bluez.Adapter. */ +struct pa_bluetooth_adapter { + /* Public data for module access. */ + char *hci_name; /* For example "hci0". */ + pa_hook hooks[PA_BLUETOOTH_ADAPTER_HOOK_MAX]; + + /* Private data, not for module access. */ + /* (nothing yet) */ +}; + +/* The FM radio API exists in BlueZ, because such API is required to support + * the WL1273 chip properly. The chip has an internal audio routing switch that + * has three states: "bluetooth", "fmrx" and "fmtx". The state is controlled + * with bluetooth commands, which is why BlueZ is handling it. The audio + * interface for the chip is implemented as an alsa device. Depending on the + * chip audio routing, the alsa device is connected to the bluetooth SCO stream + * or to FM radio recording or transmission. + * + * The routing should be kept at "bluetooth" always when FM radio functionality + * isn't needed, because the FM radio modes force the chip to be constantly + * powered on. When the bluetooth mode is on, BlueZ can turn off the chip when + * appropriate. + * + * The numeric values (except for INVALID) are defined by the + * org.bluez.FMRadio D-Bus API, so don't touch them. */ +typedef enum pa_bluetooth_fmradio_mode { + PA_BLUETOOTH_FMRADIO_MODE_INVALID = 0, + PA_BLUETOOTH_FMRADIO_MODE_BLUETOOTH = 1, + PA_BLUETOOTH_FMRADIO_MODE_FMRX = 2, + PA_BLUETOOTH_FMRADIO_MODE_FMTX = 3 +} pa_bluetooth_fmradio_mode_t; + +/* Hook data: pa_bluetooth_fmradio pointer. */ +typedef enum pa_bluetooth_fmradio_hook { + PA_BLUETOOTH_FMRADIO_HOOK_MODE_CHANGED = 0, /* Call data: the old mode as an unsigned int. */ + PA_BLUETOOTH_FMRADIO_HOOK_MAX +} pa_bluetooth_fmradio_hook_t; + +/* Proxy object for the org.bluez.FMRadio interface, which is a subinterface of + * org.bluez.Adapter. */ +struct pa_bluetooth_fmradio { + /* Public data for module access. */ + pa_bluetooth_adapter adapter; + pa_bluetooth_fmradio_mode_t mode; /* Set with pa_bluetooth_fmradio_set_mode(). */ + pa_hook hooks[PA_BLUETOOTH_FMRADIO_HOOK_MAX]; + + /* Private data, not for module access. */ + /* (nothing yet) */ +}; + +/* Hook data: pa_bluetooth_core pointer. */ +typedef enum pa_bluetooth_core_hook { + PA_BLUETOOTH_CORE_HOOK_NEW_FMRADIO = 0, /* Call data: pa_bluetooth_fmradio pointer. */ + PA_BLUETOOTH_CORE_HOOK_MAX +} pa_bluetooth_core_hook_t; + +/* This struct doesn't correspond directly to any Bluez object. The purpose is + * to collect bluetooth objects here for easy access. */ +struct pa_bluetooth_core { + /* Public data for module access. */ + pa_hashmap *adapters_by_name; /* Name is here eg. "hci0". */ + pa_hashmap *fmradios_by_adapter_name; /* Adapter name is here eg. "hci0". */ + pa_hook hooks[PA_BLUETOOTH_CORE_HOOK_MAX]; + + /* Private data, not for module access. */ + /* (nothing yet) */ +}; + pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core); pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y); void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *d); +/* The returned pa_bluetooth_core object, and any other objects pointed to by + * the core, is valid as long as the discovery object is valid, so it's safe to + * store the pointer as long as you hold a reference to the discovery + * object. */ +pa_bluetooth_core *pa_bluetooth_discovery_get_core(pa_bluetooth_discovery *d); + +/* If block is TRUE, the function will wait for reply from bluetoothd before + * returning. */ +int pa_bluetooth_adapter_set_fmradio_mode(pa_bluetooth_fmradio *f, pa_bluetooth_fmradio_mode_t mode, pa_bool_t block); + void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *d); const pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *d, const char* path); -- 1.7.7.3