From 7c5d06c1adbe75b77861c1d24b6d5f21d6801dca Mon Sep 17 00:00:00 2001 From: DoHyun Pyun Date: Tue, 20 Feb 2018 09:26:17 +0900 Subject: [PATCH] bluetooth : Apply tizen patchsets Change-Id: Ib4ba5f9f90feeea33ff9f4247358254149273a19 Signed-off-by: DoHyun Pyun --- packaging/pulseaudio.spec | 4 + src/modules/bluetooth/a2dp-codecs.h | 50 ++- src/modules/bluetooth/bluez5-util.c | 332 +++++++++++++++++- src/modules/bluetooth/bluez5-util.h | 21 ++ src/modules/bluetooth/module-bluetooth-discover.c | 105 ++++++ src/modules/bluetooth/module-bluetooth-policy.c | 4 + src/modules/bluetooth/module-bluez5-device.c | 406 +++++++++++++++++++++- src/modules/bluetooth/module-bluez5-discover.c | 55 ++- 8 files changed, 963 insertions(+), 14 deletions(-) diff --git a/packaging/pulseaudio.spec b/packaging/pulseaudio.spec index 35bee70..47a3a89 100644 --- a/packaging/pulseaudio.spec +++ b/packaging/pulseaudio.spec @@ -17,7 +17,9 @@ BuildRequires: pkgconfig(speexdsp) BuildRequires: pkgconfig(sndfile) BuildRequires: pkgconfig(alsa) BuildRequires: pkgconfig(glib-2.0) +%if "%{?TIZEN_PRODUCT_TV}" != "1" BuildRequires: pkgconfig(sbc) +%endif BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(openssl) @@ -404,12 +406,14 @@ setcap -r /usr/bin/pulseaudio %files module-bluetooth %manifest %{name}.manifest %license LICENSE LGPL +%if "%{?TIZEN_PRODUCT_TV}" != "1" %defattr(-,root,root,-) %{_libdir}/pulse-%{version}/modules/module-bluetooth-discover.so %{_libdir}/pulse-%{version}/modules/module-bluetooth-policy.so %{_libdir}/pulse-%{version}/modules/module-bluez5-discover.so %{_libdir}/pulse-%{version}/modules/module-bluez5-device.so %{_libdir}/pulse-%{version}/modules/libbluez5-util.so +%endif %files module-raop %manifest %{name}.manifest diff --git a/src/modules/bluetooth/a2dp-codecs.h b/src/modules/bluetooth/a2dp-codecs.h index 8afcfcb..a86c3f0 100644 --- a/src/modules/bluetooth/a2dp-codecs.h +++ b/src/modules/bluetooth/a2dp-codecs.h @@ -25,7 +25,11 @@ #define A2DP_CODEC_MPEG12 0x01 #define A2DP_CODEC_MPEG24 0x02 #define A2DP_CODEC_ATRAC 0x03 - +#ifdef __TIZEN_BT__ +#define BLUETOOTH_APTX_SUPPORT 1 +#define A2DP_CODEC_VENDOR 0xFF +#endif +#define A2DP_CODEC_NON_A2DP 0xFF #define SBC_SAMPLING_FREQ_16000 (1 << 3) #define SBC_SAMPLING_FREQ_32000 (1 << 2) #define SBC_SAMPLING_FREQ_44100 (1 << 1) @@ -63,9 +67,40 @@ #define MPEG_SAMPLING_FREQ_44100 (1 << 1) #define MPEG_SAMPLING_FREQ_48000 1 + +#if defined(__TIZEN_BT__) && defined(ADJUST_ANDROID_BITPOOL) +#define MAX_BITPOOL 35 +#else #define MAX_BITPOOL 64 +#endif #define MIN_BITPOOL 2 +/*#define APTX_CHANNEL_MODE_STEREO 2 */ +/* + * aptX codec for Bluetooth only supports stereo mode with value 2 + * But we do have sink devices programmed to send capabilities with other channel mode support. + * So to handle the case and keeping codec symmetry with SBC etc., we do define other channel mode, + * and we always make sure to set configuration with APTX_CHANNEL_MODE_STEREO only. + * + * */ + +#define APTX_CHANNEL_MODE_MONO (1 << 3) +#define APTX_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define APTX_CHANNEL_MODE_STEREO (1 << 1) +#define APTX_CHANNEL_MODE_JOINT_STEREO 1 + +#define APTX_VENDOR_ID0 0x4F /*APTX codec ID 79*/ +#define APTX_VENDOR_ID1 0x0 +#define APTX_VENDOR_ID2 0x0 +#define APTX_VENDOR_ID3 0x0 + +#define APTX_CODEC_ID0 0x1 +#define APTX_CODEC_ID1 0x0 + +#define APTX_SAMPLING_FREQ_16000 (1 << 3) +#define APTX_SAMPLING_FREQ_32000 (1 << 2) +#define APTX_SAMPLING_FREQ_44100 (1 << 1) +#define APTX_SAMPLING_FREQ_48000 1 #if __BYTE_ORDER == __LITTLE_ENDIAN typedef struct { @@ -88,6 +123,12 @@ typedef struct { uint16_t bitrate; } __attribute__ ((packed)) a2dp_mpeg_t; +typedef struct { + uint8_t vendor_id[4]; + uint8_t codec_id[2]; + uint8_t channel_mode:4; + uint8_t frequency:4; +} __attribute__ ((packed)) a2dp_aptx_t; #elif __BYTE_ORDER == __BIG_ENDIAN typedef struct { @@ -110,6 +151,13 @@ typedef struct { uint16_t bitrate; } __attribute__ ((packed)) a2dp_mpeg_t; +typedef struct { + uint8_t vendor_id[4]; + uint8_t codec_id[2]; + uint8_t frequency:4; + uint8_t channel_mode:4; +} __attribute__ ((packed)) a2dp_aptx_t; + #else #error "Unknown byte order" #endif diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c index c928323..5e352f6 100644 --- a/src/modules/bluetooth/bluez5-util.c +++ b/src/modules/bluetooth/bluez5-util.c @@ -36,6 +36,9 @@ #include "a2dp-codecs.h" #include "bluez5-util.h" +#ifdef BLUETOOTH_APTX_SUPPORT +#include +#endif #define WAIT_FOR_PROFILES_TIMEOUT_USEC (3 * PA_USEC_PER_SEC) @@ -50,6 +53,9 @@ #define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource" #define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink" +#ifdef BLUETOOTH_APTX_SUPPORT +#define A2DP_APTX_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource_aptx" +#endif #define ENDPOINT_INTROSPECT_XML \ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ @@ -68,6 +74,8 @@ " " \ " " \ " " \ + " " \ + " " \ " " \ " " \ " " \ @@ -94,6 +102,56 @@ struct pa_bluetooth_discovery { PA_LLIST_HEAD(pa_dbus_pending, pending); }; +#ifdef BLUETOOTH_APTX_SUPPORT +static void *aptx_handle = NULL; + +int pa_unload_aptx(void) +{ + if (aptx_handle == NULL) { + pa_log_warn("Unable to unload apt-X library"); + return -1; + } + + dlclose(aptx_handle); + aptx_handle = NULL; + + pa_log_debug("unloaded apt-X library successfully"); + return 0; +} + +int pa_load_aptx(const char *aptx_lib_name) +{ + char* lib_path = NULL ; + + if(aptx_lib_name == NULL) + return -1; + + lib_path = pa_sprintf_malloc("%s/%s", PA_DLSEARCHPATH, aptx_lib_name); + + if (!lib_path) + return -1; + + pa_log_info("aptx_lib_path = [%s]", lib_path); + + aptx_handle = dlopen(lib_path, RTLD_LAZY); + if (aptx_handle == NULL) { + pa_log_warn("Unable to load apt-X library [%s]", dlerror()); + pa_xfree(lib_path); + return -1; + } + + pa_log_debug("loaded apt-X library successfully"); + pa_xfree(lib_path); + + return 0; +} + +void* pa_aptx_get_handle(void) +{ + return aptx_handle; +} +#endif + static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m, DBusPendingCallNotifyFunction func, void *call_data) { pa_dbus_pending *p; @@ -440,6 +498,42 @@ bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d) { return false; } +#ifdef __TIZEN_BT__ +bool pa_bluetooth_device_sink_transport_connected(const pa_bluetooth_device *d) { + unsigned i; + + pa_assert(d); + + if (!d->valid) + return false; + + for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) + if (d->transports[i] && + d->transports[i]->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK && + d->transports[i]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) + return true; + + return false; +} + +bool pa_bluetooth_device_source_transport_connected(const pa_bluetooth_device *d) { + unsigned i; + + pa_assert(d); + + if (!d->valid) + return false; + + for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) + if (d->transports[i] && + d->transports[i]->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE && + d->transports[i]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) + return true; + + return false; +} +#endif + static int transport_state_from_string(const char* value, pa_bluetooth_transport_state_t *state) { pa_assert(value); pa_assert(state); @@ -889,6 +983,10 @@ static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const DBusMessageIter i, d; uint8_t codec = 0; +#ifdef BLUETOOTH_APTX_SUPPORT + if(pa_streq(endpoint,A2DP_APTX_SOURCE_ENDPOINT)) + codec = A2DP_CODEC_VENDOR; +#endif pa_log_debug("Registering %s on adapter %s", endpoint, path); pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, path, BLUEZ_MEDIA_INTERFACE, "RegisterEndpoint")); @@ -901,8 +999,37 @@ static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec); if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) || pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) { +#ifdef __TIZEN_BT__ + if (codec == A2DP_CODEC_SBC) { + a2dp_sbc_t capabilities; + capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | + SBC_CHANNEL_MODE_JOINT_STEREO; + capabilities.frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | + SBC_SAMPLING_FREQ_48000; + capabilities.allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS; + capabilities.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8; + capabilities.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16; + capabilities.min_bitpool = MIN_BITPOOL; + capabilities.max_bitpool = MAX_BITPOOL; + pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities)); + } +#ifdef BLUETOOTH_APTX_SUPPORT + if (codec == A2DP_CODEC_VENDOR ) { + /* aptx */ + a2dp_aptx_t capabilities; + capabilities.vendor_id[0] = APTX_VENDOR_ID0; + capabilities.vendor_id[1] = APTX_VENDOR_ID1; + capabilities.vendor_id[2] = APTX_VENDOR_ID2; + capabilities.vendor_id[3] = APTX_VENDOR_ID3; + capabilities.codec_id[0] = APTX_CODEC_ID0; + capabilities.codec_id[1] = APTX_CODEC_ID1; + capabilities.channel_mode= APTX_CHANNEL_MODE_STEREO; + capabilities.frequency= APTX_SAMPLING_FREQ_44100; + pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities)); + } +#endif /* BLUETOOTH_APTX_SUPPORT */ +#else a2dp_sbc_t capabilities; - capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL | SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_JOINT_STEREO; capabilities.frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 | SBC_SAMPLING_FREQ_44100 | @@ -912,8 +1039,8 @@ static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const capabilities.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16; capabilities.min_bitpool = MIN_BITPOOL; capabilities.max_bitpool = MAX_BITPOOL; - pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities)); +#endif /* __TIZEN_BT__ */ } dbus_message_iter_close_container(&i, &d); @@ -966,6 +1093,10 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa register_endpoint(y, path, A2DP_SOURCE_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE); register_endpoint(y, path, A2DP_SINK_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SINK); +#ifdef BLUETOOTH_APTX_SUPPORT + if (aptx_handle) + register_endpoint(y, path, A2DP_APTX_SOURCE_ENDPOINT, PA_BLUETOOTH_UUID_A2DP_SOURCE); +#endif } else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) { if ((d = pa_hashmap_get(y->devices, path))) { @@ -1146,10 +1277,10 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")) { DBusMessageIter arg_i; - +#ifndef __TIZEN_BT__ if (!y->objects_listed) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */ - +#endif if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) { pa_log_error("Invalid signature found in InterfacesAdded"); goto fail; @@ -1197,8 +1328,10 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us DBusMessageIter arg_i; const char *iface; +#ifndef __TIZEN_BT__ if (!y->objects_listed) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */ +#endif if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "sa{sv}as")) { pa_log_error("Invalid signature found in PropertiesChanged"); @@ -1248,6 +1381,19 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +#ifdef __TIZEN_BT__ + } else if (dbus_message_is_signal(m, "org.bluez.ag_agent", "SuspendMedia")) { + pa_bluetooth_transport *t; + + pa_log_debug("Signal from ag-agent to Suspend Media"); + + if (!(t = pa_hashmap_first(y->transports))) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_SCO_STATE_CHANGED], t); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +#endif } fail: @@ -1272,7 +1418,11 @@ static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) { case SBC_CHANNEL_MODE_STEREO: case SBC_CHANNEL_MODE_JOINT_STEREO: +#if defined(__TIZEN_BT__) && defined(ADJUST_ANDROID_BITPOOL) + return 35; +#else return 53; +#endif } pa_log_warn("Invalid channel mode %u", mode); @@ -1298,6 +1448,84 @@ static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) { return 53; } +#ifdef BLUETOOTH_APTX_SUPPORT +static DBusMessage *endpoint_select_configuration_for_aptx(DBusConnection *c, DBusMessage *m, void *userdata) { + a2dp_aptx_t *cap; + a2dp_aptx_t config; + uint8_t *pconf = (uint8_t *) &config; + int size; + DBusMessage *r; + DBusError e; + + dbus_error_init(&e); + + if (!dbus_message_get_args(m, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) { + pa_log("org.bluez.MediaEndpoint.SelectConfiguration: %s", e.message); + dbus_error_free(&e); + goto fail; + } + + pa_assert(size == sizeof(config)); + + memset(&config, 0, sizeof(config)); + + if (cap->vendor_id[0] == APTX_VENDOR_ID0 && + cap->vendor_id[1] == APTX_VENDOR_ID1 && + cap->vendor_id[2] == APTX_VENDOR_ID2 && + cap->vendor_id[3] == APTX_VENDOR_ID3 && + cap->codec_id[0] == APTX_CODEC_ID0 && + cap->codec_id[1] == APTX_CODEC_ID1 ) + pa_log_debug("A2DP_CODEC_NON_A2DP and this is APTX Codec"); + else { + pa_log_debug("A2DP_CODEC_NON_A2DP but this is not APTX Codec"); + goto fail; + } + + memcpy(&config,cap, sizeof(config)); + +/* The below code shuld be re-written by aptx */ +/* And we should configure pulseaudio freq */ + + if (cap->frequency & APTX_SAMPLING_FREQ_44100) + config.frequency = APTX_SAMPLING_FREQ_44100; + else if (cap->frequency & APTX_SAMPLING_FREQ_48000) + config.frequency = APTX_SAMPLING_FREQ_48000; + else if (cap->frequency & APTX_SAMPLING_FREQ_32000) + config.frequency = APTX_SAMPLING_FREQ_32000; + else if (cap->frequency & APTX_SAMPLING_FREQ_16000) + config.frequency = APTX_SAMPLING_FREQ_16000; + else { + pa_log_error("No aptx supported frequencies"); + goto fail; + } + + if (cap->channel_mode & APTX_CHANNEL_MODE_JOINT_STEREO) + config.channel_mode = APTX_CHANNEL_MODE_STEREO; + else if (cap->channel_mode & APTX_CHANNEL_MODE_STEREO) + config.channel_mode = APTX_CHANNEL_MODE_STEREO; + else if (cap->channel_mode & APTX_CHANNEL_MODE_DUAL_CHANNEL) + config.channel_mode = APTX_CHANNEL_MODE_STEREO; + else { + pa_log_error("No aptx supported channel modes"); + goto fail; + } + + pa_assert_se(r = dbus_message_new_method_return(m)); + + pa_assert_se(dbus_message_append_args( + r, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size, + DBUS_TYPE_INVALID)); + + return r; + +fail: + pa_assert_se(r = (dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments", + "Unable to select configuration"))); + return r; +} +#endif + const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) { switch(profile) { case PA_BLUETOOTH_PROFILE_A2DP_SINK: @@ -1320,6 +1548,9 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage pa_bluetooth_device *d; pa_bluetooth_transport *t; const char *sender, *path, *endpoint_path, *dev_path = NULL, *uuid = NULL; +#ifdef __TIZEN_BT__ + uint8_t codec = 0; +#endif const uint8_t *config = NULL; int size = 0; pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_OFF; @@ -1367,10 +1598,19 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage dbus_message_iter_get_basic(&value, &uuid); endpoint_path = dbus_message_get_path(m); +#ifdef BLUETOOTH_APTX_SUPPORT + if (pa_streq(endpoint_path, A2DP_SOURCE_ENDPOINT) || + pa_streq(endpoint_path, A2DP_APTX_SOURCE_ENDPOINT)) { + if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE)) + p = PA_BLUETOOTH_PROFILE_A2DP_SINK; + } +#else if (pa_streq(endpoint_path, A2DP_SOURCE_ENDPOINT)) { if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE)) p = PA_BLUETOOTH_PROFILE_A2DP_SINK; - } else if (pa_streq(endpoint_path, A2DP_SINK_ENDPOINT)) { + } +#endif + else if (pa_streq(endpoint_path, A2DP_SINK_ENDPOINT)) { if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE; } @@ -1379,6 +1619,15 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid, path, endpoint_path); goto fail; } +#ifdef __TIZEN_BT__ + } else if (pa_streq(key, "Codec")) { + if (var != DBUS_TYPE_BYTE) { + pa_log_error("Property %s of wrong type %c", key, (char)var); + goto fail; + } + + dbus_message_iter_get_basic(&value, &codec); +#endif } else if (pa_streq(key, "Device")) { if (var != DBUS_TYPE_OBJECT_PATH) { pa_log_error("Property %s of wrong type %c", key, (char)var); @@ -1403,6 +1652,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage } dbus_message_iter_get_fixed_array(&array, &config, &size); +#ifndef BLUETOOTH_APTX_SUPPORT if (size != sizeof(a2dp_sbc_t)) { pa_log_error("Configuration array of invalid size"); goto fail; @@ -1437,6 +1687,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage pa_log_error("Invalid block length in configuration"); goto fail; } +#endif } dbus_message_iter_next(&props); @@ -1465,6 +1716,10 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage dbus_message_unref(r); t = pa_bluetooth_transport_new(d, sender, path, p, config, size); +#ifdef __TIZEN_BT__ + t->codec = codec; + d->transports[p] = t; +#endif t->acquire = bluez5_transport_acquire_cb; t->release = bluez5_transport_release_cb; pa_bluetooth_transport_put(t); @@ -1499,6 +1754,11 @@ static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMess { 48000U, SBC_SAMPLING_FREQ_48000 } }; +#ifdef BLUETOOTH_APTX_SUPPORT + if (dbus_message_has_path(m, A2DP_APTX_SOURCE_ENDPOINT)) + return endpoint_select_configuration_for_aptx(conn ,m ,userdata); +#endif + dbus_error_init(&err); if (!dbus_message_get_args(m, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) { @@ -1648,6 +1908,28 @@ static DBusMessage *endpoint_release(DBusConnection *conn, DBusMessage *m, void return r; } +#ifdef __TIZEN_BT__ +static DBusMessage *endpoint_suspend_media(DBusConnection *conn, DBusMessage *m, void *userdata) { + pa_bluetooth_discovery *y = userdata; + pa_bluetooth_transport *t; + DBusMessage *r; + + pa_log_debug("dbus Call from to Suspend Media"); + if (!(t = pa_hashmap_first(y->transports))) + goto fail; + + pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_SCO_STATE_CHANGED], t); + + pa_assert_se(r = dbus_message_new_method_return(m)); + + return r; + +fail: + pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to clear configuration")); + return r; +} +#endif + static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) { struct pa_bluetooth_discovery *y = userdata; DBusMessage *r = NULL; @@ -1661,8 +1943,14 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member); +#ifdef BLUETOOTH_APTX_SUPPORT + if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT) + && !pa_streq(path,A2DP_APTX_SOURCE_ENDPOINT)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +#else if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT)) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +#endif /* BLUETOOTH_APTX_SUPPORT */ if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { const char *xml = ENDPOINT_INTROSPECT_XML; @@ -1678,6 +1966,10 @@ static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, voi r = endpoint_clear_configuration(c, m, userdata); else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "Release")) r = endpoint_release(c, m, userdata); +#ifdef __TIZEN_BT__ + else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SuspendMedia")) + endpoint_suspend_media(c, m, userdata); +#endif else return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -1700,6 +1992,10 @@ static void endpoint_init(pa_bluetooth_discovery *y, pa_bluetooth_profile_t prof case PA_BLUETOOTH_PROFILE_A2DP_SINK: pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT, &vtable_endpoint, y)); +#ifdef BLUETOOTH_APTX_SUPPORT + pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_APTX_SOURCE_ENDPOINT, + &vtable_endpoint, y)); +#endif break; case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT, @@ -1717,6 +2013,9 @@ static void endpoint_done(pa_bluetooth_discovery *y, pa_bluetooth_profile_t prof switch(profile) { case PA_BLUETOOTH_PROFILE_A2DP_SINK: dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT); +#ifdef BLUETOOTH_APTX_SUPPORT + dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_APTX_SOURCE_ENDPOINT); +#endif break; case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT); @@ -1757,7 +2056,13 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backe } conn = pa_dbus_connection_get(y->connection); - +#ifdef __TIZEN_BT__ + if (dbus_bus_request_name(conn, "org.PulseAudio2", DBUS_NAME_FLAG_REPLACE_EXISTING, &err) + != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + pa_log_error("Failed to set D-Bus name: %s", err.message); + goto fail; + } +#endif /* dynamic detection of bluetooth audio devices */ if (!dbus_connection_add_filter(conn, filter_cb, y, NULL)) { pa_log_error("Failed to add filter function"); @@ -1777,6 +2082,9 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backe ",arg0='" BLUEZ_DEVICE_INTERFACE "'", "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'" ",arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'", +#ifdef __TIZEN_BT__ + "type='signal',interface='org.bluez.ag_agent',member='SuspendMedia'", +#endif NULL) < 0) { pa_log_error("Failed to add D-Bus matches: %s", err.message); goto fail; @@ -1786,6 +2094,10 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backe endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SINK); endpoint_init(y, PA_BLUETOOTH_PROFILE_A2DP_SOURCE); +#ifndef __TIZEN_BT__ + y->hf_audio_agent = hf_audio_agent_init(c); +#endif + get_managed_objects(y); return y; @@ -1831,6 +2143,11 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { pa_hashmap_free(y->transports); } +#ifndef __TIZEN_BT__ + if (y->hf_audio_agent) + hf_audio_agent_done(y->hf_audio_agent); +#endif + if (y->connection) { if (y->matches_added) @@ -1847,6 +2164,9 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { "member='PropertiesChanged',arg0='" BLUEZ_DEVICE_INTERFACE "'", "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties'," "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'", +#ifdef __TIZEN_BT__ + "type='signal',interface='org.bluez.ag_agent',member='SuspendMedia'", +#endif NULL); if (y->filter_added) diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index a3e7bf3..7f4f674 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -29,6 +29,13 @@ #define PA_BLUETOOTH_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb" #define PA_BLUETOOTH_UUID_HFP_AG "0000111f-0000-1000-8000-00805f9b34fb" +#ifdef __TIZEN_BT__ +#ifdef pa_log_debug(...) +#undef pa_log_debug(...) +#define pa_log_debug(...) pa_log_level_meta(PA_LOG_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__) +#endif +#endif + typedef struct pa_bluetooth_transport pa_bluetooth_transport; typedef struct pa_bluetooth_device pa_bluetooth_device; typedef struct pa_bluetooth_adapter pa_bluetooth_adapter; @@ -38,6 +45,9 @@ typedef struct pa_bluetooth_backend pa_bluetooth_backend; typedef enum pa_bluetooth_hook { PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */ PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */ +#ifdef __TIZEN_BT__ + PA_BLUETOOTH_HOOK_SCO_STATE_CHANGED, /* Call data: pa_bluetooth_transport */ +#endif PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */ PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */ PA_BLUETOOTH_HOOK_MAX @@ -149,6 +159,10 @@ void pa_bluetooth_transport_unlink(pa_bluetooth_transport *t); void pa_bluetooth_transport_free(pa_bluetooth_transport *t); bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d); +#ifdef __TIZEN_BT__ +bool pa_bluetooth_device_sink_transport_connected(const pa_bluetooth_device *d); +bool pa_bluetooth_device_source_transport_connected(const pa_bluetooth_device *d); +#endif pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery *y, const char *path); pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local); @@ -165,4 +179,11 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core, int headset_ba pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y); void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y); void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is_running); + +#ifdef BLUETOOTH_APTX_SUPPORT +int pa_load_aptx(const char *aptx_lib_name); +int pa_unload_aptx(void); +void* pa_aptx_get_handle(void); +#endif + #endif diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c index d69a77f..e1f7eb1 100644 --- a/src/modules/bluetooth/module-bluetooth-discover.c +++ b/src/modules/bluetooth/module-bluetooth-discover.c @@ -24,6 +24,10 @@ #include #include #include +#ifdef BLUETOOTH_APTX_SUPPORT +#include +#include +#endif #include "module-bluetooth-discover-symdef.h" @@ -34,19 +38,110 @@ PA_MODULE_LOAD_ONCE(true); PA_MODULE_USAGE( "headset=ofono|native|auto (bluez 5 only)" "autodetect_mtu= (bluez 5 only)" +#ifdef BLUETOOTH_APTX_SUPPORT + "aptx_lib_name=" +#endif ); +#ifdef BLUETOOTH_APTX_SUPPORT +static const char* const valid_modargs[] = { + "aptx_lib_name", +#ifdef __TIZEN_BT__ + "enable_scmst", +#endif + NULL +}; +#endif + struct userdata { uint32_t bluez5_module_idx; uint32_t bluez4_module_idx; }; +#ifdef BLUETOOTH_APTX_SUPPORT +int pa_load_aptx(const char *aptx_lib_name); +int pa_unload_aptx(void); +void* pa_aptx_get_handle(void); +#endif + +#ifdef BLUETOOTH_APTX_SUPPORT +static void *aptx_handle = NULL; + +int pa_unload_aptx(void) +{ + if (aptx_handle == NULL) { + pa_log_warn("Unable to unload apt-X library"); + return -1; + } + + dlclose(aptx_handle); + aptx_handle = NULL; + + pa_log_debug("unloaded apt-X library successfully"); + return 0; +} + +int pa_load_aptx(const char *aptx_lib_name) +{ + char* lib_path = NULL ; + + if(aptx_lib_name == NULL) + return -1; + + lib_path = pa_sprintf_malloc("%s/%s", PA_DLSEARCHPATH, aptx_lib_name); + + if (!lib_path) + return -1; + + pa_log_info("aptx_lib_path = [%s]", lib_path); + + aptx_handle = dlopen(lib_path, RTLD_LAZY); + if (aptx_handle == NULL) { + pa_log_warn("Unable to load apt-X library [%s]", dlerror()); + pa_xfree(lib_path); + return -1; + } + + pa_log_debug("loaded apt-X library successfully"); + pa_xfree(lib_path); + + return 0; +} + +void* pa_aptx_get_handle(void) +{ + return aptx_handle; +} +#endif int pa__init(pa_module* m) { struct userdata *u; pa_module *mm; +#ifdef BLUETOOTH_APTX_SUPPORT + pa_modargs *ma = NULL; + const char *aptx_lib_name = NULL; +#endif + pa_assert(m); +#ifdef BLUETOOTH_APTX_SUPPORT + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + pa__done(m); + return -1; + } + + if (pa_modargs_get_value(ma, "async", NULL)) + pa_log_warn("The 'async' argument is deprecated and does nothing."); + + + aptx_lib_name = pa_modargs_get_value(ma, "aptx_lib_name", NULL); + if (aptx_lib_name) + pa_load_aptx(aptx_lib_name); + else + pa_log("Failed to parse aptx_lib_name argument."); +#endif + m->userdata = u = pa_xnew0(struct userdata, 1); u->bluez5_module_idx = PA_INVALID_INDEX; u->bluez4_module_idx = PA_INVALID_INDEX; @@ -65,8 +160,14 @@ int pa__init(pa_module* m) { if (u->bluez5_module_idx == PA_INVALID_INDEX && u->bluez4_module_idx == PA_INVALID_INDEX) { pa_xfree(u); +#ifdef BLUETOOTH_APTX_SUPPORT + pa_modargs_free(ma); +#endif return -1; } +#ifdef BLUETOOTH_APTX_SUPPORT + pa_modargs_free(ma); +#endif return 0; } @@ -85,5 +186,9 @@ void pa__done(pa_module* m) { if (u->bluez4_module_idx != PA_INVALID_INDEX) pa_module_unload_by_index(m->core, u->bluez4_module_idx, true); +#ifdef BLUETOOTH_APTX_SUPPORT + pa_unload_aptx(); +#endif + pa_xfree(u); } diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c index 316b9a8..df247ba 100644 --- a/src/modules/bluetooth/module-bluetooth-policy.c +++ b/src/modules/bluetooth/module-bluetooth-policy.c @@ -88,7 +88,11 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, return PA_HOOK_OK; if (u->enable_a2dp_source && pa_streq(s, "a2dp_source")) +#ifdef __TIZEN__ + role = "media"; +#else role = "music"; +#endif /* TODO: remove hfgw when we remove BlueZ 4 support */ else if (u->enable_ag && (pa_streq(s, "hfgw") || pa_streq(s, "headset_audio_gateway"))) role = "phone"; diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index 530207a..66ffa98 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -27,6 +27,10 @@ #include #include +#ifdef BLUETOOTH_APTX_SUPPORT +#include +#endif + #include #include #include @@ -45,6 +49,11 @@ #include #include +#ifdef __TIZEN_BT__ +#include +#include +#endif + #include "a2dp-codecs.h" #include "bluez5-util.h" #include "rtp.h" @@ -71,6 +80,10 @@ PA_MODULE_USAGE("path=" static const char* const valid_modargs[] = { "path", "autodetect_mtu", +#ifdef __TIZEN_BT__ + "address", + "profile", +#endif NULL }; @@ -102,7 +115,10 @@ typedef struct sbc_info { uint16_t seq_num; /* Cumulative packet sequence */ uint8_t min_bitpool; uint8_t max_bitpool; - +#ifdef BLUETOOTH_APTX_SUPPORT + bool aptx_initialized; /* Keep track if the encoder is initialized */ + void *aptx; /* aptx Codec data */ +#endif void* buffer; /* Codec transfer buffer */ size_t buffer_size; /* Size of the buffer */ } sbc_info_t; @@ -113,6 +129,9 @@ struct userdata { pa_hook_slot *device_connection_changed_slot; pa_hook_slot *transport_state_changed_slot; +#ifdef __TIZEN_BT__ + pa_hook_slot *sco_state_changed_slot; +#endif pa_hook_slot *transport_speaker_gain_changed_slot; pa_hook_slot *transport_microphone_gain_changed_slot; @@ -122,6 +141,9 @@ struct userdata { bool transport_acquired; bool stream_setup_done; +#ifdef __TIZEN_BT__ + bool transport_suspended_by_remote; +#endif pa_card *card; pa_sink *sink; pa_source *source; @@ -148,6 +170,10 @@ struct userdata { pa_memchunk write_memchunk; pa_sample_spec sample_spec; struct sbc_info sbc_info; + +#ifdef __TIZEN_BT__ + pa_modargs *modargs; +#endif }; typedef enum pa_bluetooth_form_factor { @@ -232,6 +258,42 @@ static const char *form_factor_to_string(pa_bluetooth_form_factor_t ff) { pa_assert_not_reached(); } +#ifdef BLUETOOTH_APTX_SUPPORT +void* (*aptx_new)(short endian); +int (*aptx_encode)(void* _state, void* _pcmL, void* _pcmR, void* _buffer); + +const char *aptx_new_name = "NewAptxEnc"; +const char *aptx_encode_name = "aptxbtenc_encodestereo"; + +static bool pa_load_aptx_sym(void *handle ) +{ + if (!handle) + return false; + + aptx_new = (void* (*)(short endian))dlsym(handle, aptx_new_name); + + if (aptx_new) { + pa_log_debug("Load Symbol(%s)", aptx_new_name); + } else { + pa_log_debug("Fail to Load Symbol(%s)", aptx_new_name); + return false; + } + + aptx_encode = (int (*)(void* _state, void* _pcmL, void* _pcmR, + void* _buffer)) + dlsym(handle, "aptxbtenc_encodestereo"); + + if (aptx_encode) { + pa_log_debug("Load Symbol(%s)", aptx_encode_name); + } else { + pa_log_debug("Fail to Load Symbol(%s)", aptx_encode_name); + return false; + } + + return true; +} +#endif + /* Run from main thread */ static void connect_ports(struct userdata *u, void *new_data, pa_direction_t direction) { pa_device_port *port; @@ -547,6 +609,144 @@ static int a2dp_process_render(struct userdata *u) { return ret; } +#ifdef BLUETOOTH_APTX_SUPPORT +/* Run from IO thread */ +static int a2dp_aptx_process_render(struct userdata *u) { + struct sbc_info *a2dp; + size_t nbytes; + void *d; + const void *p; + size_t to_write, to_encode; + int ret = 0; + + int pcmL[4],pcmR[4]; + int i=0; + const uint8_t *mybuffer; + + pa_assert(u); + pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK); + pa_assert(u->sink); + + /* First, render some data */ + if (!u->write_memchunk.memblock) + pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk); + + pa_assert(u->write_memchunk.length == u->write_block_size); + + a2dp_prepare_buffer(u); + + a2dp = &u->sbc_info; + + /* Try to create a packet of the full MTU */ + p = (const uint8_t*) pa_memblock_acquire(u->write_memchunk.memblock) + u->write_memchunk.index; + to_encode = u->write_memchunk.length; + + d = (uint8_t*) a2dp->buffer ; + to_write = a2dp->buffer_size; + + while (PA_LIKELY(to_encode > 0 && to_write > 0)) { + size_t written; + ssize_t encoded; + + mybuffer = (uint8_t *)p; + + for (i = 0; i < 4; i += 1) { + pcmL[i] = mybuffer[2*i]; + pcmR[i] = mybuffer[2*i+1]; + } + /*(8 audio samples)16 bytes of audo data encoded to 4 bytes*/ + aptx_encode(a2dp->aptx, pcmL, pcmR, (short*)d); + + encoded=16; + written=4; + + pa_assert_fp((size_t) encoded <= to_encode); + pa_assert_fp((size_t) written <= to_write); + + p = (const uint8_t*) p + encoded; + to_encode -= encoded; + + d = (uint8_t*) d + written; + to_write -= written; + + } + + pa_memblock_release(u->write_memchunk.memblock); + + pa_assert(to_encode == 0); + + PA_ONCE_BEGIN { + pa_log_debug("Using APTX encoder implementation"); + } PA_ONCE_END; + + nbytes = (uint8_t*) d - (uint8_t*) a2dp->buffer; + + for (;;) { + ssize_t l; + + l = pa_write(u->stream_fd, a2dp->buffer, nbytes, &u->stream_write_type); + + pa_assert(l != 0); + + if (l < 0) { + + if (errno == EINTR) + /* Retry right away if we got interrupted */ + continue; + + else if (errno == EAGAIN) + /* Hmm, apparently the socket was not writable, give up for now */ + break; + + pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno)); + ret = -1; + break; + } + + pa_assert((size_t) l <= nbytes); + + if ((size_t) l != nbytes) { + pa_log_warn("Wrote memory block to socket only partially! %llu written, wanted to write %llu.", + (unsigned long long) l, + (unsigned long long) nbytes); + ret = -1; + break; + } + + u->write_index += (uint64_t) u->write_memchunk.length; + pa_memblock_unref(u->write_memchunk.memblock); + pa_memchunk_reset(&u->write_memchunk); + + ret = 1; + + break; + } + + return ret; +} +#endif + +#ifdef __TIZEN_BT__ +/* Run from IO thread */ +static int a2dp_process_null_render(struct userdata *u) { + pa_assert(u); + pa_assert(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK); + pa_assert(u->sink); + + /* First, render some data */ + if (!u->write_memchunk.memblock) + pa_sink_render_full(u->sink, u->write_block_size, &u->write_memchunk); + + pa_assert(u->write_memchunk.length == u->write_block_size); + + u->write_index += (uint64_t) u->write_memchunk.length; + pa_memblock_unref(u->write_memchunk.memblock); + pa_memchunk_reset(&u->write_memchunk); + + return 1; +} +#endif + /* Run from IO thread */ static int a2dp_process_push(struct userdata *u) { int ret = 0; @@ -756,6 +956,10 @@ static int transport_acquire(struct userdata *u, bool optional) { return u->stream_fd; u->transport_acquired = true; + +#ifdef __TIZEN_BT__ + u->transport_suspended_by_remote = false; +#endif pa_log_info("Transport %s acquired: fd %d", u->transport->path, u->stream_fd); return 0; @@ -774,6 +978,12 @@ static void transport_release(struct userdata *u) { u->transport_acquired = false; +#ifdef __TIZEN_BT__ + /* The below code would be less effect for most of case */ + if (u->transport_suspended_by_remote) + pa_log_info("Released by remote suspend request"); +#endif + teardown_stream(u); /* Set transport state to idle if this was not already done by the remote end closing @@ -797,7 +1007,12 @@ static void transport_config_mtu(struct userdata *u) { pa_log_debug("Got invalid write MTU: %lu, rounding down", u->write_block_size); u->write_block_size = pa_frame_align(u->write_block_size, &u->sink->sample_spec); } + +#ifdef __TIZEN_BT__ + } else if(u->sbc_info.sbc_initialized) { +#else } else { +#endif u->read_block_size = (u->read_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) / u->sbc_info.frame_length * u->sbc_info.codesize; @@ -1079,7 +1294,15 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_RUNNING: if (u->sink->thread_info.state != PA_SINK_SUSPENDED) break; - +#ifdef __TIZEN_BT__ + /* Wait until PA_BLUETOOTH_TRANSPORT_IDLE */ + if ((u->transport != NULL) && (u->transport->state == + PA_BLUETOOTH_TRANSPORT_STATE_IDLE)) + pa_log_info("This is good time for(%d)", + (pa_sink_state_t) PA_PTR_TO_UINT(data)); + else + pa_log_info("Wait for PA_BLUETOOTH_TRANSPORT_STATE_IDLE"); +#endif /* Resume the device if the source was suspended as well */ if (!u->source || !PA_SOURCE_IS_OPENED(u->source->thread_info.state)) failed = !setup_transport_and_stream(u); @@ -1217,6 +1440,36 @@ static int add_sink(struct userdata *u) { return 0; } +#ifdef BLUETOOTH_APTX_SUPPORT +/* should be implemeted */ +static void bt_transport_config_a2dp_for_aptx(struct userdata *u) { + const pa_bluetooth_transport *t; + struct sbc_info *a2dp = &u->sbc_info; + + t = u->transport; + pa_assert(t); + + u->sample_spec.format = PA_SAMPLE_S16LE; + + if (!a2dp->aptx_initialized) { + #if __BYTE_ORDER==__LITTLE_ENDIAN + a2dp->aptx = aptx_new(1); + #elif __BYTE_ORDER==__BIG_ENDIAN + a2dp->aptx = aptx_new(0); + #else + #error "Unknown byte order" + #endif + a2dp->aptx_initialized = true; + } + + pa_log_debug("aptx Encoder is intialized !!"); + + u->write_block_size =(size_t)(u->write_link_mtu/(size_t)16) *16*4 ; + pa_log_info("APTX parameters block_size(%d),link_mtu(%d)",u->write_block_size,u->write_link_mtu); + +} +#endif + /* Run from main thread */ static void transport_config(struct userdata *u) { if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) { @@ -1226,9 +1479,34 @@ static void transport_config(struct userdata *u) { } else { sbc_info_t *sbc_info = &u->sbc_info; a2dp_sbc_t *config; +#ifdef BLUETOOTH_APTX_SUPPORT + const pa_bluetooth_transport *t; + a2dp_aptx_t *aptx_config; +#endif pa_assert(u->transport); +#ifdef BLUETOOTH_APTX_SUPPORT + t = u->transport; + if (t->codec == A2DP_CODEC_VENDOR) { + aptx_config = (a2dp_aptx_t *) t->config; + if (aptx_config->vendor_id[0] == APTX_VENDOR_ID0 && + aptx_config->vendor_id[1] == APTX_VENDOR_ID1 && + aptx_config->vendor_id[2] == APTX_VENDOR_ID2 && + aptx_config->vendor_id[3] == APTX_VENDOR_ID3 && + aptx_config->codec_id[0] == APTX_CODEC_ID0 && + aptx_config->codec_id[1] == APTX_CODEC_ID1 ){ + pa_log("A2DP_CODEC_NON_A2DP and this is APTX Codec"); + + bt_transport_config_a2dp_for_aptx(u); + return; + } else { + pa_log("A2DP_CODEC_NON_A2DP but this is not APTX Codec"); + return; + } + } +#endif + u->sample_spec.format = PA_SAMPLE_S16LE; config = (a2dp_sbc_t *) u->transport->config; @@ -1529,10 +1807,26 @@ static void thread_func(void *userdata) { if (u->write_index <= 0) u->started_at = pa_rtclock_now(); - +#ifdef __TIZEN_BT__ + if ((u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) && + !u->transport_suspended_by_remote) { + if(u->sbc_info.sbc_initialized) { + if ((n_written = a2dp_process_render(u)) < 0) + goto fail; + } else { +#ifdef BLUETOOTH_APTX_SUPPORT + if ((n_written = a2dp_aptx_process_render(u)) < 0) + goto fail; + #endif + } + } else if ((u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) && + u->transport_suspended_by_remote) { + a2dp_process_null_render(u); +#else if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) { if ((n_written = a2dp_process_render(u)) < 0) goto fail; +#endif } else { if ((n_written = sco_process_render(u)) < 0) goto fail; @@ -1872,7 +2166,6 @@ static pa_card_profile *create_card_profile(struct userdata *u, pa_bluetooth_pro p = PA_CARD_PROFILE_DATA(cp); break; - case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: cp = pa_card_profile_new(name, _("Headset Head Unit (HSP/HFP)"), sizeof(pa_bluetooth_profile_t)); cp->priority = 20; @@ -1898,7 +2191,6 @@ static pa_card_profile *create_card_profile(struct userdata *u, pa_bluetooth_pro p = PA_CARD_PROFILE_DATA(cp); break; - case PA_BLUETOOTH_PROFILE_OFF: pa_assert_not_reached(); } @@ -1980,6 +2272,9 @@ static int add_card(struct userdata *u) { pa_bluetooth_profile_t *p; const char *uuid; void *state; +#ifdef __TIZEN_BT__ + const char *default_profile; +#endif pa_assert(u); pa_assert(u->device); @@ -2031,6 +2326,22 @@ static int add_card(struct userdata *u) { *p = PA_BLUETOOTH_PROFILE_OFF; pa_hashmap_put(data.profiles, cp->name, cp); +#ifdef __TIZEN_BT__ +/* Default profile was assigned in the previous tizen version. Because pa_card_new_data_set_profile function + * was removed pulseaudio 11.1 version. So we can't use these codes. If some problems occurs in later. + * We should use this code. */ +#if 0 + if ((default_profile = pa_modargs_get_value(u->modargs, "profile", NULL))) { + pa_log_debug("default_profile: %s", default_profile); + + if (pa_hashmap_get(data.profiles, default_profile)) + pa_card_new_data_set_profile(&data, default_profile); + else + pa_log_warn("Profile '%s' not valid or not supported by device.", default_profile); + } +#endif +#endif + u->card = pa_card_new(u->core, &data); pa_card_new_data_done(&data); if (!u->card) { @@ -2044,6 +2355,7 @@ static int add_card(struct userdata *u) { pa_card_put(u->card); p = PA_CARD_PROFILE_DATA(u->card->active_profile); + u->profile = *p; return 0; @@ -2099,6 +2411,9 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot if (u->sink) { pa_log_debug("Resuming sink %s because its transport state changed to playing", u->sink->name); +#ifdef __TIZEN_BT__ + u->transport_suspended_by_remote = false; +#endif /* Same comment as above */ if (PA_SINK_IS_OPENED(u->sink->state)) pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_SETUP_STREAM, NULL, 0, NULL); @@ -2122,7 +2437,13 @@ static void handle_transport_state_change(struct userdata *u, struct pa_bluetoot if (u->sink) { pa_log_debug("Suspending sink %s because the remote end closed the stream", u->sink->name); +#ifdef __TIZEN_BT__ + /* if we change PA state as Suspend, PA client application + * such as music app would face lock-up */ + u->transport_suspended_by_remote = true; +#else pa_sink_suspend(u->sink, true, PA_SUSPEND_USER); +#endif } } } @@ -2141,6 +2462,46 @@ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, return PA_HOOK_OK; } +#ifdef __TIZEN_BT__ +static void dbus_sco_open_handler(struct userdata *u, struct pa_bluetooth_transport *t) +{ + if (u->sink) { + pa_sink *sink_null; + pa_sink_input *si; + uint32_t idx; + + pa_log_info("Suspending sink %s to handle the SCO connection", u->sink->name); + + if (pa_sink_check_suspend(u->sink, NULL, NULL) > 0) { + sink_null = (pa_sink *)pa_namereg_get(u->core, "null", 0); + + if (sink_null) { + PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) { + pa_sink_input_move_to(si, sink_null, false); + } + } + } + + pa_sink_suspend(u->sink, true, PA_SUSPEND_INTERNAL); + } +} + +/* Run from main thread */ +static pa_hook_result_t sco_state_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) { + pa_assert(t); + pa_assert(u); + + if (t == u->transport && t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) + return PA_HOOK_OK; + + if (t->device == u->device) + dbus_sco_open_handler(u, t); + + return PA_HOOK_OK; +} +#endif + + /* Run from main thread */ static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) { pa_assert(t); @@ -2237,6 +2598,9 @@ int pa__init(pa_module* m) { const char *path; pa_modargs *ma; bool autodetect_mtu; +#ifdef BLUETOOTH_APTX_SUPPORT + void *handle; +#endif pa_assert(m); @@ -2249,6 +2613,11 @@ int pa__init(pa_module* m) { goto fail_free_modargs; } +#ifdef __TIZEN_BT__ + u->sample_spec = m->core->default_sample_spec; + u->modargs = ma; +#endif + if (!(path = pa_modargs_get_value(ma, "path", NULL))) { pa_log_error("Failed to get device path from module arguments"); goto fail_free_modargs; @@ -2274,7 +2643,9 @@ int pa__init(pa_module* m) { u->device->autodetect_mtu = autodetect_mtu; +#ifndef __TIZEN_BT__ pa_modargs_free(ma); +#endif u->device_connection_changed_slot = pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED), @@ -2290,6 +2661,12 @@ int pa__init(pa_module* m) { u->transport_microphone_gain_changed_slot = pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_microphone_gain_changed_cb, u); +#ifdef __TIZEN_BT__ + u->sco_state_changed_slot = + pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_SCO_STATE_CHANGED), + PA_HOOK_NORMAL, (pa_hook_cb_t) sco_state_changed_cb, u); +#endif + if (add_card(u) < 0) goto fail; @@ -2300,6 +2677,20 @@ int pa__init(pa_module* m) { u->msg->card = u->card; u->stream_setup_done = false; +#ifdef BLUETOOTH_APTX_SUPPORT + handle = pa_aptx_get_handle(); + + if (handle) { + pa_log_debug("Aptx Library loaded\n"); + pa_load_aptx_sym(handle); + } +#endif + +#ifdef __TIZEN_BT__ + pa_modargs_free(ma); + u->modargs = NULL; +#endif + if (u->profile != PA_BLUETOOTH_PROFILE_OFF) if (init_profile(u) < 0) goto off; @@ -2348,6 +2739,11 @@ void pa__done(pa_module *m) { if (u->transport_speaker_gain_changed_slot) pa_hook_slot_free(u->transport_speaker_gain_changed_slot); +#ifdef __TIZEN_BT__ + if (u->sco_state_changed_slot) + pa_hook_slot_free(u->sco_state_changed_slot); +#endif + if (u->transport_microphone_gain_changed_slot) pa_hook_slot_free(u->transport_microphone_gain_changed_slot); diff --git a/src/modules/bluetooth/module-bluez5-discover.c b/src/modules/bluetooth/module-bluez5-discover.c index c535ead..8133b4b 100644 --- a/src/modules/bluetooth/module-bluez5-discover.c +++ b/src/modules/bluetooth/module-bluez5-discover.c @@ -27,7 +27,6 @@ #include #include #include - #include "bluez5-util.h" #include "module-bluez5-discover-symdef.h" @@ -35,16 +34,41 @@ PA_MODULE_AUTHOR("João Paulo Rechi Vita"); PA_MODULE_DESCRIPTION("Detect available BlueZ 5 Bluetooth audio devices and load BlueZ 5 Bluetooth audio drivers"); PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_LOAD_ONCE(true); +#ifdef __TIZEN_BT__ +#define HAVE_BLUEZ_5_NATIVE_HEADSET + +PA_MODULE_USAGE("sco_sink= " + "sco_source= " + "headset=ofono|native|auto" +#ifdef BLUETOOTH_APTX_SUPPORT + "aptx_lib_name=" +#endif +); +#else PA_MODULE_USAGE( "headset=ofono|native|auto" ); +#endif +PA_MODULE_LOAD_ONCE(true); +#ifdef __TIZEN_BT__ static const char* const valid_modargs[] = { "headset", "autodetect_mtu", + "sco_sink", + "sco_source", +#ifdef BLUETOOTH_APTX_SUPPORT + "aptx_lib_name", +#endif NULL }; +#else +static const char* const valid_modargs[] = { + "headset", + "autodetect_mtu", + NULL +}; +#endif struct userdata { pa_module *module; @@ -73,7 +97,19 @@ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, if (!module_loaded && pa_bluetooth_device_any_transport_connected(d)) { /* a new device has been connected */ pa_module *m; +#ifdef __TIZEN_BT__ + char *args = pa_sprintf_malloc("address=\"%s\" path=\"%s\" autodetect_mtu=%i", d->address, d->path, (int)u->autodetect_mtu); +#else char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i", d->path, (int)u->autodetect_mtu); +#endif + +#ifdef __TIZEN_BT__ + if (pa_bluetooth_device_sink_transport_connected(d) == true) + args = pa_sprintf_malloc("%s profile=\"a2dp_sink\"", args); + + if (pa_bluetooth_device_source_transport_connected(d) == true) + args = pa_sprintf_malloc("%s profile=\"a2dp_source\"", args); +#endif pa_log_debug("Loading module-bluez5-device %s", args); m = pa_module_load(u->module->core, "module-bluez5-device", args); @@ -100,6 +136,9 @@ const char *default_headset_backend = "ofono"; int pa__init(pa_module *m) { struct userdata *u; +#ifdef BLUETOOTH_APTX_SUPPORT + const char *aptx_lib_name = NULL; +#endif pa_modargs *ma; const char *headset_str; int headset_backend; @@ -112,6 +151,14 @@ int pa__init(pa_module *m) { goto fail; } +#ifdef BLUETOOTH_APTX_SUPPORT + aptx_lib_name = pa_modargs_get_value(ma, "aptx_lib_name", NULL); + if (aptx_lib_name) + pa_load_aptx(aptx_lib_name); + else + pa_log("Failed to parse aptx_lib_name argument."); +#endif + pa_assert_se(headset_str = pa_modargs_get_value(ma, "headset", default_headset_backend)); if (pa_streq(headset_str, "ofono")) headset_backend = HEADSET_BACKEND_OFONO; @@ -170,5 +217,9 @@ void pa__done(pa_module *m) { if (u->loaded_device_paths) pa_hashmap_free(u->loaded_device_paths); +#ifdef BLUETOOTH_APTX_SUPPORT + pa_unload_aptx(); +#endif + pa_xfree(u); } -- 2.7.4