diff -Nru pulseaudio-10.0/debian/changelog pulseaudio-10.0/debian/changelog --- pulseaudio-10.0/debian/changelog 2017-07-11 16:57:16.000000000 +0800 +++ pulseaudio-10.0/debian/changelog 2017-08-07 17:16:23.000000000 +0800 @@ -1,3 +1,10 @@ +pulseaudio (1:10.0-2ubuntu2) artful; urgency=medium + + * Drop Android support (used for Ubuntu Touch). This removes most of the + delta to upstream and Debian. + + -- Daniel van Vugt Mon, 07 Aug 2017 17:16:23 +0800 + pulseaudio (1:10.0-2ubuntu1) artful; urgency=medium * Cherrypick fixes for common crashes from upstream: diff -Nru pulseaudio-10.0/debian/control pulseaudio-10.0/debian/control --- pulseaudio-10.0/debian/control 2017-07-11 16:57:16.000000000 +0800 +++ pulseaudio-10.0/debian/control 2017-08-07 17:16:14.000000000 +0800 @@ -5,16 +5,13 @@ XSBC-Original-Maintainer: Pulseaudio maintenance team Uploaders: Sjoerd Simons , Felipe Sateler -Build-Depends: android-headers-19 (>= 23) [armhf i386 amd64], - android-headers-22 (>= 23) [armhf i386 amd64], - debhelper (>= 9.20141010), +Build-Depends: debhelper (>= 9.20141010), check, dh-autoreconf, dh-exec, dpkg-dev (>= 1.17.14), intltool, libapparmor-dev [linux-any], - libandroid-properties-dev [armhf i386 amd64], libasound2-dev (>= 1.0.24) [linux-any], libasyncns-dev, libatomic-ops-dev, @@ -190,20 +187,6 @@ . This module enables PulseAudio to stream audio to an Apple Airport Express. -Package: pulseaudio-module-droid -Architecture: armhf i386 amd64 -Priority: extra -Depends: ${shlibs:Depends}, ${misc:Depends} -Conflicts: pulseaudio (<< 0.9.14-2) -Description: Android Audio HAL module for PulseAudio sound server - PulseAudio, previously known as Polypaudio, is a sound server for POSIX and - WIN32 systems. It is a drop in replacement for the ESD sound server with - much better latency, mixing/re-sampling quality and overall architecture. - . - This module enables PulseAudio to work on top of the Android Audio HAL. - . - The module is called module-droid. - Package: pulseaudio-module-trust-store Architecture: armhf i386 amd64 Priority: extra diff -Nru pulseaudio-10.0/debian/patches/0020-stream-Return-error-in-case-a-client-peeks-to-early.patch pulseaudio-10.0/debian/patches/0020-stream-Return-error-in-case-a-client-peeks-to-early.patch --- pulseaudio-10.0/debian/patches/0020-stream-Return-error-in-case-a-client-peeks-to-early.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0020-stream-Return-error-in-case-a-client-peeks-to-early.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,64 +0,0 @@ -From david.henningsson@canonical.com Mon Oct 1 15:06:56 2012 -Return-Path: -X-Original-To: diwic@mail.canonical.com -Delivered-To: diwic@mail.canonical.com -Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) - by grenadilla.canonical.com (Postfix) with ESMTP id A48961472160 - for ; Mon, 1 Oct 2012 15:06:56 +0000 (UTC) -Received: from cluster-e.mailcontrol.com (cluster-e.mailcontrol.com [85.115.58.190]) - by fiordland.canonical.com (Postfix) with ESMTP id 5DC1EA18423 - for ; Mon, 1 Oct 2012 15:06:56 +0000 (UTC) -Received: from arctowski.canonical.com (arctowski.canonical.com [91.189.94.158]) - by rly62e.srv.mailcontrol.com (MailControl) with ESMTP id q91F6t9E016745 - for ; Mon, 1 Oct 2012 16:06:55 +0100 -Received: from fiordland.canonical.com ([91.189.94.145]) - by arctowski.canonical.com with esmtp (Exim 4.71) - (envelope-from ) - id 1TIhaB-0002MW-HX - for david.henningsson@cleanmail.canonical.com; Mon, 01 Oct 2012 15:06:55 +0000 -Received: from youngberry.canonical.com (youngberry.canonical.com [91.189.89.112]) - by fiordland.canonical.com (Postfix) with ESMTP id 8706DA18423 - for ; Mon, 1 Oct 2012 15:06:55 +0000 (UTC) -Received: from hd9483857.selulk5.dyn.perspektivbredband.net ([217.72.56.87] helo=localhost.localdomain) - by youngberry.canonical.com with esmtpsa (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) - (Exim 4.71) - (envelope-from ) - id 1TIhaB-0006f2-8F; Mon, 01 Oct 2012 15:06:55 +0000 -From: David Henningsson -To: pulseaudio-discuss@lists.freedesktop.org -Cc: 1058200@bugs.launchpad.net, - David Henningsson -Subject: [PATCH] stream: Return error in case a client peeks to early -Date: Mon, 1 Oct 2012 17:06:55 +0200 -Message-Id: <1349104015-5924-1-git-send-email-david.henningsson@canonical.com> -X-Mailer: git-send-email 1.7.9.5 -X-Mailcontrol-Inbound: uq3drnD2P+ps5SfEb0fvr78+NoP1DHBZwGqKpaXB2eTgNv8D6KLIxb8+NoP1DHBZ8VSaBg0k0xw= -X-Spam-Score: -0.4 -X-Scanned-By: MailControl 9446.0 (www.mailcontrol.com) on 10.69.0.172 - -If there is no silence memblock and no data, pa_memblockq_peek can -return NULL. In this case, do not crash on an assertion in -pa_memblock_acquire, but instead return a proper error to the client. - -BugLink: http://bugs.launchpad.net/bugs/1058200 -Signed-off-by: David Henningsson ---- - src/pulse/stream.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/src/pulse/stream.c b/src/pulse/stream.c -index 2b6d306..9bb0995 100644 ---- a/src/pulse/stream.c -+++ b/src/pulse/stream.c -@@ -1598,6 +1598,8 @@ int pa_stream_peek(pa_stream *s, const void **data, size_t *length) { - return 0; - } - -+ PA_CHECK_VALIDITY(s->context, s->peek_memchunk.memblock != NULL, PA_ERR_NODATA); -+ - s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock); - } - --- -1.7.9.5 - diff -Nru pulseaudio-10.0/debian/patches/0100-switch-on-port-available-Switch-from-HDMI-to-analog-.patch pulseaudio-10.0/debian/patches/0100-switch-on-port-available-Switch-from-HDMI-to-analog-.patch --- pulseaudio-10.0/debian/patches/0100-switch-on-port-available-Switch-from-HDMI-to-analog-.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0100-switch-on-port-available-Switch-from-HDMI-to-analog-.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,54 +0,0 @@ -From f6e1ac2dd2af01088fb9fea85bd57502a214335b Mon Sep 17 00:00:00 2001 -From: David Henningsson -Date: Fri, 29 Jan 2016 20:33:22 +0100 -Subject: [PATCH] switch-on-port-available: Switch from HDMI to analog; but not - the other way around - -If you have headphones plugged in and plug in HDMI; you want sound -to stay on headphones. -If you have HDMI plugged in and you plug in headphones; you want sound -to switch to headphones. - -Hence we need to take priority into account as well when determining -whether to switch to a new profile or not. - -BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=93903 -Signed-off-by: David Henningsson ---- - src/modules/module-switch-on-port-available.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c -index 5dd9786..6f4c895 100644 ---- a/src/modules/module-switch-on-port-available.c -+++ b/src/modules/module-switch-on-port-available.c -@@ -29,7 +29,7 @@ - - #include "module-switch-on-port-available-symdef.h" - --static bool profile_good_for_output(pa_card_profile *profile) { -+static bool profile_good_for_output(pa_card_profile *profile, unsigned prio) { - pa_sink *sink; - uint32_t idx; - -@@ -49,7 +49,7 @@ static bool profile_good_for_output(pa_card_profile *profile) { - if (!sink->active_port) - continue; - -- if (sink->active_port->available != PA_AVAILABLE_NO) -+ if ((sink->active_port->available != PA_AVAILABLE_NO) && (sink->active_port->priority >= prio)) - return false; - } - -@@ -88,7 +88,7 @@ static int try_to_switch_profile(pa_device_port *port) { - switch (port->direction) { - case PA_DIRECTION_OUTPUT: - name = profile->output_name; -- good = profile_good_for_output(profile); -+ good = profile_good_for_output(profile, port->priority); - break; - - case PA_DIRECTION_INPUT: --- -2.7.3 - diff -Nru pulseaudio-10.0/debian/patches/0202-dont-probe-ucm.patch pulseaudio-10.0/debian/patches/0202-dont-probe-ucm.patch --- pulseaudio-10.0/debian/patches/0202-dont-probe-ucm.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0202-dont-probe-ucm.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,14 +0,0 @@ -Index: pulseaudio/src/modules/alsa/alsa-ucm.c -=================================================================== ---- pulseaudio.orig/src/modules/alsa/alsa-ucm.c -+++ pulseaudio/src/modules/alsa/alsa-ucm.c -@@ -1617,7 +1617,8 @@ pa_alsa_profile_set* pa_alsa_ucm_add_pro - ucm_create_profile(ucm, ps, verb, verb_name, verb_desc); - } - -- ucm_probe_profile_set(ucm, ps); -+/* Just trust that the person writing the UCM file knows what (s)he was doing, right? */ -+/* ucm_probe_profile_set(ucm, ps); */ - ps->probed = true; - - return ps; diff -Nru pulseaudio-10.0/debian/patches/0203-card-Add-hook-before-profile-changes.patch pulseaudio-10.0/debian/patches/0203-card-Add-hook-before-profile-changes.patch --- pulseaudio-10.0/debian/patches/0203-card-Add-hook-before-profile-changes.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0203-card-Add-hook-before-profile-changes.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,37 +0,0 @@ -From 3e04d944626ba4caee8a4d74a683c6c967e935ca Mon Sep 17 00:00:00 2001 -From: David Henningsson -Date: Tue, 13 Aug 2013 12:43:29 +0200 -Subject: [PATCH 203/204] card: Add hook before profile changes - -This is useful to modules that want to do things just before a -profile becomes inactive. ---- - src/pulsecore/card.c | 2 ++ - src/pulsecore/core.h | 1 + - 2 files changed, 3 insertions(+) - -Index: pulseaudio/src/pulsecore/card.c -=================================================================== ---- pulseaudio.orig/src/pulsecore/card.c -+++ pulseaudio/src/pulsecore/card.c -@@ -308,6 +308,8 @@ int pa_card_set_profile(pa_card *c, pa_c - return 0; - } - -+ pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGING], profile); -+ - /* If we're setting the initial profile, we shouldn't call set_profile(), - * because the implementations don't expect that (for historical reasons). - * We should just set c->active_profile, and the implementations will -Index: pulseaudio/src/pulsecore/core.h -=================================================================== ---- pulseaudio.orig/src/pulsecore/core.h -+++ pulseaudio/src/pulsecore/core.h -@@ -123,6 +123,7 @@ typedef enum pa_core_hook { - PA_CORE_HOOK_CARD_UNLINK, - PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED, - PA_CORE_HOOK_CARD_PROFILE_CHANGED, -+ PA_CORE_HOOK_CARD_PROFILE_CHANGING, - PA_CORE_HOOK_CARD_PROFILE_ADDED, - PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED, - PA_CORE_HOOK_CARD_SUSPEND_CHANGED, diff -Nru pulseaudio-10.0/debian/patches/0206-module-bluetooth-discover-adding-module-option-profi.patch pulseaudio-10.0/debian/patches/0206-module-bluetooth-discover-adding-module-option-profi.patch --- pulseaudio-10.0/debian/patches/0206-module-bluetooth-discover-adding-module-option-profi.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0206-module-bluetooth-discover-adding-module-option-profi.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,45 +0,0 @@ -From 6b12d4743640059c4ad9ffe055ddeebb616f6a9a Mon Sep 17 00:00:00 2001 -From: Ricardo Salveti de Araujo -Date: Sun, 22 Jun 2014 23:00:59 -0300 -Subject: [PATCH] module-bluetooth-discover: adding module option profile - -Signed-off-by: Ricardo Salveti de Araujo ---- - src/modules/bluetooth/module-bluez4-discover.c | 13 ++++++++++++- - 1 file changed, 12 insertions(+), 1 deletion(-) - -Index: pulseaudio/src/modules/bluetooth/module-bluez4-discover.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/module-bluez4-discover.c -+++ pulseaudio/src/modules/bluetooth/module-bluez4-discover.c -@@ -38,11 +38,13 @@ - PA_MODULE_AUTHOR("João Paulo Rechi Vita"); - PA_MODULE_DESCRIPTION("Detect available BlueZ 4 Bluetooth audio devices and load BlueZ 4 Bluetooth audio drivers"); - PA_MODULE_VERSION(PACKAGE_VERSION); --PA_MODULE_USAGE("sco_sink= " -+PA_MODULE_USAGE("profile= " -+ "sco_sink= " - "sco_source= "); - PA_MODULE_LOAD_ONCE(true); - - static const char* const valid_modargs[] = { -+ "profile", - "sco_sink", - "sco_source", - "async", /* deprecated */ -@@ -81,6 +83,15 @@ static pa_hook_result_t load_module_for_ - - args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path); - -+ if (pa_modargs_get_value(u->modargs, "profile", NULL)) { -+ char *profile; -+ -+ profile = pa_sprintf_malloc("%s profile=\"%s\"", args, -+ pa_modargs_get_value(u->modargs, "profile", NULL)); -+ pa_xfree(args); -+ args = profile; -+ } -+ - if (pa_modargs_get_value(u->modargs, "sco_sink", NULL) && - pa_modargs_get_value(u->modargs, "sco_source", NULL)) { - char *tmp; diff -Nru pulseaudio-10.0/debian/patches/0207-Enable-pulseaudio-droid.patch pulseaudio-10.0/debian/patches/0207-Enable-pulseaudio-droid.patch --- pulseaudio-10.0/debian/patches/0207-Enable-pulseaudio-droid.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0207-Enable-pulseaudio-droid.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,5513 +0,0 @@ -Index: pulseaudio/configure.ac -=================================================================== ---- pulseaudio.orig/configure.ac -+++ pulseaudio/configure.ac -@@ -843,6 +843,21 @@ AM_CONDITIONAL([HAVE_ALSA], [test "x$HAV - AS_IF([test "x$HAVE_ALSA" = "x1"], AC_DEFINE([HAVE_ALSA], 1, [Have ALSA?])) - AS_IF([test "x$HAVE_ALSA_UCM" = "x1"], AC_DEFINE([HAVE_ALSA_UCM], 1, [Have ALSA UCM?])) - -+#### Android Audio HAL support (optional) #### -+ -+AC_ARG_ENABLE([android-hal], -+ AS_HELP_STRING([--disable-android-hal],[Disable optional droid module (Android Audio HAL support)])) -+ -+AS_IF([test "x$enable_android_hal" != "xno"], -+ [PKG_CHECK_MODULES(LIBHARDWARE, [ libhardware ], HAVE_ANDROID=1, HAVE_ANDROID=0)], -+ HAVE_ANDROID=0) -+ -+AS_IF([test "x$enable_android_hal" = "xyes" && test "x$HAVE_ANDROID" = "x0"], -+ [AC_MSG_ERROR([*** libhardware not found])]) -+ -+AM_CONDITIONAL([HAVE_ANDROID], [test "x$HAVE_ANDROID" = "x1"]) -+AS_IF([test "x$HAVE_ANDROID" = "x1"], AC_DEFINE([HAVE_ANDROID], 1, [Have Android Audio HAL?])) -+ - #### EsounD support (optional) #### - - AC_ARG_ENABLE([esound], -@@ -1542,6 +1557,7 @@ AS_IF([test "x$HAVE_X11" = "x1"], ENABLE - AS_IF([test "x$HAVE_OSS_OUTPUT" = "x1"], ENABLE_OSS_OUTPUT=yes, ENABLE_OSS_OUTPUT=no) - AS_IF([test "x$HAVE_OSS_WRAPPER" = "x1"], ENABLE_OSS_WRAPPER=yes, ENABLE_OSS_WRAPPER=no) - AS_IF([test "x$HAVE_ALSA" = "x1"], ENABLE_ALSA=yes, ENABLE_ALSA=no) -+AS_IF([test "x$HAVE_ANDROID" = "x1"], ENABLE_ANDROID=yes, ENABLE_ANDROID=no) - AS_IF([test "x$HAVE_COREAUDIO" = "x1"], ENABLE_COREAUDIO=yes, ENABLE_COREAUDIO=no) - AS_IF([test "x$HAVE_SOLARIS" = "x1"], ENABLE_SOLARIS=yes, ENABLE_SOLARIS=no) - AS_IF([test "x$HAVE_WAVEOUT" = "x1"], ENABLE_WAVEOUT=yes, ENABLE_WAVEOUT=no) -@@ -1604,6 +1620,7 @@ echo " - Enable OSS Wrapper: ${ENABLE_OSS_WRAPPER} - Enable EsounD: ${ENABLE_ESOUND} - Enable Alsa: ${ENABLE_ALSA} -+ Enable Android Audio HAL: ${ENABLE_ANDROID} - Enable CoreAudio: ${ENABLE_COREAUDIO} - Enable Solaris: ${ENABLE_SOLARIS} - Enable WaveOut: ${ENABLE_WAVEOUT} -Index: pulseaudio/src/Makefile.am -=================================================================== ---- pulseaudio.orig/src/Makefile.am -+++ pulseaudio/src/Makefile.am -@@ -1296,6 +1296,16 @@ modlibexec_LTLIBRARIES += \ - module-alsa-source.la \ - module-alsa-card.la - -+if HAVE_ANDROID -+modlibexec_LTLIBRARIES += \ -+ libdroid-util.la \ -+ libdroid-sink.la \ -+ libdroid-source.la \ -+ module-droid-sink.la \ -+ module-droid-source.la \ -+ module-droid-card.la -+endif -+ - dist_alsaprofilesets_DATA = \ - modules/alsa/mixer/profile-sets/default.conf \ - modules/alsa/mixer/profile-sets/force-speaker.conf \ -@@ -1560,6 +1570,13 @@ SYMDEF_FILES = \ - module-filter-heuristics-symdef.h \ - module-allow-passthrough-symdef.h - -+if HAVE_ANDROID -+SYMDEF_FILES += \ -+ module-droid-sink-symdef.h \ -+ module-droid-source-symdef.h \ -+ module-droid-card-symdef.h -+endif -+ - if HAVE_ESOUND - SYMDEF_FILES += \ - module-esound-protocol-tcp-symdef.h \ -@@ -1869,6 +1886,44 @@ libalsa_util_la_LIBADD += $(DBUS_LIBS) - libalsa_util_la_CFLAGS += $(DBUS_CFLAGS) - endif - -+if HAVE_ANDROID -+libdroid_util_la_SOURCES = modules/droid/droid-util.c modules/droid/droid-util.h -+libdroid_util_la_LDFLAGS = -avoid-version -+libdroid_util_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) -+libdroid_util_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS) -+ -+libdroid_sink_la_SOURCES = modules/droid/droid-sink.c modules/droid/droid-sink.h -+libdroid_sink_la_LDFLAGS = -avoid-version -+libdroid_sink_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la -+libdroid_sink_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS) -+ -+libdroid_source_la_SOURCES = modules/droid/droid-source.c modules/droid/droid-source.h -+libdroid_source_la_LDFLAGS = -avoid-version -+libdroid_source_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la -+libdroid_source_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS) -+ -+module_droid_sink_la_SOURCES = modules/droid/module-droid-sink.c -+module_droid_sink_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_droid_sink_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la libdroid-sink.la -+module_droid_sink_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS) -+ -+module_droid_source_la_SOURCES = modules/droid/module-droid-source.c -+module_droid_source_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_droid_source_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la libdroid-source.la -+module_droid_source_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS) -+ -+module_droid_card_la_SOURCES = modules/droid/module-droid-card.c -+module_droid_card_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_droid_card_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la libdroid-sink.la libdroid-source.la -+module_droid_card_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS) -+ -+if HAVE_UDEV -+module_droid_card_la_SOURCES += modules/droid/droid-extcon.c modules/droid/droid-extcon.h -+module_droid_card_la_LIBADD += $(UDEV_LIBS) -+module_droid_card_la_CFLAGS += $(UDEV_CFLAGS) -+endif -+endif -+ - module_alsa_sink_la_SOURCES = modules/alsa/module-alsa-sink.c - module_alsa_sink_la_LDFLAGS = $(MODULE_LDFLAGS) - module_alsa_sink_la_LIBADD = $(MODULE_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la -Index: pulseaudio/src/modules/droid/droid-extcon.c -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/droid-extcon.c -@@ -0,0 +1,269 @@ -+/*** -+ This file is part of PulseAudio. -+ -+ Copyright (C) 2013 Canonical Ltd. -+ Contact: David Henningsson -+ Ricardo Salveti de Araujo -+ -+ PulseAudio is free software; you can redistribute it and/or modify -+ it under the terms of the GNU Lesser General Public License as published -+ by the Free Software Foundation; either version 2.1 of the License, -+ or (at your option) any later version. -+ -+ PulseAudio is distributed in the hope that it will be useful, but -+ WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public License -+ along with PulseAudio; if not, write to the Free Software -+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -+ USA. -+***/ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+ -+#include "droid-extcon.h" -+ -+/* For android */ -+#define EXTCON_NAME "switch" -+ -+/* TODO: Backport stuff to 4.0, remove before upstreaming */ -+#ifndef PA_PORT_AVAILABLE_YES -+#define PA_PORT_AVAILABLE_YES PA_AVAILABLE_YES -+#define PA_PORT_AVAILABLE_NO PA_AVAILABLE_NO -+#define PA_PORT_AVAILABLE_UNKNOWN PA_AVAILABLE_UNKNOWN -+#define pa_port_available_t pa_available_t -+#endif -+ -+static pa_port_available_t hponly_avail(int state) -+{ -+ return (state & 2) ? PA_PORT_AVAILABLE_YES : PA_PORT_AVAILABLE_NO; -+} -+ -+static pa_port_available_t hsmic_avail(int state) -+{ -+ return (state & 1) ? PA_PORT_AVAILABLE_YES : PA_PORT_AVAILABLE_NO; -+} -+ -+struct droid_switch { -+ char *name; -+ uint32_t current_value; -+}; -+ -+static void droid_switch_free(struct droid_switch *as) { -+ if (!as) -+ return; -+ pa_xfree(as->name); -+ pa_xfree(as); -+} -+ -+static struct droid_switch *droid_switch_new(const char *name) { -+ -+ struct droid_switch *as = NULL; -+ char *filename = pa_sprintf_malloc("/sys/class/%s/%s/state", EXTCON_NAME, name); -+ char *state = pa_read_line_from_file(filename); -+ -+ if (state == NULL) { -+ pa_log_debug("Cannot open '%s'. Skipping.", filename); -+ pa_xfree(filename); -+ return NULL; -+ } -+ pa_xfree(filename); -+ -+ as = pa_xnew0(struct droid_switch, 1); -+ as->name = pa_xstrdup(name); -+ -+ if (pa_atou(state, &as->current_value) < 0) { -+ pa_log_warn("Switch '%s' has invalid value '%s'", name, state); -+ pa_xfree(state); -+ droid_switch_free(as); -+ return NULL; -+ } -+ pa_log_debug("Switch '%s' opened with value '%s'", name, state); -+ -+ return as; -+} -+ -+struct udev_data { -+ struct udev *udev; -+ struct udev_monitor *monitor; -+ pa_io_event *event; -+}; -+ -+struct pa_droid_extcon { -+ pa_card *card; -+ struct droid_switch *h2w; -+ struct udev_data udev; -+}; -+ -+static struct droid_switch *find_matching_switch(pa_droid_extcon *u, -+ const char *devpath) { -+ -+ if (pa_streq(devpath, "/devices/virtual/" EXTCON_NAME "/h2w")) -+ return u->h2w; /* To be extended if we ever support more switches */ -+ return NULL; -+} -+ -+static void notify_ports(pa_droid_extcon *u, struct droid_switch *as) { -+ -+ pa_device_port *p; -+ void *state; -+ -+ pa_assert(as == u->h2w); /* To be extended if we ever support more switches */ -+ -+ pa_log_debug("Value of switch %s is now %d.", as->name, as->current_value); -+ -+ PA_HASHMAP_FOREACH(p, u->card->ports, state) { -+ if (p->direction == PA_DIRECTION_OUTPUT) { -+ if (!strcmp(p->name, "output-wired_headset")) -+ pa_device_port_set_available(p, hsmic_avail(as->current_value)); -+ if (!strcmp(p->name, "output-wired_headphone")) -+ pa_device_port_set_available(p, hponly_avail(as->current_value)); -+ } -+ if (p->direction == PA_DIRECTION_INPUT) { -+ if (!strcmp(p->name, "input-wired_headset")) -+ pa_device_port_set_available(p, hsmic_avail(as->current_value)); -+ } -+ } -+} -+ -+static void udev_cb(pa_mainloop_api *a, pa_io_event *e, int fd, -+ pa_io_event_flags_t events, void *userdata) { -+ -+ pa_droid_extcon *u = userdata; -+ struct udev_device *d = udev_monitor_receive_device(u->udev.monitor); -+ struct udev_list_entry *entry; -+ struct droid_switch *as; -+ const char *devpath, *state; -+ -+ if (!d) { -+ pa_log("udev_monitor_receive_device failed."); -+ pa_assert(a); -+ a->io_free(u->udev.event); -+ u->udev.event = NULL; -+ return; -+ } -+ -+ devpath = udev_device_get_devpath(d); -+ if (!devpath) { -+ pa_log("udev_device_get_devpath failed."); -+ goto out; -+ } -+ pa_log_debug("Got uevent with devpath=%s", devpath); -+ -+ as = find_matching_switch(u, devpath); -+ if (!as) -+ goto out; -+ -+ entry = udev_list_entry_get_by_name( -+ udev_device_get_properties_list_entry(d), "SWITCH_STATE"); -+ if (!entry) { -+ pa_log("udev_list_entry_get_by_name failed to find 'SWITCH_STATE' entry."); -+ goto out; -+ } -+ -+ state = udev_list_entry_get_value(entry); -+ if (!state) { -+ pa_log("udev_list_entry_get_by_name failed."); -+ goto out; -+ } -+ -+ if (pa_atou(state, &as->current_value) < 0) { -+ pa_log_warn("Switch '%s' has invalid value '%s'", as->name, state); -+ goto out; -+ } -+ -+ notify_ports(u, as); -+ -+out: -+ udev_device_unref(d); -+} -+ -+static bool init_udev(pa_droid_extcon *u, pa_core *core) { -+ -+ int fd; -+ -+ u->udev.udev = udev_new(); -+ if (!u->udev.udev) { -+ pa_log("udev_new failed."); -+ return false; -+ } -+ -+ u->udev.monitor = udev_monitor_new_from_netlink(u->udev.udev, "udev"); -+ if (!u->udev.monitor) { -+ pa_log("udev_monitor_new_from_netlink failed."); -+ return false; -+ } -+ -+ if (udev_monitor_filter_add_match_subsystem_devtype(u->udev.monitor, EXTCON_NAME, NULL) < 0) { -+ pa_log("udev_monitor_filter_add_match_subsystem_devtype failed."); -+ return false; -+ } -+ -+ if (udev_monitor_enable_receiving(u->udev.monitor) < 0) { -+ pa_log("udev_monitor_enable_receiving failed."); -+ return false; -+ } -+ -+ fd = udev_monitor_get_fd(u->udev.monitor); -+ if (fd < 0) { -+ pa_log("udev_monitor_get_fd failed"); -+ return false; -+ } -+ -+ pa_assert_se(u->udev.event = core->mainloop->io_new(core->mainloop, fd, -+ PA_IO_EVENT_INPUT, udev_cb, u)); -+ -+ return true; -+} -+ -+pa_droid_extcon *pa_droid_extcon_new(pa_core *core, pa_card *card) { -+ -+ pa_droid_extcon *u = pa_xnew0(pa_droid_extcon, 1); -+ -+ pa_assert(core); -+ pa_assert(card); -+ -+ u->card = card; -+ u->h2w = droid_switch_new("h2w"); -+ if (!u->h2w) -+ goto fail; -+ -+ if (!init_udev(u, core)) -+ goto fail; -+ -+ notify_ports(u, u->h2w); -+ -+ return u; -+ -+fail: -+ pa_droid_extcon_free(u); -+ return NULL; -+} -+ -+void pa_droid_extcon_free(pa_droid_extcon *u) { -+ -+ pa_assert(u); -+ -+ if (u->udev.event) -+ u->card->core->mainloop->io_free(u->udev.event); -+ -+ if (u->udev.monitor) -+ udev_monitor_unref(u->udev.monitor); -+ -+ if (u->udev.udev) -+ udev_unref(u->udev.udev); -+ -+ if (u->h2w) -+ droid_switch_free(u->h2w); -+ -+ pa_xfree(u); -+} -Index: pulseaudio/src/modules/droid/droid-extcon.h -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/droid-extcon.h -@@ -0,0 +1,32 @@ -+#ifndef foodroidextconhfoo -+#define foodroidextconhfoo -+ -+/*** -+ This file is part of PulseAudio. -+ -+ Copyright (C) 2013 Canonical Ltd. -+ Contact: David Henningsson -+ -+ PulseAudio is free software; you can redistribute it and/or modify -+ it under the terms of the GNU Lesser General Public License as published -+ by the Free Software Foundation; either version 2.1 of the License, -+ or (at your option) any later version. -+ -+ PulseAudio is distributed in the hope that it will be useful, but -+ WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public License -+ along with PulseAudio; if not, write to the Free Software -+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -+ USA. -+***/ -+ -+typedef struct pa_droid_extcon pa_droid_extcon; -+ -+pa_droid_extcon *pa_droid_extcon_new(pa_core *, pa_card *); -+ -+void pa_droid_extcon_free(pa_droid_extcon *); -+ -+#endif -Index: pulseaudio/src/modules/droid/droid-sink.c -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/droid-sink.c -@@ -0,0 +1,1394 @@ -+/* -+ * Copyright (C) 2013 Jolla Ltd. -+ * Copyright (C) 2010 Nokia Corporation. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include -+#include -+#define __STDC_FORMAT_MACROS -+#include -+ -+#ifdef HAVE_VALGRIND_MEMCHECK_H -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "droid-sink.h" -+#include "droid-util.h" -+ -+struct userdata { -+ pa_core *core; -+ pa_module *module; -+ pa_card *card; -+ pa_sink *sink; -+ -+ pa_thread *thread; -+ pa_thread_mq thread_mq; -+ pa_rtpoll *rtpoll; -+ int32_t routing_counter; -+ int32_t mute_routing_before; -+ int32_t mute_routing_after; -+ -+ bool deferred_volume; /* TODO */ -+ -+ pa_memblockq *memblockq; -+ pa_memchunk silence; -+ size_t buffer_count; -+ size_t buffer_size; -+ pa_usec_t buffer_latency; -+ pa_usec_t timestamp; -+ -+ audio_devices_t primary_devices; -+ audio_devices_t extra_devices; -+ -+ bool use_hw_volume; -+ bool use_voice_volume; -+ bool voice_volume_call_mode; -+ bool voice_virtual_stream; -+ char *voice_property_key; -+ char *voice_property_value; -+ pa_sink_input *voice_virtual_sink_input; -+ pa_sink_input *voice_control_sink_input; -+ pa_subscription *sink_input_subscription; -+ -+ pa_hook_slot *sink_input_put_hook_slot; -+ pa_hook_slot *sink_input_unlink_hook_slot; -+ pa_hook_slot *sink_proplist_changed_hook_slot; -+ pa_hashmap *parameters; -+ -+ pa_droid_card_data *card_data; -+ pa_droid_hw_module *hw_module; -+ struct audio_stream_out *stream_out; -+ -+ char *sco_fake_sink_name; -+ struct pa_sink *sco_fake_sink; -+}; -+ -+enum { -+ SINK_MESSAGE_DO_ROUTING = PA_SINK_MESSAGE_MAX, -+}; -+ -+#define DEFAULT_MODULE_ID "primary" -+ -+/* sink properties */ -+#define PROP_DROID_PARAMETER_PREFIX "droid.parameter." -+typedef struct droid_parameter_mapping { -+ char *key; -+ char *value; -+} droid_parameter_mapping; -+ -+/* sink-input properties */ -+#define PROP_DROID_ROUTE "droid.device.additional-route" -+ -+/* Voice call volume control. -+ * With defaults defined below, whenever sink-input with proplist key "media.role" with -+ * value "phone" connects to the sink AND voice volume control is enabled, that connected -+ * sink-input's absolute volume is used for HAL voice volume. */ -+#define DEFAULT_VOICE_CONTROL_PROPERTY_KEY "media.role" -+#define DEFAULT_VOICE_CONTROL_PROPERTY_VALUE "phone" -+ -+/* While the HAL interface supports until 0, android just use up to ~0.05 -+ * Lower values can crash the modem or cause mixer issues */ -+#define VOICE_VOLUME_MIN_VALUE 0.05 -+ -+/* Name of the fake sco sink used for HSP (used to set transport property) */ -+#define DEFAULT_SCO_FAKE_SINK "sink.fake.sco" -+#define HSP_PREVENT_SUSPEND_STR "bluetooth.hsp.prevent.suspend.transport" -+ -+static void userdata_free(struct userdata *u); -+static void set_voice_volume_from_input(struct userdata *u, pa_sink_input *i); -+static struct pa_sink *pa_sco_fake_sink_discover(pa_core *core, const char *sink_name); -+ -+static void set_primary_devices(struct userdata *u, audio_devices_t devices) { -+ pa_assert(u); -+ pa_assert(devices); -+ -+ u->primary_devices = devices; -+} -+ -+static void add_extra_devices(struct userdata *u, audio_devices_t devices) { -+ pa_assert(u); -+ pa_assert(devices); -+ -+ u->extra_devices |= devices; -+} -+ -+static void remove_extra_devices(struct userdata *u, audio_devices_t devices) { -+ pa_assert(u); -+ pa_assert(devices); -+ -+ u->extra_devices &= ~devices; -+} -+ -+static void parameter_free(droid_parameter_mapping *m) { -+ pa_assert(m); -+ -+ pa_xfree(m->key); -+ pa_xfree(m->value); -+ pa_xfree(m); -+} -+ -+static void set_fake_sco_sink_transport_property(struct userdata *u, const char *value) { -+ pa_proplist *pl; -+ -+ pa_assert(u); -+ pa_assert(value); -+ pa_assert(u->sco_fake_sink); -+ -+ pl = pa_proplist_new(); -+ pa_proplist_sets(pl, HSP_PREVENT_SUSPEND_STR, value); -+ pa_sink_update_proplist(u->sco_fake_sink, PA_UPDATE_REPLACE, pl); -+ pa_proplist_free(pl); -+} -+ -+/* Called from main context during voice calls, and from IO context during media operation. */ -+static bool do_routing(struct userdata *u) { -+ audio_devices_t routing; -+ char tmp[32]; -+ -+ pa_assert(u); -+ pa_assert(u->stream_out); -+ -+ routing = u->primary_devices | u->extra_devices; -+ -+ pa_snprintf(tmp, sizeof(tmp), "%s=%u;", AUDIO_PARAMETER_STREAM_ROUTING, routing); -+ pa_log_debug("Routing: set_parameters(): %s (%#010x)", tmp, routing); -+ pa_droid_hw_module_lock(u->hw_module); -+ u->stream_out->common.set_parameters(&u->stream_out->common, tmp); -+ pa_droid_hw_module_unlock(u->hw_module); -+ -+ return true; -+} -+ -+static bool parse_device_list(const char *str, audio_devices_t *dst) { -+ char *dev; -+ const char *state = NULL; -+ -+ pa_assert(str); -+ pa_assert(dst); -+ -+ *dst = 0; -+ -+ while ((dev = pa_split(str, "|", &state))) { -+ audio_devices_t d; -+ -+ if (!pa_string_convert_output_device_str_to_num(dev, &d)) { -+ pa_log_warn("Unknown device %s", dev); -+ pa_xfree(dev); -+ return false; -+ } -+ -+ *dst |= d; -+ -+ pa_xfree(dev); -+ } -+ -+ return true; -+} -+ -+static int thread_write_silence(struct userdata *u) { -+ const void *p; -+ ssize_t wrote; -+ -+ /* Drop our rendered audio and write silence to HAL. */ -+ pa_memblockq_drop(u->memblockq, u->buffer_size); -+ -+ /* We should be able to write everything in one go as long as memblock size -+ * is multiples of buffer_size. Even if we don't write whole buffer size -+ * here it's okay, as long as mute time isn't configured too strictly. */ -+ -+ p = pa_memblock_acquire(u->silence.memblock); -+ wrote = u->stream_out->write(u->stream_out, (const uint8_t*) p + u->silence.index, u->silence.length); -+ pa_memblock_release(u->silence.memblock); -+ -+ if (wrote < 0) -+ return -1; -+ -+ return 0; -+} -+ -+static int thread_write(struct userdata *u) { -+ pa_memchunk c; -+ const void *p; -+ ssize_t wrote; -+ -+ pa_memblockq_peek_fixed_size(u->memblockq, u->buffer_size, &c); -+ -+ /* We should be able to write everything in one go as long as memblock size -+ * is multiples of buffer_size. */ -+ -+ for (;;) { -+ p = pa_memblock_acquire(c.memblock); -+ wrote = u->stream_out->write(u->stream_out, (const uint8_t*) p + c.index, c.length); -+ pa_memblock_release(c.memblock); -+ -+ if (wrote < 0) { -+ pa_memblockq_drop(u->memblockq, c.length); -+ pa_memblock_unref(c.memblock); -+ return -1; -+ } -+ -+ if (wrote < (ssize_t) c.length) { -+ c.index += wrote; -+ c.length -= wrote; -+ continue; -+ } -+ -+ pa_memblockq_drop(u->memblockq, c.length); -+ pa_memblock_unref(c.memblock); -+ -+ break; -+ } -+ -+ return 0; -+} -+static void thread_render(struct userdata *u) { -+ size_t length; -+ size_t missing; -+ -+ length = pa_memblockq_get_length(u->memblockq); -+ missing = u->buffer_size * u->buffer_count - length; -+ -+ if (missing > 0) { -+ pa_memchunk c; -+ pa_sink_render_full(u->sink, missing, &c); -+ pa_memblockq_push_align(u->memblockq, &c); -+ pa_memblock_unref(c.memblock); -+ } -+} -+ -+static void process_rewind(struct userdata *u) { -+ size_t rewind_nbytes; -+ size_t max_rewind_nbytes; -+ size_t queue_length; -+ -+ pa_assert(u); -+ -+ if (u->sink->thread_info.rewind_nbytes == 0) { -+ pa_sink_process_rewind(u->sink, 0); -+ return; -+ } -+ -+ rewind_nbytes = u->sink->thread_info.rewind_nbytes; -+ u->sink->thread_info.rewind_nbytes = 0; -+ -+ pa_assert(rewind_nbytes > 0); -+ pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes); -+ -+ queue_length = pa_memblockq_get_length(u->memblockq); -+ if (queue_length <= u->buffer_size) -+ goto do_nothing; -+ max_rewind_nbytes = queue_length - u->buffer_size; -+ if (max_rewind_nbytes == 0) -+ goto do_nothing; -+ -+ if (rewind_nbytes > max_rewind_nbytes) -+ rewind_nbytes = max_rewind_nbytes; -+ -+ pa_memblockq_drop(u->memblockq, rewind_nbytes); -+ -+ pa_sink_process_rewind(u->sink, rewind_nbytes); -+ -+ pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); -+ return; -+ -+do_nothing: -+ pa_log_debug("Rewound 0 bytes."); -+ pa_sink_process_rewind(u->sink, 0); -+} -+ -+static void thread_func(void *userdata) { -+ struct userdata *u = userdata; -+ -+ pa_assert(u); -+ -+ pa_log_debug("Thread starting up."); -+ -+ if (u->core->realtime_scheduling) -+ pa_make_realtime(u->core->realtime_priority); -+ -+ pa_thread_mq_install(&u->thread_mq); -+ -+ u->timestamp = 0; -+ -+ for (;;) { -+ int ret; -+ -+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { -+ -+ u->timestamp = pa_rtclock_now(); -+ -+ if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) -+ process_rewind(u); -+ else -+ thread_render(u); -+ -+ if (pa_rtpoll_timer_elapsed(u->rtpoll)) { -+ pa_usec_t now, sleept; -+ -+ if (u->routing_counter == u->mute_routing_after) { -+ do_routing(u); -+ u->routing_counter--; -+ } else if (u->routing_counter > -1) { -+ thread_write_silence(u); -+ u->routing_counter--; -+ } else -+ thread_write(u); -+ -+ now = pa_rtclock_now(); -+ -+ if (now - u->timestamp > u->buffer_latency / 2) -+ sleept = 0; -+ else -+ sleept = u->buffer_latency / 2 - (now - u->timestamp) ; -+ -+ pa_rtpoll_set_timer_relative(u->rtpoll, sleept); -+ } -+ } else -+ pa_rtpoll_set_timer_disabled(u->rtpoll); -+ -+ /* Sleep */ -+ if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) -+ goto fail; -+ -+ if (ret == 0) -+ goto finish; -+ } -+ -+fail: -+ /* If this was no regular exit from the loop we have to continue -+ * processing messages until we received PA_MESSAGE_SHUTDOWN */ -+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); -+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); -+ -+finish: -+ pa_log_debug("Thread shutting down."); -+} -+ -+/* Called from IO context */ -+static int suspend(struct userdata *u) { -+ int ret; -+ size_t length; -+ -+ pa_assert(u); -+ pa_assert(u->sink); -+ pa_assert(u->stream_out); -+ -+ ret = u->stream_out->common.standby(&u->stream_out->common); -+ -+ if (ret == 0) { -+ pa_sink_set_max_request_within_thread(u->sink, 0); -+ pa_log_info("Device suspended."); -+ } else -+ pa_log("Couldn't set standby, err %d", ret); -+ -+ /* Clear memblockq */ -+ if ((length = pa_memblockq_get_length(u->memblockq)) > 0) -+ pa_memblockq_drop(u->memblockq, length); -+ -+ return ret; -+} -+ -+static int unsuspend(struct userdata *u) { -+ pa_assert(u); -+ pa_assert(u->sink); -+ -+ /* HAL resumes automagically when writing to standby stream, but let's set max request */ -+ pa_sink_set_max_request_within_thread(u->sink, u->buffer_size); -+ -+ pa_log_info("Resuming..."); -+ -+ return 0; -+} -+ -+/* Called from IO context */ -+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { -+ struct userdata *u = PA_SINK(o)->userdata; -+ -+ switch (code) { -+ case SINK_MESSAGE_DO_ROUTING: { -+ /* When mute_routing_before & mute_routing_after are 0, routing change is done -+ * immediately when next round in thread_func. Otherwise write silence until -+ * counter equals mute_routing_after, execute routing, and write silence until -+ * routing_counter is 0. */ -+ u->routing_counter = u->mute_routing_before + u->mute_routing_after; -+ return 0; -+ } -+ -+ case PA_SINK_MESSAGE_GET_LATENCY: { -+ pa_usec_t r = 0; -+ -+ /* HAL reports milliseconds */ -+ if (u->stream_out) -+ r = u->stream_out->get_latency(u->stream_out) * PA_USEC_PER_MSEC * u->buffer_count; -+ -+ *((pa_usec_t*) data) = r; -+ -+ return 0; -+ } -+ -+ case PA_SINK_MESSAGE_SET_STATE: { -+ switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { -+ case PA_SINK_SUSPENDED: { -+ int r; -+ -+ pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); -+ -+ if ((r = suspend(u)) < 0) -+ return r; -+ -+ break; -+ } -+ -+ case PA_SINK_IDLE: -+ /* Fall through */ -+ case PA_SINK_RUNNING: { -+ int r; -+ u->timestamp = 0; -+ -+ if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { -+ if ((r = unsuspend(u)) < 0) -+ return r; -+ } -+ -+ pa_rtpoll_set_timer_absolute(u->rtpoll, pa_rtclock_now()); -+ break; -+ } -+ -+ /* not needed */ -+ case PA_SINK_UNLINKED: -+ case PA_SINK_INIT: -+ case PA_SINK_INVALID_STATE: -+ ; -+ } -+ break; -+ } -+ } -+ -+ return pa_sink_process_msg(o, code, data, offset, chunk); -+} -+ -+static int sink_set_port_cb(pa_sink *s, pa_device_port *p) { -+ struct userdata *u = s->userdata; -+ pa_droid_port_data *data; -+ const char *sco_transport_enabled; -+ -+ pa_assert(u); -+ pa_assert(p); -+ -+ data = PA_DEVICE_PORT_DATA(p); -+ -+ if (!data->device) { -+ /* If there is no device defined, just return 0 to say everything is ok. -+ * Then next port change can be whatever sink port, even the one enabled -+ * before parking. */ -+ pa_log_debug("Sink set port to parking"); -+ return 0; -+ } -+ -+ pa_log_debug("Sink set port %u", data->device); -+ -+ set_primary_devices(u, data->device); -+ -+ /* See if the sco fake sink element is available (only when needed) */ -+ if ((u->sco_fake_sink == NULL) && (data->device & AUDIO_DEVICE_OUT_ALL_SCO)) -+ u->sco_fake_sink = pa_sco_fake_sink_discover(u->core, u->sco_fake_sink_name); -+ -+ /* Update the bluetooth hsp transport property before we do the routing */ -+ if (u->sco_fake_sink) { -+ sco_transport_enabled = pa_proplist_gets(u->sco_fake_sink->proplist, HSP_PREVENT_SUSPEND_STR); -+ if (sco_transport_enabled && pa_streq(sco_transport_enabled, "true")) { -+ if (data->device & ~AUDIO_DEVICE_OUT_ALL_SCO) -+ set_fake_sco_sink_transport_property(u, "false"); -+ } else if (data->device & AUDIO_DEVICE_OUT_ALL_SCO) -+ set_fake_sco_sink_transport_property(u, "true"); -+ } -+ -+ /* If we are in voice call, sink is usually in suspended state and routing change can be applied immediately. -+ * When in media use cases, do the routing change in IO thread. */ -+ if (u->use_voice_volume) -+ do_routing(u); -+ else { -+ pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL); -+ } -+ -+ return 0; -+} -+ -+static void sink_set_volume_cb(pa_sink *s) { -+ struct userdata *u = s->userdata; -+ pa_cvolume r; -+ -+ /* Shift up by the base volume */ -+ pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume); -+ -+ if (r.channels == 1) { -+ float val = pa_sw_volume_to_linear(r.values[0]); -+ pa_log_debug("Set hw volume %f", val); -+ pa_droid_hw_module_lock(u->hw_module); -+ if (u->stream_out->set_volume(u->stream_out, val, val) < 0) -+ pa_log_warn("Failed to set hw volume."); -+ pa_droid_hw_module_unlock(u->hw_module); -+ } else if (r.channels == 2) { -+ float val[2]; -+ for (unsigned i = 0; i < 2; i++) -+ val[i] = pa_sw_volume_to_linear(r.values[i]); -+ pa_log_debug("Set hw volume %f : %f", val[0], val[1]); -+ pa_droid_hw_module_lock(u->hw_module); -+ if (u->stream_out->set_volume(u->stream_out, val[0], val[1]) < 0) -+ pa_log_warn("Failed to set hw volume."); -+ pa_droid_hw_module_unlock(u->hw_module); -+ } -+} -+ -+/* Called from main context when set from input and from IO when set from sink volume. */ -+static void set_voice_volume(struct userdata *u, pa_cvolume vol) { -+ float val; -+ -+ pa_assert(u); -+ -+ val = pa_sw_volume_to_linear(pa_cvolume_avg(&vol)); -+ -+ /* Make sure our lower value is still HAL compatible */ -+ if (val < VOICE_VOLUME_MIN_VALUE) { -+ val = VOICE_VOLUME_MIN_VALUE; -+ pa_log_debug("Forcing minimal voice volume to %f", val); -+ } -+ -+ pa_log_debug("Set voice volume %f", val); -+ -+ pa_droid_hw_module_lock(u->hw_module); -+ if (u->hw_module->device->set_voice_volume(u->hw_module->device, val) < 0) -+ pa_log_warn("Failed to set voice volume."); -+ pa_droid_hw_module_unlock(u->hw_module); -+} -+ -+static void sink_set_voice_volume_cb(pa_sink *s) { -+ struct userdata *u = s->userdata; -+ pa_cvolume r; -+ -+ /* Shift up by the base volume */ -+ pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume); -+ -+ set_voice_volume(u, r); -+} -+ -+/* Called from main thread */ -+static void set_voice_volume_from_input(struct userdata *u, pa_sink_input *i) { -+ pa_cvolume vol; -+ float val; -+ -+ pa_assert_ctl_context(); -+ pa_assert(u); -+ pa_assert(i); -+ -+ pa_sink_input_get_volume(i, &vol, true); -+ -+ set_voice_volume(u, vol); -+} -+ -+static void update_volumes(struct userdata *u) { -+ int ret = -1; -+ -+ /* set_volume returns 0 if hw volume control is implemented, < 0 otherwise. */ -+ pa_droid_hw_module_lock(u->hw_module); -+ if (u->stream_out->set_volume) { -+ pa_log_debug("Probe hw volume support for %s", u->sink->name); -+ pa_log_debug("Stream out volume set to 1.0f, 1.0f"); -+ ret = u->stream_out->set_volume(u->stream_out, 1.0f, 1.0f); -+ } -+ pa_droid_hw_module_unlock(u->hw_module); -+ -+ u->use_hw_volume = (ret == 0); -+ -+ /* Apply callbacks */ -+ pa_droid_sink_set_voice_control(u->sink, false); -+} -+ -+static void set_sink_name(pa_modargs *ma, pa_sink_new_data *data, const char *module_id) { -+ const char *tmp; -+ -+ pa_assert(ma); -+ pa_assert(data); -+ -+ if ((tmp = pa_modargs_get_value(ma, "sink_name", NULL))) { -+ pa_sink_new_data_set_name(data, tmp); -+ data->namereg_fail = true; -+ pa_proplist_sets(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid sink"); -+ } else { -+ char *tt; -+ pa_assert(module_id); -+ tt = pa_sprintf_malloc("sink.%s", module_id); -+ pa_sink_new_data_set_name(data, tt); -+ pa_xfree(tt); -+ data->namereg_fail = false; -+ pa_proplist_setf(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid sink %s", module_id); -+ } -+} -+ -+/* Called from main thread */ -+static pa_sink_input *find_volume_control_sink_input(struct userdata *u) { -+ const char *val; -+ uint32_t idx; -+ pa_sink_input *i; -+ -+ pa_assert_ctl_context(); -+ pa_assert(u); -+ pa_assert(u->sink); -+ -+ PA_IDXSET_FOREACH(i, u->sink->inputs, idx) { -+ if ((val = pa_proplist_gets(i->proplist, u->voice_property_key))) { -+ if (pa_streq(val, u->voice_property_value)) { -+ return i; -+ } -+ } -+ } -+ -+ return NULL; -+} -+ -+/* Called from main thread */ -+static void sink_input_subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, struct userdata *u) { -+ pa_sink_input *i; -+ -+ pa_assert_ctl_context(); -+ -+ if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_NEW) && -+ t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_CHANGE) && -+ t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_REMOVE)) -+ return; -+ -+ if (!(i = pa_idxset_get_by_index(c->sink_inputs, idx))) -+ return; -+ -+ if (t == (PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_NEW)) { -+ if (!u->voice_control_sink_input && (i = find_volume_control_sink_input(u))) { -+ u->voice_control_sink_input = i; -+ set_voice_volume_from_input(u, i); -+ } -+ } -+ else if (t == (PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_CHANGE)) { -+ if (u->voice_control_sink_input == i) -+ set_voice_volume_from_input(u, i); -+ } -+ else if (t == (PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_REMOVE)) { -+ if (u->voice_control_sink_input == i) -+ u->voice_control_sink_input = NULL; -+ } -+} -+ -+/* For voice virtual stream, based on meego-mainvolume */ -+static void sink_input_kill_cb(pa_sink_input *i) { -+ struct userdata *u; -+ -+ pa_sink_input_assert_ref(i); -+ pa_assert_se(u = i->userdata); -+ -+ pa_sink_input_unlink(u->voice_virtual_sink_input); -+ pa_sink_input_unref(u->voice_virtual_sink_input); -+ u->voice_virtual_sink_input = NULL; -+} -+ -+/* no-op */ -+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { -+ return 0; -+} -+ -+/* no-op */ -+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { -+} -+ -+static void create_voice_virtual_stream(struct userdata *u) { -+ pa_sink_input_new_data data; -+ -+ pa_assert(u); -+ -+ if (!u->voice_virtual_stream || u->voice_virtual_sink_input) -+ return; -+ -+ pa_sink_input_new_data_init(&data); -+ -+ data.driver = __FILE__; -+ data.module = u->module; -+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, "Virtual Stream for Voice Volume Control (Droid)"); -+ pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "phone"); -+ pa_sink_input_new_data_set_sample_spec(&data, &u->core->default_sample_spec); -+ pa_sink_input_new_data_set_channel_map(&data, &u->core->default_channel_map); -+ data.flags = PA_SINK_INPUT_START_CORKED | PA_SINK_INPUT_NO_REMAP | PA_SINK_INPUT_NO_REMIX; -+ -+ pa_sink_input_new(&u->voice_virtual_sink_input, u->module->core, &data); -+ pa_sink_input_new_data_done(&data); -+ -+ if (!u->voice_virtual_sink_input) { -+ pa_log_warn("Failed to create virtual sink input."); -+ return; -+ } -+ -+ u->voice_virtual_sink_input->userdata = u; -+ u->voice_virtual_sink_input->kill = sink_input_kill_cb; -+ u->voice_virtual_sink_input->pop = sink_input_pop_cb; -+ u->voice_virtual_sink_input->process_rewind = sink_input_process_rewind_cb; -+ -+ pa_sink_input_put(u->voice_virtual_sink_input); -+ -+ pa_log_debug("Created virtual sink input for voice call volume control."); -+} -+ -+static void destroy_voice_virtual_stream(struct userdata *u) { -+ pa_assert(u); -+ -+ if (!u->voice_virtual_sink_input) -+ return; -+ -+ sink_input_kill_cb(u->voice_virtual_sink_input); -+ -+ pa_log_debug("Removed virtual stream."); -+} -+ -+/* Called from main thread */ -+void pa_droid_sink_set_voice_control(pa_sink* sink, bool enable) { -+ pa_sink_input *i; -+ struct userdata *u; -+ -+ pa_assert_ctl_context(); -+ pa_assert(sink); -+ -+ u = sink->userdata; -+ pa_assert(u); -+ pa_assert(u->sink == sink); -+ -+ pa_log_debug("Set voice control - use_voice_volume: %d, enable: %d", u->use_voice_volume, enable); -+ if (u->use_voice_volume == enable) -+ return; -+ -+ u->use_voice_volume = enable; -+ -+ if (u->use_voice_volume) { -+ pa_log_debug("Using voice volume control for %s", u->sink->name); -+ -+ if (u->voice_virtual_stream) -+ create_voice_virtual_stream(u); -+ -+ if (u->voice_volume_call_mode) { -+ /* In this case we want the sink volume to directly map into the voice volume */ -+ pa_log_debug("Sink volume is now controlling the voice volume for %s", u->sink->name); -+ -+ /* First disable module-device-restore, as we don't want to save the voice volume -+ * as the default sink volume when restoring to the default mode */ -+ pa_proplist_sets(u->sink->proplist, MODULE_DEVICE_RESTORE_SKIP_PROPERTY, "true"); -+ -+ /* Then map normal sink volume changes to voice call volume changes */ -+ pa_sink_set_set_volume_callback(u->sink, sink_set_voice_volume_cb); -+ } else { -+ pa_sink_set_set_volume_callback(u->sink, NULL); -+ -+ /* Susbcription tracking voice call volume control sink-input is set up when -+ * voice volume control is enabled. In case volume control sink-input has already -+ * connected to the sink, check for the sink-input here as well. */ -+ -+ if (!u->sink_input_subscription) -+ u->sink_input_subscription = pa_subscription_new(u->core, -+ PA_SUBSCRIPTION_MASK_SINK_INPUT, -+ (pa_subscription_cb_t) sink_input_subscription_cb, -+ u); -+ -+ if ((i = find_volume_control_sink_input(u))) { -+ u->voice_control_sink_input = i; -+ set_voice_volume_from_input(u, i); -+ } -+ } -+ } else { -+ if (u->voice_virtual_stream) -+ destroy_voice_virtual_stream(u); -+ -+ if (u->voice_volume_call_mode) { -+ /* Enable module-device-restore again now that we're back to !voicecall mode */ -+ pa_proplist_unset(u->sink->proplist, MODULE_DEVICE_RESTORE_SKIP_PROPERTY); -+ } -+ -+ if (u->sink_input_subscription) { -+ pa_subscription_free(u->sink_input_subscription); -+ u->sink_input_subscription = NULL; -+ u->voice_control_sink_input = NULL; -+ } -+ -+ if (u->use_hw_volume) { -+ pa_log_debug("Using hardware volume control for %s", u->sink->name); -+ pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); -+ } else { -+ pa_log_debug("Using software volume control for %s", u->sink->name); -+ pa_sink_set_set_volume_callback(u->sink, NULL); -+ } -+ } -+} -+ -+/* When sink-input with proper proplist variable appears, do extra routing configuration -+ * for the lifetime of that sink-input. */ -+static pa_hook_result_t sink_input_put_hook_cb(pa_core *c, pa_sink_input *sink_input, struct userdata *u) { -+ const char *dev_str; -+ const char *media_str; -+ audio_devices_t devices; -+ -+ /* Dynamic routing changes do not apply during active voice call. */ -+ if (u->use_voice_volume) -+ return PA_HOOK_OK; -+ -+ if ((dev_str = pa_proplist_gets(sink_input->proplist, PROP_DROID_ROUTE))) { -+ -+ /* Do not change routing for gstreamer pulsesink probe. Workaround for unnecessary routing changes when gst-plugin -+ * pulsesink connects to our sink. Not the best fix or the best place for a fix, but let's have this here -+ * for now anyway. */ -+ if ((media_str = pa_proplist_gets(sink_input->proplist, PA_PROP_MEDIA_NAME)) && pa_streq(media_str, "pulsesink probe")) -+ return PA_HOOK_OK; -+ -+ if (parse_device_list(dev_str, &devices) && devices) { -+ -+ pa_log_debug("Add extra route %s (%u).", dev_str, devices); -+ -+ add_extra_devices(u, devices); -+ /* post routing change */ -+ pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL); -+ } -+ } -+ -+ return PA_HOOK_OK; -+} -+ -+/* Remove extra routing when sink-inputs disappear. */ -+static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *sink_input, struct userdata *u) { -+ const char *dev_str; -+ const char *media_str; -+ audio_devices_t devices; -+ -+ /* Dynamic routing changes do not apply during active voice call. */ -+ if (u->use_voice_volume) -+ return PA_HOOK_OK; -+ -+ if ((dev_str = pa_proplist_gets(sink_input->proplist, PROP_DROID_ROUTE))) { -+ -+ /* Do not change routing for gstreamer pulsesink probe. Workaround for unnecessary routing changes when gst-plugin -+ * pulsesink connects to our sink. Not the best fix or the best place for a fix, but let's have this here -+ * for now anyway. */ -+ if ((media_str = pa_proplist_gets(sink_input->proplist, PA_PROP_MEDIA_NAME)) && pa_streq(media_str, "pulsesink probe")) -+ return PA_HOOK_OK; -+ -+ if (parse_device_list(dev_str, &devices) && devices) { -+ -+ pa_log_debug("Remove extra route %s (%u).", dev_str, devices); -+ -+ remove_extra_devices(u, devices); -+ /* post routing change */ -+ pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL); -+ } -+ } -+ -+ return PA_HOOK_OK; -+} -+ -+/* Watch for properties starting with droid.parameter. and translate them directly to -+ * HAL set_parameters() calls. */ -+static pa_hook_result_t sink_proplist_changed_hook_cb(pa_core *c, pa_sink *sink, struct userdata *u) { -+ bool changed = false; -+ const char *pkey; -+ const char *key; -+ const char *value; -+ char *tmp; -+ void *state = NULL; -+ droid_parameter_mapping *parameter = NULL; -+ -+ pa_assert(sink); -+ pa_assert(u); -+ -+ if (u->sink != sink) -+ return PA_HOOK_OK; -+ -+ while ((key = pa_proplist_iterate(sink->proplist, &state))) { -+ if (!pa_startswith(key, PROP_DROID_PARAMETER_PREFIX)) -+ continue; -+ -+ pkey = key + strlen(PROP_DROID_PARAMETER_PREFIX); -+ if (pkey[0] == '\0') -+ continue; -+ -+ changed = false; -+ -+ if (!(parameter = pa_hashmap_get(u->parameters, pkey))) { -+ parameter = pa_xnew0(droid_parameter_mapping, 1); -+ parameter->key = pa_xstrdup(pkey); -+ parameter->value = pa_xstrdup(pa_proplist_gets(sink->proplist, key)); -+ pa_hashmap_put(u->parameters, parameter->key, parameter); -+ changed = true; -+ } else { -+ value = pa_proplist_gets(sink->proplist, key); -+ if (!pa_streq(parameter->value, value)) { -+ pa_xfree(parameter->value); -+ parameter->value = pa_xstrdup(value); -+ changed = true; -+ } -+ } -+ -+ if (changed) { -+ pa_assert(parameter); -+ tmp = pa_sprintf_malloc("%s=%s;", parameter->key, parameter->value); -+ pa_log_debug("sink proplist changed: set_parameters(): %s", tmp); -+ pa_droid_hw_module_lock(u->hw_module); -+ u->stream_out->common.set_parameters(&u->stream_out->common, tmp); -+ pa_droid_hw_module_unlock(u->hw_module); -+ pa_xfree(tmp); -+ } -+ } -+ -+ return PA_HOOK_OK; -+} -+ -+static struct pa_sink *pa_sco_fake_sink_discover(pa_core *core, const char *sink_name) { -+ struct pa_sink *sink; -+ pa_idxset *idxset; -+ void *state = NULL; -+ -+ pa_assert(core); -+ pa_assert(sink_name); -+ pa_assert_se((idxset = core->sinks)); -+ -+ while ((sink = pa_idxset_iterate(idxset, &state, NULL)) != NULL) { -+ if (pa_streq(sink_name, sink->name)) { -+ pa_log_debug("Found fake SCO sink '%s'", sink_name); -+ return sink; -+ } -+ } -+ -+ return NULL; -+} -+ -+pa_sink *pa_droid_sink_new(pa_module *m, -+ pa_modargs *ma, -+ const char *driver, -+ pa_droid_card_data *card_data, -+ audio_output_flags_t flags, -+ pa_droid_mapping *am, -+ pa_card *card) { -+ -+ struct userdata *u = NULL; -+ bool deferred_volume = false; -+ bool voice_volume_call_mode = false; -+ bool voice_virtual_stream = false; -+ char *thread_name = NULL; -+ pa_sink_new_data data; -+ const char *module_id = NULL; -+ const char *tmp; -+ /* char *list = NULL; */ -+ uint32_t alternate_sample_rate; -+ uint32_t sample_rate; -+ audio_devices_t dev_out; -+ pa_sample_spec sample_spec; -+ pa_channel_map channel_map; -+ bool namereg_fail = false; -+ uint32_t total_latency; -+ pa_droid_config_audio *config = NULL; /* Only used when sink is created without card */ -+ int32_t mute_routing_before = 0; -+ int32_t mute_routing_after = 0; -+ uint32_t sink_buffer = 0; -+ int ret; -+ -+ audio_format_t hal_audio_format = 0; -+ audio_channel_mask_t hal_channel_mask = 0; -+ -+ pa_assert(m); -+ pa_assert(ma); -+ pa_assert(driver); -+ -+ deferred_volume = m->core->deferred_volume; -+ if (pa_modargs_get_value_boolean(ma, "deferred_volume", &deferred_volume) < 0) { -+ pa_log("Failed to parse deferred_volume argument."); -+ goto fail; -+ } -+ -+ if (card && am) -+ module_id = am->output->module->name; -+ else -+ module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID); -+ -+ sample_spec = m->core->default_sample_spec; -+ channel_map = m->core->default_channel_map; -+ -+ if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) { -+ pa_log("Failed to parse sample specification and channel map."); -+ goto fail; -+ } -+ -+ alternate_sample_rate = m->core->alternate_sample_rate; -+ if (pa_modargs_get_alternate_sample_rate(ma, &alternate_sample_rate) < 0) { -+ pa_log("Failed to parse alternate sample rate."); -+ goto fail; -+ } -+ -+ if ((pa_modargs_get_value_s32(ma, "mute_routing_before", &mute_routing_before) < 0) || mute_routing_before < 0) { -+ pa_log("Failed to parse mute_routing_before. Needs to be integer >= 0."); -+ goto fail; -+ } -+ -+ if ((pa_modargs_get_value_s32(ma, "mute_routing_after", &mute_routing_after) < 0) || mute_routing_after < 0) { -+ pa_log("Failed to parse mute_routing_after. Needs to be integer >= 0."); -+ goto fail; -+ } -+ -+ if (pa_modargs_get_value_u32(ma, "sink_buffer", &sink_buffer) < 0) { -+ pa_log("Failed to parse sink_buffer. Needs to be integer >= 0."); -+ goto fail; -+ } -+ -+ if (pa_modargs_get_value_boolean(ma, "voice_volume_call_mode", &voice_volume_call_mode) < 0) { -+ pa_log("Failed to parse voice_volume_call_mode. Needs to be a boolean argument."); -+ goto fail; -+ } -+ -+ if (pa_modargs_get_value_boolean(ma, "voice_virtual_stream", &voice_virtual_stream) < 0) { -+ pa_log("Failed to parse voice_virtual_stream. Needs to be a boolean argument."); -+ goto fail; -+ } -+ -+ u = pa_xnew0(struct userdata, 1); -+ u->core = m->core; -+ u->module = m; -+ u->card = card; -+ u->deferred_volume = deferred_volume; -+ u->rtpoll = pa_rtpoll_new(); -+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); -+ u->parameters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) parameter_free); -+ u->voice_volume_call_mode = voice_volume_call_mode; -+ u->voice_virtual_stream = voice_virtual_stream; -+ u->voice_property_key = pa_xstrdup(pa_modargs_get_value(ma, "voice_property_key", DEFAULT_VOICE_CONTROL_PROPERTY_KEY)); -+ u->voice_property_value = pa_xstrdup(pa_modargs_get_value(ma, "voice_property_value", DEFAULT_VOICE_CONTROL_PROPERTY_VALUE)); -+ u->sco_fake_sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sco_fake_sink", DEFAULT_SCO_FAKE_SINK)); -+ -+ if (card_data) { -+ u->card_data = card_data; -+ pa_assert(card); -+ pa_assert_se((u->hw_module = pa_droid_hw_module_get(u->core, NULL, card_data->module_id))); -+ } else { -+ /* Sink wasn't created from inside card module, so we'll need to open -+ * hw module ourselves. -+ * TODO some way to share hw module between other sinks/sources since -+ * opening same module from different places likely isn't a good thing. */ -+ -+ if (!(config = pa_droid_config_load(ma))) -+ goto fail; -+ -+ /* Ownership of config transfers to hw_module if opening of hw module succeeds. */ -+ if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id))) -+ goto fail; -+ } -+ -+ if (!pa_convert_format(sample_spec.format, CONV_FROM_PA, &hal_audio_format)) { -+ pa_log("Sample spec format %u not supported.", sample_spec.format); -+ goto fail; -+ } -+ -+ for (int i = 0; i < channel_map.channels; i++) { -+ audio_channel_mask_t c; -+ if (!pa_convert_output_channel(channel_map.map[i], CONV_FROM_PA, &c)) { -+ pa_log("Failed to convert channel map."); -+ goto fail; -+ } -+ hal_channel_mask |= c; -+ } -+ -+ struct audio_config config_out = { -+ .sample_rate = sample_spec.rate, -+ .channel_mask = hal_channel_mask, -+ .format = hal_audio_format -+ }; -+ -+ /* Default routing */ -+ dev_out = AUDIO_DEVICE_OUT_DEFAULT; -+ -+ if ((tmp = pa_modargs_get_value(ma, "output_devices", NULL))) { -+ audio_devices_t tmp_dev; -+ -+ if (parse_device_list(tmp, &tmp_dev) && tmp_dev) -+ dev_out = tmp_dev; -+ -+ pa_log_debug("Set initial devices %s", tmp); -+ } -+ -+ if (am) -+ flags = am->output->flags; -+ -+ pa_droid_hw_module_lock(u->hw_module); -+ ret = u->hw_module->device->open_output_stream(u->hw_module->device, -+ u->hw_module->stream_out_id++, -+ dev_out, -+ flags, -+ &config_out, -+ &u->stream_out); -+ pa_droid_hw_module_unlock(u->hw_module); -+ -+ if (!u->stream_out) { -+ pa_log("Failed to open output stream. (errno %d)", ret); -+ goto fail; -+ } -+ -+ if ((sample_rate = u->stream_out->common.get_sample_rate(&u->stream_out->common)) != sample_spec.rate) { -+ pa_log_warn("Requested sample rate %u but got %u instead.", sample_spec.rate, sample_rate); -+ sample_spec.rate = sample_rate; -+ } -+ -+ u->buffer_size = u->stream_out->common.get_buffer_size(&u->stream_out->common); -+ if (sink_buffer) { -+ if (sink_buffer < u->buffer_size) -+ pa_log_warn("Requested buffer size %u less than HAL reported buffer size (%u).", sink_buffer, u->buffer_size); -+ else if (sink_buffer % u->buffer_size) { -+ uint32_t trunc = (sink_buffer / u->buffer_size) * u->buffer_size; -+ pa_log_warn("Requested buffer size %u not multiple of HAL buffer size (%u). Using buffer size %u", sink_buffer, u->buffer_size, trunc); -+ u->buffer_size = trunc; -+ } else { -+ pa_log_info("Using requested buffer size %u.", sink_buffer); -+ u->buffer_size = sink_buffer; -+ } -+ } -+ -+ u->buffer_latency = pa_bytes_to_usec(u->buffer_size, &sample_spec); -+ /* Disable internal rewinding for now. */ -+ u->buffer_count = 1; -+ -+ pa_log_info("Created Android stream with device: %u flags: %u sample rate: %u channel mask: %u format: %u buffer size: %u", -+ dev_out, -+ flags, -+ sample_rate, -+ config_out.channel_mask, -+ config_out.format, -+ u->buffer_size); -+ -+ -+ u->mute_routing_before = mute_routing_before / u->buffer_size; -+ u->mute_routing_after = mute_routing_after / u->buffer_size; -+ if (u->mute_routing_before == 0 && mute_routing_before) -+ u->mute_routing_before = u->buffer_size; -+ if (u->mute_routing_after == 0 && mute_routing_after) -+ u->mute_routing_after = u->buffer_size; -+ if (u->mute_routing_before || u->mute_routing_after) -+ pa_log_debug("Mute playback when routing is changing, %u before and %u after.", -+ u->mute_routing_before * u->buffer_size, -+ u->mute_routing_after * u->buffer_size); -+ pa_silence_memchunk_get(&u->core->silence_cache, u->core->mempool, &u->silence, &sample_spec, u->buffer_size); -+ u->memblockq = pa_memblockq_new("droid-sink", 0, u->buffer_size * u->buffer_count, u->buffer_size * u->buffer_count, &sample_spec, 1, 0, 0, &u->silence); -+ -+ pa_sink_new_data_init(&data); -+ data.driver = driver; -+ data.module = m; -+ data.card = card; -+ -+ set_sink_name(ma, &data, module_id); -+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound"); -+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, "internal"); -+ -+ /* We need to give pa_modargs_get_value_boolean() a pointer to a local -+ * variable instead of using &data.namereg_fail directly, because -+ * data.namereg_fail is a bitfield and taking the address of a bitfield -+ * variable is impossible. */ -+ namereg_fail = data.namereg_fail; -+ if (pa_modargs_get_value_boolean(ma, "namereg_fail", &namereg_fail) < 0) { -+ pa_log("Failed to parse namereg_fail argument."); -+ pa_sink_new_data_done(&data); -+ goto fail; -+ } -+ data.namereg_fail = namereg_fail; -+ -+ pa_sink_new_data_set_sample_spec(&data, &sample_spec); -+ pa_sink_new_data_set_channel_map(&data, &channel_map); -+ pa_sink_new_data_set_alternate_sample_rate(&data, alternate_sample_rate); -+ -+ /* -+ if (!(list = pa_list_string_output_device(dev_out))) { -+ pa_log("Couldn't format device list string."); -+ goto fail; -+ } -+ pa_proplist_sets(data.proplist, PROP_DROID_DEVICES, list); -+ pa_xfree(list); -+ -+ if (flags) { -+ if (!(list = pa_list_string_flags(flags))) { -+ pa_log("Couldn't format flag list string."); -+ goto fail; -+ } -+ } else -+ list = NULL; -+ -+ pa_proplist_sets(data.proplist, PROP_DROID_FLAGS, list ? list : ""); -+ pa_xfree(list); -+ */ -+ -+ if (am) -+ pa_droid_add_ports(data.ports, am, card); -+ -+ u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE | PA_SINK_LATENCY | PA_SINK_FLAT_VOLUME); -+ pa_sink_new_data_done(&data); -+ -+ if (!u->sink) { -+ pa_log("Failed to create sink."); -+ goto fail; -+ } -+ -+ u->sink->userdata = u; -+ -+ u->sink->parent.process_msg = sink_process_msg; -+ -+ u->sink->set_port = sink_set_port_cb; -+ -+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); -+ pa_sink_set_rtpoll(u->sink, u->rtpoll); -+ -+ /* Rewind internal memblockq */ -+ pa_sink_set_max_rewind(u->sink, u->buffer_size * (u->buffer_count - 1)); -+ -+ thread_name = pa_sprintf_malloc("droid-sink-%s", module_id); -+ if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) { -+ pa_log("Failed to create thread."); -+ goto fail; -+ } -+ pa_xfree(thread_name); -+ thread_name = NULL; -+ -+ /* Latency consists of HAL latency + our memblockq latency */ -+ total_latency = u->stream_out->get_latency(u->stream_out) + (uint32_t) pa_bytes_to_usec(u->buffer_size * u->buffer_count, &sample_spec); -+ pa_sink_set_fixed_latency(u->sink, total_latency); -+ pa_log_debug("Set fixed latency %lu usec", (unsigned long) pa_bytes_to_usec(total_latency, &sample_spec)); -+ pa_sink_set_max_request(u->sink, u->buffer_size * u->buffer_count); -+ -+ if (u->sink->active_port) -+ sink_set_port_cb(u->sink, u->sink->active_port); -+ -+ /* Hooks to track appearance and disappearance of sink-inputs. */ -+ /* Hook a little bit earlier and later than module-role-ducking. */ -+ u->sink_input_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE+10, -+ (pa_hook_cb_t) sink_input_put_hook_cb, u); -+ u->sink_input_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_EARLY-10, -+ (pa_hook_cb_t) sink_input_unlink_hook_cb, u); -+ u->sink_proplist_changed_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_EARLY, -+ (pa_hook_cb_t) sink_proplist_changed_hook_cb, u); -+ -+ update_volumes(u); -+ -+ pa_sink_put(u->sink); -+ -+ return u->sink; -+ -+fail: -+ pa_xfree(thread_name); -+ -+ if (config) -+ pa_xfree(config); -+ -+ if (u) -+ userdata_free(u); -+ -+ return NULL; -+} -+ -+void pa_droid_sink_free(pa_sink *s) { -+ struct userdata *u; -+ -+ pa_sink_assert_ref(s); -+ pa_assert_se(u = s->userdata); -+ -+ userdata_free(u); -+} -+ -+static void userdata_free(struct userdata *u) { -+ -+ if (u->sink) -+ pa_sink_unlink(u->sink); -+ -+ if (u->thread) { -+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); -+ pa_thread_free(u->thread); -+ } -+ -+ pa_thread_mq_done(&u->thread_mq); -+ -+ if (u->sink_input_subscription) -+ pa_subscription_free(u->sink_input_subscription); -+ -+ if (u->sink_input_put_hook_slot) -+ pa_hook_slot_free(u->sink_input_put_hook_slot); -+ -+ if (u->sink_input_unlink_hook_slot) -+ pa_hook_slot_free(u->sink_input_unlink_hook_slot); -+ -+ if (u->sink_proplist_changed_hook_slot) -+ pa_hook_slot_free(u->sink_proplist_changed_hook_slot); -+ -+ if (u->sink) -+ pa_sink_unref(u->sink); -+ -+ if (u->parameters) -+ pa_hashmap_free(u->parameters); -+ -+ if (u->hw_module && u->stream_out) { -+ pa_droid_hw_module_lock(u->hw_module); -+ u->hw_module->device->close_output_stream(u->hw_module->device, u->stream_out); -+ pa_droid_hw_module_unlock(u->hw_module); -+ } -+ -+ if (u->memblockq) -+ pa_memblockq_free(u->memblockq); -+ -+ if (u->silence.memblock) -+ pa_memblock_unref(u->silence.memblock); -+ -+ if (u->hw_module) -+ pa_droid_hw_module_unref(u->hw_module); -+ -+ if (u->sco_fake_sink_name) -+ pa_xfree(u->sco_fake_sink_name); -+ -+ if (u->voice_property_key) -+ pa_xfree(u->voice_property_key); -+ if (u->voice_property_value) -+ pa_xfree(u->voice_property_value); -+ -+ pa_xfree(u); -+} -Index: pulseaudio/src/modules/droid/droid-sink.h -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/droid-sink.h -@@ -0,0 +1,57 @@ -+#ifndef foodroidsinkfoo -+#define foodroidsinkfoo -+ -+/* -+ * Copyright (C) 2013 Jolla Ltd. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#ifdef HAVE_VALGRIND_MEMCHECK_H -+#include -+#endif -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "droid-util.h" -+ -+pa_sink *pa_droid_sink_new(pa_module *m, -+ pa_modargs *ma, -+ const char *driver, -+ pa_droid_card_data *card_data, -+ audio_output_flags_t flags, -+ pa_droid_mapping *am, -+ pa_card *card); -+void pa_droid_sink_free(pa_sink *s); -+ -+void pa_droid_sink_set_voice_control(pa_sink* sink, bool enable); -+ -+#endif -Index: pulseaudio/src/modules/droid/droid-source.c -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/droid-source.c -@@ -0,0 +1,667 @@ -+/* -+ * Copyright (C) 2013 Jolla Ltd. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include -+#include -+ -+#ifdef HAVE_VALGRIND_MEMCHECK_H -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "droid-source.h" -+#include "droid-util.h" -+ -+struct userdata { -+ pa_core *core; -+ pa_module *module; -+ pa_card *card; -+ pa_source *source; -+ -+ pa_thread *thread; -+ pa_thread_mq thread_mq; -+ pa_rtpoll *rtpoll; -+ -+ pa_memchunk memchunk; -+ audio_devices_t primary_devices; -+ audio_devices_t enabled_devices; -+ bool routing_changes_enabled; -+ -+ size_t buffer_size; -+ pa_usec_t timestamp; -+ -+ pa_droid_card_data *card_data; -+ pa_droid_hw_module *hw_module; -+ audio_stream_in_t *stream; -+}; -+ -+#define DEFAULT_MODULE_ID "primary" -+ -+static void userdata_free(struct userdata *u); -+ -+static bool do_routing(struct userdata *u, audio_devices_t devices) { -+ char tmp[32]; -+ char *devlist; -+ -+ pa_assert(u); -+ pa_assert(u->stream); -+ -+ if (!u->routing_changes_enabled) { -+ pa_log_debug("Skipping routing change."); -+ return false; -+ } -+ -+ if (u->primary_devices == devices) -+ pa_log_debug("Refresh active device routing."); -+ -+ u->enabled_devices &= ~u->primary_devices; -+ u->primary_devices = devices; -+ u->enabled_devices |= u->primary_devices; -+ -+ devlist = pa_list_string_input_device(devices); -+ pa_assert(devlist); -+#ifdef DROID_DEVICE_I9305 -+ pa_snprintf(tmp, sizeof(tmp), "%s=%u", AUDIO_PARAMETER_STREAM_ROUTING, devices & ~AUDIO_DEVICE_BIT_IN); -+#else -+ pa_snprintf(tmp, sizeof(tmp), "%s=%u", AUDIO_PARAMETER_STREAM_ROUTING, devices); -+#endif -+ pa_log_debug("set_parameters(): %s (%s : %#010x)", tmp, devlist, devices); -+ pa_xfree(devlist); -+#ifdef DROID_DEVICE_MAKO -+#warning Using mako set_parameters hack. -+ u->card_data->set_parameters(u->card_data, tmp); -+#else -+ u->stream->common.set_parameters(&u->stream->common, tmp); -+#endif -+ -+ return true; -+} -+ -+static bool parse_device_list(const char *str, audio_devices_t *dst) { -+ char *dev; -+ const char *state = NULL; -+ -+ pa_assert(str); -+ pa_assert(dst); -+ -+ *dst = 0; -+ -+ while ((dev = pa_split(str, "|", &state))) { -+ audio_devices_t d; -+ -+ if (!pa_string_convert_input_device_str_to_num(dev, &d)) { -+ pa_log_warn("Unknown device %s", dev); -+ pa_xfree(dev); -+ return false; -+ } -+ -+ *dst |= d; -+ -+ pa_xfree(dev); -+ } -+ -+ return true; -+} -+ -+static int thread_read(struct userdata *u) { -+ void *p; -+ ssize_t readd; -+ pa_memchunk chunk; -+ -+ chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) u->buffer_size); -+ -+ p = pa_memblock_acquire(chunk.memblock); -+ readd = u->stream->read(u->stream, (uint8_t*) p, pa_memblock_get_length(chunk.memblock)); -+ pa_memblock_release(chunk.memblock); -+ -+ if (readd < 0) { -+ pa_log("Failed to read from stream. (err %i)", readd); -+ goto end; -+ } -+ -+ u->timestamp += pa_bytes_to_usec(readd, &u->source->sample_spec); -+ -+ chunk.index = 0; -+ chunk.length = readd; -+ -+ if (chunk.length > 0) -+ pa_source_post(u->source, &chunk); -+ -+end: -+ pa_memblock_unref(chunk.memblock); -+ -+ return 0; -+} -+ -+static void thread_func(void *userdata) { -+ struct userdata *u = userdata; -+ -+ pa_assert(u); -+ -+ pa_log_debug("Thread starting up."); -+ -+ if (u->core->realtime_scheduling) -+ pa_make_realtime(u->core->realtime_priority); -+ -+ pa_thread_mq_install(&u->thread_mq); -+ -+ u->timestamp = pa_rtclock_now(); -+ -+ for (;;) { -+ int ret; -+ -+ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { -+ thread_read(u); -+ -+ pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp); -+ } else -+ pa_rtpoll_set_timer_disabled(u->rtpoll); -+ -+ /* Sleep */ -+ if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) -+ goto fail; -+ -+ if (ret == 0) -+ goto finish; -+ -+ } -+ -+fail: -+ /* If this was no regular exit from the loop we have to continue -+ * processing messages until we received PA_MESSAGE_SHUTDOWN */ -+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); -+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); -+ -+finish: -+ pa_log_debug("Thread shutting down."); -+} -+ -+/* Called from IO context */ -+static int suspend(struct userdata *u) { -+ int ret; -+ -+ pa_assert(u); -+ pa_assert(u->stream); -+ -+ ret = u->stream->common.standby(&u->stream->common); -+ -+ if (ret == 0) -+ pa_log_info("Device suspended."); -+ -+ return ret; -+} -+ -+/* Called from IO context */ -+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { -+ struct userdata *u = PA_SOURCE(o)->userdata; -+ -+ switch (code) { -+ case PA_SOURCE_MESSAGE_SET_STATE: { -+ switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) { -+ case PA_SOURCE_SUSPENDED: { -+ int r; -+ -+ pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state)); -+ -+ if ((r = suspend(u)) < 0) -+ return r; -+ -+ break; -+ } -+ -+ case PA_SOURCE_IDLE: -+ break; -+ case PA_SOURCE_RUNNING: { -+ pa_log_info("Resuming..."); -+ u->timestamp = pa_rtclock_now(); -+ break; -+ } -+ -+ /* not needed */ -+ case PA_SOURCE_UNLINKED: -+ case PA_SOURCE_INIT: -+ case PA_SOURCE_INVALID_STATE: -+ ; -+ } -+ break; -+ } -+ } -+ -+ return pa_source_process_msg(o, code, data, offset, chunk); -+} -+ -+static int source_set_port_cb(pa_source *s, pa_device_port *p) { -+ struct userdata *u = s->userdata; -+ pa_droid_port_data *data; -+ -+ pa_assert(u); -+ pa_assert(p); -+ -+ data = PA_DEVICE_PORT_DATA(p); -+ -+ if (!data->device) { -+ /* If there is no device defined, just return 0 to say everything is ok. -+ * Then next port change can be whatever source port, even the one enabled -+ * before parking. */ -+ pa_log_debug("Source set port to parking"); -+ return 0; -+ } -+ -+ pa_log_debug("Source set port %u", data->device); -+ -+ do_routing(u, data->device); -+ -+ return 0; -+} -+ -+ -+static void source_set_name(pa_modargs *ma, pa_source_new_data *data, const char *module_id) { -+ const char *tmp; -+ -+ pa_assert(ma); -+ pa_assert(data); -+ -+ if ((tmp = pa_modargs_get_value(ma, "source_name", NULL))) { -+ pa_source_new_data_set_name(data, tmp); -+ data->namereg_fail = true; -+ pa_proplist_sets(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid source"); -+ } else { -+ char *tt; -+ pa_assert(module_id); -+ tt = pa_sprintf_malloc("source.%s", module_id); -+ pa_source_new_data_set_name(data, tt); -+ pa_xfree(tt); -+ data->namereg_fail = false; -+ pa_proplist_setf(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid source %s", module_id); -+ } -+} -+ -+static void source_get_mute_cb(pa_source *s) { -+ struct userdata *u = s->userdata; -+ bool b; -+ -+ pa_assert(u); -+ pa_assert(u->hw_module && u->hw_module->device); -+ -+ pa_droid_hw_module_lock(u->hw_module); -+ if (u->hw_module->device->get_mic_mute(u->hw_module->device, &b) < 0) { -+ pa_log("Failed to get mute state."); -+ pa_droid_hw_module_unlock(u->hw_module); -+ return; -+ } -+ pa_droid_hw_module_unlock(u->hw_module); -+ -+ s->muted = b; -+} -+ -+static void source_set_mute_cb(pa_source *s) { -+ struct userdata *u = s->userdata; -+ -+ pa_assert(u); -+ pa_assert(u->hw_module && u->hw_module->device); -+ -+ pa_droid_hw_module_lock(u->hw_module); -+ if (u->hw_module->device->set_mic_mute(u->hw_module->device, s->muted) < 0) -+ pa_log("Failed to set mute state to %smuted.", s->muted ? "" : "un"); -+ pa_droid_hw_module_unlock(u->hw_module); -+} -+ -+static void source_set_mute_control(struct userdata *u) { -+ pa_assert(u); -+ pa_assert(u->hw_module && u->hw_module->device); -+ -+ if (u->hw_module->device->set_mic_mute) { -+ pa_log_info("Using hardware mute control for %s", u->source->name); -+ pa_source_set_get_mute_callback(u->source, source_get_mute_cb); -+ pa_source_set_set_mute_callback(u->source, source_set_mute_cb); -+ } else { -+ pa_log_info("Using software mute control for %s", u->source->name); -+ pa_source_set_get_mute_callback(u->source, NULL); -+ pa_source_set_set_mute_callback(u->source, NULL); -+ } -+} -+ -+void pa_droid_source_set_routing(pa_source *s, bool enabled) { -+ struct userdata *u = s->userdata; -+ -+ pa_assert(s); -+ pa_assert(s->userdata); -+ -+ if (u->routing_changes_enabled != enabled) -+ pa_log_debug("%s source routing changes.", enabled ? "Enabling" : "Disabling"); -+ u->routing_changes_enabled = enabled; -+} -+ -+pa_source *pa_droid_source_new(pa_module *m, -+ pa_modargs *ma, -+ const char *driver, -+ pa_droid_card_data *card_data, -+ pa_droid_mapping *am, -+ pa_card *card) { -+ -+ struct userdata *u = NULL; -+ char *thread_name = NULL; -+ pa_source_new_data data; -+ const char *module_id = NULL; -+ /* const char *tmp; */ -+ uint32_t sample_rate; -+ uint32_t alternate_sample_rate; -+ audio_devices_t dev_in; -+ pa_sample_spec sample_spec; -+ pa_channel_map channel_map; -+ bool namereg_fail = false; -+ pa_droid_config_audio *config = NULL; /* Only used when source is created without card */ -+ uint32_t source_buffer = 0; -+ char audio_source[32]; -+ int ret; -+ -+ audio_format_t hal_audio_format = 0; -+ audio_channel_mask_t hal_channel_mask = 0; -+ -+ pa_assert(m); -+ pa_assert(ma); -+ pa_assert(driver); -+ -+ /* When running under card use hw module name for source by default. */ -+ if (card && ma) -+ module_id = am->input->module->name; -+ else -+ module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID); -+ -+ sample_spec = m->core->default_sample_spec; -+ channel_map = m->core->default_channel_map; -+ -+ if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) { -+ pa_log("Failed to parse sample specification and channel map."); -+ goto fail; -+ } -+ -+ alternate_sample_rate = m->core->alternate_sample_rate; -+ if (pa_modargs_get_alternate_sample_rate(ma, &alternate_sample_rate) < 0) { -+ pa_log("Failed to parse alternate sample rate."); -+ goto fail; -+ } -+ -+ if (pa_modargs_get_value_u32(ma, "source_buffer", &source_buffer) < 0) { -+ pa_log("Failed to parse source_buffer. Needs to be integer >= 0."); -+ goto fail; -+ } -+ -+ u = pa_xnew0(struct userdata, 1); -+ u->core = m->core; -+ u->module = m; -+ u->card = card; -+ u->rtpoll = pa_rtpoll_new(); -+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); -+ -+ /* Enabled routing changes by default. */ -+ u->routing_changes_enabled = true; -+ -+ if (card_data) { -+ pa_assert(card); -+ u->card_data = card_data; -+ pa_assert_se((u->hw_module = pa_droid_hw_module_get(u->core, NULL, card_data->module_id))); -+ } else { -+ /* Stand-alone source */ -+ -+ if (!(config = pa_droid_config_load(ma))) -+ goto fail; -+ -+ /* Ownership of config transfers to hw_module if opening of hw module succeeds. */ -+ if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id))) -+ goto fail; -+ } -+ -+ if (!pa_convert_format(sample_spec.format, CONV_FROM_PA, &hal_audio_format)) { -+ pa_log("Sample spec format %u not supported.", sample_spec.format); -+ goto fail; -+ } -+ -+ for (int i = 0; i < channel_map.channels; i++) { -+ audio_channel_mask_t c; -+ if (!pa_convert_input_channel(channel_map.map[i], CONV_FROM_PA, &c)) { -+ pa_log("Failed to convert channel map."); -+ goto fail; -+ } -+ hal_channel_mask |= c; -+ } -+ -+ struct audio_config config_in = { -+ .sample_rate = sample_spec.rate, -+ .channel_mask = hal_channel_mask, -+ .format = hal_audio_format -+ }; -+ -+ /* Default routing */ -+ /* FIXME So while setting routing through stream with HALv2 API fails, creation of stream -+ * requires HALv2 style device to work properly. So until that oddity is resolved we always -+ * set AUDIO_DEVICE_IN_BUILTIN_MIC as initial device here. */ -+#if 0 -+ pa_assert_se(pa_string_convert_input_device_str_to_num("AUDIO_DEVICE_IN_BUILTIN_MIC", &dev_in)); -+ -+ if ((tmp = pa_modargs_get_value(ma, "input_devices", NULL))) { -+ audio_devices_t tmp_dev; -+ -+ if (parse_device_list(tmp, &tmp_dev) && tmp_dev) -+ dev_in = tmp_dev; -+ -+ pa_log_debug("Set initial devices %s", tmp); -+ } -+#else -+ pa_log_info("FIXME: Setting AUDIO_DEVICE_IN_BUILTIN_MIC as initial device."); -+ dev_in = AUDIO_DEVICE_IN_BUILTIN_MIC; -+#endif -+ pa_droid_hw_module_lock(u->hw_module); -+ ret = u->hw_module->device->open_input_stream(u->hw_module->device, -+ u->hw_module->stream_in_id, -+ dev_in, -+ &config_in, -+ &u->stream); -+ /* On some devices the first call will fail if the config parameters are -+ * not supported, but it'll automatically set the right ones, expecting -+ * the caller to call it again, so let's try at least one more time */ -+ if (!u->stream) -+ ret = u->hw_module->device->open_input_stream(u->hw_module->device, -+ u->hw_module->stream_in_id, -+ dev_in, -+ &config_in, -+ &u->stream); -+ -+ u->hw_module->stream_in_id++; -+ pa_droid_hw_module_unlock(u->hw_module); -+ -+ if (ret < 0) { -+ pa_log("Failed to open input stream."); -+ goto fail; -+ } -+ -+ if ((sample_rate = u->stream->common.get_sample_rate(&u->stream->common)) != sample_spec.rate) { -+ pa_log_warn("Requested sample rate %u but got %u instead.", sample_spec.rate, sample_rate); -+ sample_spec.rate = sample_rate; -+ } -+ -+ u->buffer_size = u->stream->common.get_buffer_size(&u->stream->common); -+ if (source_buffer) { -+ if (source_buffer < u->buffer_size) -+ pa_log_warn("Requested buffer size %u less than HAL reported buffer size (%u).", source_buffer, u->buffer_size); -+ else if (source_buffer % u->buffer_size) { -+ uint32_t trunc = (source_buffer / u->buffer_size) * u->buffer_size; -+ pa_log_warn("Requested buffer size %u not multiple of HAL buffer size (%u). Using buffer size %u", source_buffer, u->buffer_size, trunc); -+ u->buffer_size = trunc; -+ } else { -+ pa_log_info("Using requested buffer size %u.", source_buffer); -+ u->buffer_size = source_buffer; -+ } -+ } -+ -+ pa_log_info("Created Android stream with device: %u sample rate: %u channel mask: %u format: %u buffer size: %u", -+ dev_in, -+ sample_rate, -+ config_in.channel_mask, -+ config_in.format, -+ u->buffer_size); -+ -+ /* Setting audio source to MIC by default */ -+ pa_snprintf(audio_source, sizeof(audio_source), "%s=%u", AUDIO_PARAMETER_STREAM_INPUT_SOURCE, AUDIO_SOURCE_MIC); -+ u->stream->common.set_parameters(&u->stream->common, audio_source); -+ pa_log_debug("Setting audio source to AUDIO_SOURCE_MIC by default"); -+ -+ pa_source_new_data_init(&data); -+ data.driver = driver; -+ data.module = m; -+ data.card = card; -+ -+ source_set_name(ma, &data, module_id); -+ -+ /* We need to give pa_modargs_get_value_boolean() a pointer to a local -+ * variable instead of using &data.namereg_fail directly, because -+ * data.namereg_fail is a bitfield and taking the address of a bitfield -+ * variable is impossible. */ -+ namereg_fail = data.namereg_fail; -+ if (pa_modargs_get_value_boolean(ma, "namereg_fail", &namereg_fail) < 0) { -+ pa_log("Failed to parse namereg_fail argument."); -+ pa_source_new_data_done(&data); -+ goto fail; -+ } -+ data.namereg_fail = namereg_fail; -+ -+ pa_source_new_data_set_sample_spec(&data, &sample_spec); -+ pa_source_new_data_set_channel_map(&data, &channel_map); -+ pa_source_new_data_set_alternate_sample_rate(&data, alternate_sample_rate); -+ -+ if (am) -+ pa_droid_add_ports(data.ports, am, card); -+ -+ u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE); -+ pa_source_new_data_done(&data); -+ -+ if (!u->source) { -+ pa_log("Failed to create source."); -+ goto fail; -+ } -+ -+ u->source->userdata = u; -+ -+ u->source->parent.process_msg = source_process_msg; -+ -+ source_set_mute_control(u); -+ -+ u->source->set_port = source_set_port_cb; -+ -+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); -+ pa_source_set_rtpoll(u->source, u->rtpoll); -+ -+ /* Disable rewind for droid source */ -+ pa_source_set_max_rewind(u->source, 0); -+ -+ thread_name = pa_sprintf_malloc("droid-source-%s", module_id); -+ if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) { -+ pa_log("Failed to create thread."); -+ goto fail; -+ } -+ pa_xfree(thread_name); -+ thread_name = NULL; -+ -+ pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &sample_spec)); -+ pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, &sample_spec)); -+ -+ if (u->source->active_port) -+ source_set_port_cb(u->source, u->source->active_port); -+ -+ pa_source_put(u->source); -+ -+ return u->source; -+ -+fail: -+ pa_xfree(thread_name); -+ -+ if (config) -+ pa_xfree(config); -+ -+ if (u) -+ userdata_free(u); -+ -+ return NULL; -+} -+ -+void pa_droid_source_free(pa_source *s) { -+ struct userdata *u; -+ -+ pa_source_assert_ref(s); -+ pa_assert_se(u = s->userdata); -+ -+ userdata_free(u); -+} -+ -+static void userdata_free(struct userdata *u) { -+ -+ if (u->source) -+ pa_source_unlink(u->source); -+ -+ if (u->thread) { -+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); -+ pa_thread_free(u->thread); -+ } -+ -+ pa_thread_mq_done(&u->thread_mq); -+ -+ if (u->source) -+ pa_source_unref(u->source); -+ -+ if (u->memchunk.memblock) -+ pa_memblock_unref(u->memchunk.memblock); -+ -+ if (u->hw_module && u->stream) { -+ pa_droid_hw_module_lock(u->hw_module); -+ u->hw_module->device->close_input_stream(u->hw_module->device, u->stream); -+ pa_droid_hw_module_unlock(u->hw_module); -+ } -+ -+ // Stand alone source -+ if (u->hw_module) -+ pa_droid_hw_module_unref(u->hw_module); -+ -+ pa_xfree(u); -+} -Index: pulseaudio/src/modules/droid/droid-source.h -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/droid-source.h -@@ -0,0 +1,56 @@ -+#ifndef foodroidsourcefoo -+#define foodroidsourcefoo -+ -+/* -+ * Copyright (C) 2013 Jolla Ltd. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#ifdef HAVE_VALGRIND_MEMCHECK_H -+#include -+#endif -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "droid-util.h" -+ -+pa_source *pa_droid_source_new(pa_module *m, -+ pa_modargs *ma, -+ const char *driver, -+ pa_droid_card_data *card_data, -+ pa_droid_mapping *am, -+ pa_card *card); -+void pa_droid_source_free(pa_source *s); -+ -+void pa_droid_source_set_routing(pa_source *s, bool enabled); -+ -+#endif -Index: pulseaudio/src/modules/droid/droid-util-41qc.h -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/droid-util-41qc.h -@@ -0,0 +1,274 @@ -+/* -+ * Copyright (C) 2013 Jolla Ltd. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifndef _ANDROID_UTIL_V412_H_ -+#define _ANDROID_UTIL_V412_H_ -+ -+// PulseAudio value - Android value -+ -+static uint32_t conversion_table_output_channel[][2] = { -+ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_OUT_MONO }, -+ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_OUT_FRONT_LEFT }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_OUT_FRONT_RIGHT}, -+ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_OUT_FRONT_CENTER }, -+ { PA_CHANNEL_POSITION_SUBWOOFER, AUDIO_CHANNEL_OUT_LOW_FREQUENCY }, -+ { PA_CHANNEL_POSITION_REAR_LEFT, AUDIO_CHANNEL_OUT_BACK_LEFT }, -+ { PA_CHANNEL_POSITION_REAR_RIGHT, AUDIO_CHANNEL_OUT_BACK_RIGHT }, -+ { PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER }, -+ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_OUT_BACK_CENTER }, -+ { PA_CHANNEL_POSITION_SIDE_LEFT, AUDIO_CHANNEL_OUT_SIDE_LEFT }, -+ { PA_CHANNEL_POSITION_SIDE_RIGHT, AUDIO_CHANNEL_OUT_SIDE_RIGHT }, -+ { PA_CHANNEL_POSITION_TOP_CENTER, AUDIO_CHANNEL_OUT_TOP_CENTER }, -+ { PA_CHANNEL_POSITION_TOP_FRONT_LEFT, AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT }, -+ { PA_CHANNEL_POSITION_TOP_FRONT_CENTER, AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER }, -+ { PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT }, -+ { PA_CHANNEL_POSITION_TOP_REAR_LEFT, AUDIO_CHANNEL_OUT_TOP_BACK_LEFT }, -+ { PA_CHANNEL_POSITION_TOP_REAR_CENTER, AUDIO_CHANNEL_OUT_TOP_BACK_CENTER }, -+ { PA_CHANNEL_POSITION_TOP_REAR_RIGHT, AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT } -+}; -+ -+static uint32_t conversion_table_input_channel[][2] = { -+ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_MONO }, -+ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_IN_LEFT }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_IN_RIGHT}, -+ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_IN_FRONT }, -+ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_IN_BACK }, -+ /* Following are missing suitable counterparts on PulseAudio side. */ -+ { AUDIO_CHANNEL_IN_LEFT_PROCESSED, AUDIO_CHANNEL_IN_LEFT_PROCESSED }, -+ { AUDIO_CHANNEL_IN_RIGHT_PROCESSED, AUDIO_CHANNEL_IN_RIGHT_PROCESSED }, -+ { AUDIO_CHANNEL_IN_FRONT_PROCESSED, AUDIO_CHANNEL_IN_FRONT_PROCESSED }, -+ { AUDIO_CHANNEL_IN_BACK_PROCESSED, AUDIO_CHANNEL_IN_BACK_PROCESSED }, -+ { AUDIO_CHANNEL_IN_PRESSURE, AUDIO_CHANNEL_IN_PRESSURE }, -+ { AUDIO_CHANNEL_IN_X_AXIS, AUDIO_CHANNEL_IN_X_AXIS }, -+ { AUDIO_CHANNEL_IN_Y_AXIS, AUDIO_CHANNEL_IN_Y_AXIS }, -+ { AUDIO_CHANNEL_IN_Z_AXIS, AUDIO_CHANNEL_IN_Z_AXIS }, -+ { AUDIO_CHANNEL_IN_VOICE_UPLINK, AUDIO_CHANNEL_IN_VOICE_UPLINK }, -+ { AUDIO_CHANNEL_IN_VOICE_DNLINK, AUDIO_CHANNEL_IN_VOICE_DNLINK } -+}; -+ -+static uint32_t conversion_table_format[][2] = { -+ { PA_SAMPLE_U8, AUDIO_FORMAT_PCM_8_BIT }, -+ { PA_SAMPLE_S16LE, AUDIO_FORMAT_PCM_16_BIT }, -+ { PA_SAMPLE_S32LE, AUDIO_FORMAT_PCM_32_BIT }, -+ { PA_SAMPLE_S24LE, AUDIO_FORMAT_PCM_8_24_BIT } -+}; -+ -+struct string_conversion { -+ uint32_t value; -+ const char *str; -+}; -+ -+#if defined(STRING_ENTRY) || defined(STRING_ENTRY) -+#error STRING_ENTRY already defined somewhere, fix this lib. -+#endif -+#define STRING_ENTRY(str) { str, #str } -+/* Output devices */ -+static struct string_conversion string_conversion_table_output_device[] = { -+ STRING_ENTRY(AUDIO_DEVICE_OUT_EARPIECE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_SPEAKER), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADPHONE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_AUX_DIGITAL), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_USB_ACCESSORY), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_USB_DEVICE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_FM), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_FM_TX), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ANC_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ANC_HEADPHONE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_PROXY), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_A2DP), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_SCO), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_USB), -+ { 0, NULL } -+}; -+ -+static struct string_conversion string_conversion_table_output_device_fancy[] = { -+ { AUDIO_DEVICE_OUT_EARPIECE, "output-earpiece" }, -+ { AUDIO_DEVICE_OUT_SPEAKER, "output-speaker" }, -+ { AUDIO_DEVICE_OUT_SPEAKER -+ | AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-speaker+wired_headphone" }, -+ { AUDIO_DEVICE_OUT_WIRED_HEADSET, "output-wired_headset" }, -+ { AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-wired_headphone" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO, "output-bluetooth_sco" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, "output-sco_headset" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, "output-sco_carkit" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, "output-a2dp" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, "output-a2dp_headphones" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, "output-a2dp_speaker" }, -+ { AUDIO_DEVICE_OUT_AUX_DIGITAL, "output-aux_digital" }, -+ { AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, "output-analog_dock_headset" }, -+ { AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, "output-digital_dock_headset" }, -+ { AUDIO_DEVICE_OUT_USB_ACCESSORY, "output-usb_accessory" }, -+ { AUDIO_DEVICE_OUT_USB_DEVICE, "output-usb_device" }, -+ { AUDIO_DEVICE_OUT_FM, "output-fm" }, -+ { AUDIO_DEVICE_OUT_FM_TX, "output-fm_tx" }, -+ { AUDIO_DEVICE_OUT_ANC_HEADSET, "output-anc_headset" }, -+ { AUDIO_DEVICE_OUT_ANC_HEADPHONE, "output-anc_headphone" }, -+ { AUDIO_DEVICE_OUT_PROXY, "output-proxy" }, -+ { 0, NULL } -+}; -+ -+/* Input devices */ -+static struct string_conversion string_conversion_table_input_device[] = { -+ STRING_ENTRY(AUDIO_DEVICE_IN_COMMUNICATION), -+ STRING_ENTRY(AUDIO_DEVICE_IN_AMBIENT), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BUILTIN_MIC), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_WIRED_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_AUX_DIGITAL), -+ STRING_ENTRY(AUDIO_DEVICE_IN_VOICE_CALL), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BACK_MIC), -+ STRING_ENTRY(AUDIO_DEVICE_IN_ANC_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX), -+ STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX_A2DP), -+ STRING_ENTRY(AUDIO_DEVICE_IN_PROXY), -+ { 0, NULL } -+}; -+ -+static struct string_conversion string_conversion_table_input_device_fancy[] = { -+ { AUDIO_DEVICE_IN_COMMUNICATION, "input-in_communication" }, -+ { AUDIO_DEVICE_IN_AMBIENT, "input-ambient" }, -+ { AUDIO_DEVICE_IN_BUILTIN_MIC, "input-builtin_mic" }, -+ { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, "input-bluetooth_sco_headset" }, -+ { AUDIO_DEVICE_IN_WIRED_HEADSET, "input-wired_headset" }, -+ { AUDIO_DEVICE_IN_AUX_DIGITAL, "input-aux_digital" }, -+ { AUDIO_DEVICE_IN_VOICE_CALL, "input-voice_call" }, -+ { AUDIO_DEVICE_IN_BACK_MIC, "input-back_mic" }, -+ { AUDIO_DEVICE_IN_ANC_HEADSET, "input-anc_headset" }, -+ { AUDIO_DEVICE_IN_FM_RX, "input-fm_rx" }, -+ { AUDIO_DEVICE_IN_FM_RX_A2DP, "input-fm_rx_a2dp" }, -+ { AUDIO_DEVICE_IN_PROXY, "input-in_proxy" }, -+ { 0, NULL } -+}; -+ -+/* Flags */ -+static struct string_conversion string_conversion_table_flag[] = { -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_NONE), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_DIRECT), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_PRIMARY), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_FAST), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_DEEP_BUFFER), -+ /* Qualcomm flags */ -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_LPA), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_TUNNEL), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_VOIP_RX), -+ { 0, NULL } -+}; -+ -+/* Channels */ -+static struct string_conversion string_conversion_table_output_channels[] = { -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_LOW_FREQUENCY), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_STEREO), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_QUAD), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_SURROUND), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_5POINT1), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_7POINT1), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_ALL), -+ { 0, NULL } -+}; -+static struct string_conversion string_conversion_table_input_channels[] = { -+ STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_BACK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_BACK_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_PRESSURE), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_X_AXIS), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_Y_AXIS), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_Z_AXIS), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_UPLINK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_DNLINK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_STEREO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_5POINT1), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_CALL_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_ALL), -+ { 0, NULL } -+}; -+ -+/* Formats */ -+static struct string_conversion string_conversion_table_format[] = { -+ STRING_ENTRY(AUDIO_FORMAT_DEFAULT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM), -+ STRING_ENTRY(AUDIO_FORMAT_MP3), -+ STRING_ENTRY(AUDIO_FORMAT_AMR_NB), -+ STRING_ENTRY(AUDIO_FORMAT_AMR_WB), -+ STRING_ENTRY(AUDIO_FORMAT_AAC), -+ STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V1), -+ STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V2), -+ STRING_ENTRY(AUDIO_FORMAT_VORBIS), -+ STRING_ENTRY(AUDIO_FORMAT_EVRC), -+ STRING_ENTRY(AUDIO_FORMAT_QCELP), -+ STRING_ENTRY(AUDIO_FORMAT_AC3), -+ STRING_ENTRY(AUDIO_FORMAT_AC3_PLUS), -+ STRING_ENTRY(AUDIO_FORMAT_DTS), -+ STRING_ENTRY(AUDIO_FORMAT_WMA), -+ STRING_ENTRY(AUDIO_FORMAT_WMA_PRO), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_ADIF), -+ STRING_ENTRY(AUDIO_FORMAT_EVRCB), -+ STRING_ENTRY(AUDIO_FORMAT_EVRCWB), -+ STRING_ENTRY(AUDIO_FORMAT_EAC3), -+ STRING_ENTRY(AUDIO_FORMAT_DTS_LBR), -+ STRING_ENTRY(AUDIO_FORMAT_AMR_WB_PLUS), -+ /* Currently we support only PCM formats, but keep all formats -+ * here so audio_policy.conf can be parsed. */ -+ STRING_ENTRY(AUDIO_FORMAT_PCM_16_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_8_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_32_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_8_24_BIT), -+ { 0, NULL } -+}; -+#undef STRING_ENTRY -+ -+#endif -Index: pulseaudio/src/modules/droid/droid-util-42.h -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/droid-util-42.h -@@ -0,0 +1,286 @@ -+/* -+ * Copyright (C) 2013 Jolla Ltd. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifndef _ANDROID_UTIL_V42_H_ -+#define _ANDROID_UTIL_V42_H_ -+ -+#define HAL_V2 -+ -+// PulseAudio value - Android value -+ -+static uint32_t conversion_table_output_channel[][2] = { -+ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_OUT_MONO }, -+ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_OUT_FRONT_LEFT }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_OUT_FRONT_RIGHT}, -+ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_OUT_FRONT_CENTER }, -+ { PA_CHANNEL_POSITION_SUBWOOFER, AUDIO_CHANNEL_OUT_LOW_FREQUENCY }, -+ { PA_CHANNEL_POSITION_REAR_LEFT, AUDIO_CHANNEL_OUT_BACK_LEFT }, -+ { PA_CHANNEL_POSITION_REAR_RIGHT, AUDIO_CHANNEL_OUT_BACK_RIGHT }, -+ { PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER }, -+ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_OUT_BACK_CENTER }, -+ { PA_CHANNEL_POSITION_SIDE_LEFT, AUDIO_CHANNEL_OUT_SIDE_LEFT }, -+ { PA_CHANNEL_POSITION_SIDE_RIGHT, AUDIO_CHANNEL_OUT_SIDE_RIGHT }, -+ { PA_CHANNEL_POSITION_TOP_CENTER, AUDIO_CHANNEL_OUT_TOP_CENTER }, -+ { PA_CHANNEL_POSITION_TOP_FRONT_LEFT, AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT }, -+ { PA_CHANNEL_POSITION_TOP_FRONT_CENTER, AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER }, -+ { PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT }, -+ { PA_CHANNEL_POSITION_TOP_REAR_LEFT, AUDIO_CHANNEL_OUT_TOP_BACK_LEFT }, -+ { PA_CHANNEL_POSITION_TOP_REAR_CENTER, AUDIO_CHANNEL_OUT_TOP_BACK_CENTER }, -+ { PA_CHANNEL_POSITION_TOP_REAR_RIGHT, AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT } -+}; -+ -+static uint32_t conversion_table_input_channel[][2] = { -+ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_MONO }, -+ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_IN_LEFT }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_IN_RIGHT}, -+ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_IN_FRONT }, -+ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_IN_BACK }, -+ /* Following are missing suitable counterparts on PulseAudio side. */ -+ { AUDIO_CHANNEL_IN_LEFT_PROCESSED, AUDIO_CHANNEL_IN_LEFT_PROCESSED }, -+ { AUDIO_CHANNEL_IN_RIGHT_PROCESSED, AUDIO_CHANNEL_IN_RIGHT_PROCESSED }, -+ { AUDIO_CHANNEL_IN_FRONT_PROCESSED, AUDIO_CHANNEL_IN_FRONT_PROCESSED }, -+ { AUDIO_CHANNEL_IN_BACK_PROCESSED, AUDIO_CHANNEL_IN_BACK_PROCESSED }, -+ { AUDIO_CHANNEL_IN_PRESSURE, AUDIO_CHANNEL_IN_PRESSURE }, -+ { AUDIO_CHANNEL_IN_X_AXIS, AUDIO_CHANNEL_IN_X_AXIS }, -+ { AUDIO_CHANNEL_IN_Y_AXIS, AUDIO_CHANNEL_IN_Y_AXIS }, -+ { AUDIO_CHANNEL_IN_Z_AXIS, AUDIO_CHANNEL_IN_Z_AXIS }, -+ { AUDIO_CHANNEL_IN_VOICE_UPLINK, AUDIO_CHANNEL_IN_VOICE_UPLINK }, -+ { AUDIO_CHANNEL_IN_VOICE_DNLINK, AUDIO_CHANNEL_IN_VOICE_DNLINK } -+}; -+ -+static uint32_t conversion_table_format[][2] = { -+ { PA_SAMPLE_U8, AUDIO_FORMAT_PCM_8_BIT }, -+ { PA_SAMPLE_S16LE, AUDIO_FORMAT_PCM_16_BIT }, -+ { PA_SAMPLE_S32LE, AUDIO_FORMAT_PCM_32_BIT }, -+ { PA_SAMPLE_S24LE, AUDIO_FORMAT_PCM_8_24_BIT } -+}; -+ -+struct string_conversion { -+ uint32_t value; -+ const char *str; -+}; -+ -+#if defined(STRING_ENTRY) -+#error STRING_ENTRY already defined somewhere, fix this lib. -+#endif -+#define STRING_ENTRY(str) { str, #str } -+/* Output devices */ -+static struct string_conversion string_conversion_table_output_device[] = { -+ STRING_ENTRY(AUDIO_DEVICE_OUT_EARPIECE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_SPEAKER), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADPHONE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_AUX_DIGITAL), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_USB_ACCESSORY), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_USB_DEVICE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_REMOTE_SUBMIX), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ANC_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ANC_HEADPHONE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_PROXY), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_FM), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_FM_TX), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_SPDIF), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_DEFAULT), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_A2DP), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_SCO), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_USB), -+ { 0, NULL } -+}; -+ -+static struct string_conversion string_conversion_table_output_device_fancy[] = { -+ { AUDIO_DEVICE_OUT_EARPIECE, "output-earpiece" }, -+ { AUDIO_DEVICE_OUT_SPEAKER, "output-speaker" }, -+ { AUDIO_DEVICE_OUT_SPEAKER -+ | AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-speaker+wired_headphone" }, -+ { AUDIO_DEVICE_OUT_WIRED_HEADSET, "output-wired_headset" }, -+ { AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-wired_headphone" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO, "output-bluetooth_sco" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, "output-sco_headset" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, "output-sco_carkit" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, "output-a2dp" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, "output-a2dp_headphones" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, "output-a2dp_speaker" }, -+ { AUDIO_DEVICE_OUT_AUX_DIGITAL, "output-aux_digital" }, -+ { AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, "output-analog_dock_headset" }, -+ { AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, "output-digital_dock_headset" }, -+ { AUDIO_DEVICE_OUT_USB_ACCESSORY, "output-usb_accessory" }, -+ { AUDIO_DEVICE_OUT_USB_DEVICE, "output-usb_device" }, -+ { AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "output-remote_submix" }, -+ { AUDIO_DEVICE_OUT_ANC_HEADSET, "output-anc_headset" }, -+ { AUDIO_DEVICE_OUT_ANC_HEADPHONE, "output-anc_headphone" }, -+ { AUDIO_DEVICE_OUT_PROXY, "output-proxy" }, -+ { AUDIO_DEVICE_OUT_FM, "output-fm" }, -+ { AUDIO_DEVICE_OUT_FM_TX, "output-fm_tx" }, -+ { AUDIO_DEVICE_OUT_SPDIF, "output-spdif" }, -+ { 0, NULL } -+}; -+ -+/* Input devices */ -+static struct string_conversion string_conversion_table_input_device[] = { -+ STRING_ENTRY(AUDIO_DEVICE_IN_COMMUNICATION), -+ STRING_ENTRY(AUDIO_DEVICE_IN_AMBIENT), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BUILTIN_MIC), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_WIRED_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_AUX_DIGITAL), -+ STRING_ENTRY(AUDIO_DEVICE_IN_VOICE_CALL), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BACK_MIC), -+ STRING_ENTRY(AUDIO_DEVICE_IN_REMOTE_SUBMIX), -+ STRING_ENTRY(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_USB_ACCESSORY), -+ STRING_ENTRY(AUDIO_DEVICE_IN_USB_DEVICE), -+ STRING_ENTRY(AUDIO_DEVICE_IN_ANC_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_PROXY), -+ STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX), -+ STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX_A2DP), -+ STRING_ENTRY(AUDIO_DEVICE_IN_FM), -+ STRING_ENTRY(AUDIO_DEVICE_IN_MATV), -+ STRING_ENTRY(AUDIO_DEVICE_IN_AUX_DIGITAL2), -+ STRING_ENTRY(AUDIO_DEVICE_IN_ALL_SCO), -+ STRING_ENTRY(AUDIO_DEVICE_IN_DEFAULT), -+ { 0, NULL } -+}; -+ -+static struct string_conversion string_conversion_table_input_device_fancy[] = { -+ { AUDIO_DEVICE_IN_COMMUNICATION, "input-communication" }, -+ { AUDIO_DEVICE_IN_AMBIENT, "input-ambient" }, -+ { AUDIO_DEVICE_IN_BUILTIN_MIC, "input-builtin_mic" }, -+ { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, "input-bluetooth_sco_headset" }, -+ { AUDIO_DEVICE_IN_WIRED_HEADSET, "input-wired_headset" }, -+ { AUDIO_DEVICE_IN_AUX_DIGITAL, "input-aux_digital" }, -+ { AUDIO_DEVICE_IN_VOICE_CALL, "input-voice_call" }, -+ { AUDIO_DEVICE_IN_BACK_MIC, "input-back_mic" }, -+ { AUDIO_DEVICE_IN_REMOTE_SUBMIX, "input-remote_submix" }, -+ { AUDIO_DEVICE_IN_ANC_HEADSET, "input-anc_headset" }, -+ { AUDIO_DEVICE_IN_PROXY, "input-proxy" }, -+ { AUDIO_DEVICE_IN_FM_RX, "input-fm_rx" }, -+ { AUDIO_DEVICE_IN_FM_RX_A2DP, "input-fm_rx_a2dp" }, -+ { AUDIO_DEVICE_IN_FM, "input-fm" }, -+ { AUDIO_DEVICE_IN_MATV, "input-matv" }, -+ { AUDIO_DEVICE_IN_AUX_DIGITAL2, "input-aux_digital2" }, -+ { 0, NULL } -+}; -+ -+/* Flags */ -+static struct string_conversion string_conversion_table_flag[] = { -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_NONE), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_DIRECT), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_PRIMARY), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_FAST), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_DEEP_BUFFER), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_NON_BLOCKING), -+ { 0, NULL } -+}; -+ -+/* Channels */ -+static struct string_conversion string_conversion_table_output_channels[] = { -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_LOW_FREQUENCY), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_STEREO), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_QUAD), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_SURROUND), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_5POINT1), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_7POINT1), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_ALL), -+ { 0, NULL } -+}; -+static struct string_conversion string_conversion_table_input_channels[] = { -+ STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_BACK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_BACK_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_PRESSURE), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_X_AXIS), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_Y_AXIS), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_Z_AXIS), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_UPLINK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_DNLINK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_STEREO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT_BACK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_ALL), -+ { 0, NULL } -+}; -+ -+/* Formats */ -+static struct string_conversion string_conversion_table_format[] = { -+ STRING_ENTRY(AUDIO_FORMAT_DEFAULT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM), -+ STRING_ENTRY(AUDIO_FORMAT_MP3), -+ STRING_ENTRY(AUDIO_FORMAT_AMR_NB), -+ STRING_ENTRY(AUDIO_FORMAT_AMR_WB), -+ STRING_ENTRY(AUDIO_FORMAT_AAC), -+ STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V1), -+ STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V2), -+ STRING_ENTRY(AUDIO_FORMAT_VORBIS), -+ STRING_ENTRY(AUDIO_FORMAT_MAIN_MASK), -+ STRING_ENTRY(AUDIO_FORMAT_SUB_MASK), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_16_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_8_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_32_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_8_24_BIT), -+ { 0, NULL } -+}; -+#undef STRING_ENTRY -+ -+/* Ports with availability option (for port/jack detection) */ -+static const char* port_availability[] = { -+ "output-wired_headset", -+ "output-wired_headphone", -+ "input-wired_headset", -+ NULL -+}; -+ -+ -+#endif -Index: pulseaudio/src/modules/droid/droid-util.c -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/droid-util.c -@@ -0,0 +1,1189 @@ -+/* -+ * Copyright (C) 2013 Jolla Ltd. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include -+#include -+ -+#ifdef HAVE_VALGRIND_MEMCHECK_H -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include "droid-util.h" -+ -+#include -+ -+#ifndef ANDROID_VERSION_MAJOR -+#error "ANDROID_VERSION_* not defined." -+#endif -+ -+#if ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 1 -+#include "droid-util-41qc.h" -+#elif ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR >= 2 -+#include "droid-util-42.h" -+#else -+#error "No valid ANDROID_VERSION found." -+#endif -+ -+#define CONVERT_FUNC(TABL) \ -+bool pa_convert_ ## TABL (uint32_t value, pa_conversion_field_t field, uint32_t *to_value) { \ -+ for (unsigned int i = 0; i < sizeof( conversion_table_ ## TABL )/(sizeof(uint32_t)*2); i++) { \ -+ if ( conversion_table_ ## TABL [i][field] == value) { \ -+ *to_value = conversion_table_ ## TABL [i][!field]; \ -+ return true; \ -+ } \ -+ } \ -+ return false; \ -+} struct __funny_extra_to_allow_semicolon -+ -+/* Creates convert_format convert_channel etc. -+ * bool pa_convert_func(uint32_t value, pa_conversion_field_t field, uint32_t *to_value); -+ * return true if conversion succesful */ -+CONVERT_FUNC(format); -+CONVERT_FUNC(output_channel); -+CONVERT_FUNC(input_channel); -+ -+#define DEFAULT_PRIORITY (100) -+ -+static bool string_convert_num_to_str(const struct string_conversion *list, const uint32_t value, const char **to_str) { -+ pa_assert(list); -+ pa_assert(to_str); -+ -+ pa_log_debug("Trying to convert %x to string.", value); -+ -+ for (unsigned int i = 0; list[i].str; i++) { -+ if (list[i].value == value) { -+ *to_str = list[i].str; -+ return true; -+ } -+ } -+ return false; -+} -+ -+static bool string_convert_str_to_num(const struct string_conversion *list, const char *str, uint32_t *to_value) { -+ pa_assert(list); -+ pa_assert(str); -+ pa_assert(to_value); -+ -+ pa_log_debug("Trying to convert %s to num.", str); -+ -+ for (unsigned int i = 0; list[i].str; i++) { -+ if (pa_streq(list[i].str, str)) { -+ *to_value = list[i].value; -+ return true; -+ } -+ } -+ return false; -+} -+ -+static bool check_port_availability(const char *port) { -+ pa_assert(port); -+ -+ pa_log_debug("Checking availability for port '%s'", port); -+ -+ for (unsigned int i = 0; port_availability[i]; i++) { -+ if (pa_streq(port_availability[i], port)) { -+ return true; -+ } -+ } -+ -+ return false; -+} -+ -+static char *list_string(struct string_conversion *list, uint32_t flags) { -+ char *str = NULL; -+ char *tmp; -+ -+#ifdef HAL_V2 -+ if (flags & AUDIO_DEVICE_BIT_IN) -+ flags &= ~AUDIO_DEVICE_BIT_IN; -+#endif -+ -+ for (unsigned int i = 0; list[i].str; i++) { -+#ifdef HAL_V2 -+ if (list[i].value & AUDIO_DEVICE_BIT_IN) { -+ if (popcount(list[i].value & ~AUDIO_DEVICE_BIT_IN) != 1) -+ continue; -+ } else -+#endif -+ if (popcount(list[i].value) != 1) -+ continue; -+ -+ if (flags & list[i].value) { -+ if (str) { -+ tmp = pa_sprintf_malloc("%s|%s", str, list[i].str); -+ pa_xfree(str); -+ str = tmp; -+ } else { -+ str = pa_sprintf_malloc("%s", list[i].str); -+ } -+ } -+ } -+ -+ return str; -+} -+ -+static void droid_port_free(pa_droid_port *p) { -+ pa_assert(p); -+ -+ pa_xfree(p->name); -+ pa_xfree(p->description); -+ pa_xfree(p); -+} -+ -+/* Output device */ -+bool pa_string_convert_output_device_num_to_str(audio_devices_t value, const char **to_str) { -+ return string_convert_num_to_str(string_conversion_table_output_device, (uint32_t) value, to_str); -+} -+ -+bool pa_string_convert_output_device_str_to_num(const char *str, audio_devices_t *to_value) { -+ return string_convert_str_to_num(string_conversion_table_output_device, str, (uint32_t*) to_value); -+} -+ -+char *pa_list_string_output_device(audio_devices_t devices) { -+ return list_string(string_conversion_table_output_device, devices); -+} -+ -+/* Input device */ -+bool pa_string_convert_input_device_num_to_str(audio_devices_t value, const char **to_str) { -+ return string_convert_num_to_str(string_conversion_table_input_device, (uint32_t) value, to_str); -+} -+ -+bool pa_string_convert_input_device_str_to_num(const char *str, audio_devices_t *to_value) { -+ return string_convert_str_to_num(string_conversion_table_input_device, str, (uint32_t*) to_value); -+} -+ -+char *pa_list_string_input_device(audio_devices_t devices) { -+ return list_string(string_conversion_table_input_device, devices); -+} -+ -+/* Flags */ -+bool pa_string_convert_flag_num_to_str(audio_output_flags_t value, const char **to_str) { -+ return string_convert_num_to_str(string_conversion_table_flag, (uint32_t) value, to_str); -+} -+ -+bool pa_string_convert_flag_str_to_num(const char *str, audio_output_flags_t *to_value) { -+ return string_convert_str_to_num(string_conversion_table_flag, str, (uint32_t*) to_value); -+} -+ -+char *pa_list_string_flags(audio_output_flags_t flags) { -+ return list_string(string_conversion_table_flag, flags); -+} -+ -+/* Config parser */ -+ -+#define WHITESPACE "\n\r \t" -+ -+static int parse_list(const struct string_conversion *table, const char *str, uint32_t *dst) { -+ int count = 0; -+ char *entry; -+ const char *state = NULL; -+ -+ pa_assert(table); -+ pa_assert(str); -+ pa_assert(dst); -+ -+ *dst = 0; -+ -+ while ((entry = pa_split(str, "|", &state))) { -+ uint32_t d = 0; -+ -+ if (!string_convert_str_to_num(table, entry, &d)) { -+ pa_log("Unknown entry %s", entry); -+ pa_xfree(entry); -+ return -1; -+ } -+ -+ *dst |= d; -+ count++; -+ -+ pa_xfree(entry); -+ } -+ -+ return count; -+} -+ -+static bool parse_sampling_rates(const char *str, uint32_t sampling_rates[32]) { -+ char *entry; -+ const char *state = NULL; -+ -+ pa_assert(str); -+ -+ uint32_t pos = 0; -+ while ((entry = pa_split(str, "|", &state))) { -+ int32_t val; -+ -+ if (pos == AUDIO_MAX_SAMPLING_RATES) { -+ pa_log("Too many sample rate entries (> %d)", AUDIO_MAX_SAMPLING_RATES); -+ pa_xfree(entry); -+ return false; -+ } -+ -+ if (pa_atoi(entry, &val) < 0) { -+ pa_log("Bad sample rate value %s", entry); -+ pa_xfree(entry); -+ return false; -+ } -+ -+ sampling_rates[pos++] = val; -+ -+ pa_xfree(entry); -+ -+ } -+ -+ sampling_rates[pos] = 0; -+ -+ return true; -+} -+ -+static bool parse_formats(const char *str, audio_format_t *formats) { -+ pa_assert(str); -+ pa_assert(formats); -+ -+ return parse_list(string_conversion_table_format, str, formats) > 0; -+} -+ -+static int parse_channels(const char *str, bool in_output, audio_channel_mask_t *channels) { -+ pa_assert(str); -+ pa_assert(channels); -+ -+ /* Needs to be probed later */ -+ if (pa_streq(str, "dynamic")) { -+ *channels = 0; -+ return true; -+ } -+ -+ if (in_output) -+ return parse_list(string_conversion_table_output_channels, str, channels); -+ else -+ return parse_list(string_conversion_table_input_channels, str, channels); -+} -+ -+static bool parse_devices(const char *str, bool in_output, audio_devices_t *devices) { -+ pa_assert(str); -+ pa_assert(devices); -+ -+ if (in_output) -+ return parse_list(string_conversion_table_output_device, str, devices) > 0; -+ else -+ return parse_list(string_conversion_table_input_device, str, devices) > 0; -+} -+ -+static bool parse_flags(const char *str, audio_output_flags_t *flags) { -+ pa_assert(str); -+ pa_assert(flags); -+ -+ return parse_list(string_conversion_table_flag, str, flags) > 0; -+} -+ -+bool pa_parse_droid_audio_config(const char *filename, pa_droid_config_audio *config) { -+ FILE *f; -+ int n = 0; -+ bool ret = true; -+ -+ enum config_loc { -+ IN_ROOT = 0, -+ IN_GLOBAL = 1, -+ IN_HW_MODULES = 1, -+ IN_MODULE = 2, -+ IN_OUTPUT_INPUT = 3, -+ IN_CONFIG = 4 -+ } loc = IN_ROOT; -+ -+ -+ bool in_global = false; -+ bool in_output = true; -+ -+ pa_droid_config_hw_module *module = NULL; -+ pa_droid_config_output *output = NULL; -+ pa_droid_config_input *input = NULL; -+ -+ pa_assert(filename); -+ pa_assert(config); -+ -+ memset(config, 0, sizeof(pa_droid_config_audio)); -+ -+ f = fopen(filename, "r"); -+ -+ if (!f) { -+ pa_log_info("Failed to open config file (%s): %s", filename, pa_cstrerror(errno)); -+ ret = false; -+ goto finish; -+ } -+ -+ pa_lock_fd(fileno(f), 1); -+ -+ while (!feof(f)) { -+ char ln[512]; -+ char *d, *v, *val; -+ -+ if (!fgets(ln, sizeof(ln), f)) -+ break; -+ -+ n++; -+ -+ pa_strip_nl(ln); -+ -+ if (ln[0] == '#' || !*ln ) -+ continue; -+ -+ /* Enter section */ -+ if (ln[strlen(ln)-1] == '{') { -+ d = ln+strspn(ln, WHITESPACE); -+ v = d; -+ d = v+strcspn(v, WHITESPACE); -+ d[0] = '\0'; -+ -+ if (!*v) { -+ pa_log(__FILE__ ": [%s:%u] failed to parse line - too few words", filename, n); -+ goto finish; -+ } -+ -+ switch (loc) { -+ case IN_ROOT: -+ if (pa_streq(v, GLOBAL_CONFIG_TAG)) { -+ in_global = true; -+ loc = IN_GLOBAL; -+ } -+ else if (pa_streq(v, AUDIO_HW_MODULE_TAG)) -+ loc = IN_HW_MODULES; -+ else { -+ pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown field (%s)", filename, n, v); -+ ret = false; -+ goto finish; -+ } -+ break; -+ -+ case IN_HW_MODULES: -+ module = &config->hw_modules[config->hw_modules_size]; -+ config->hw_modules_size++; -+ strncpy(module->name, v, AUDIO_HARDWARE_MODULE_ID_MAX_LEN); -+ module->config = config; -+ loc = IN_MODULE; -+ pa_log_debug("config: New module: %s", module->name); -+ break; -+ -+ case IN_MODULE: -+ if (pa_streq(v, OUTPUTS_TAG)) { -+ loc = IN_OUTPUT_INPUT; -+ in_output = true; -+ } else if (pa_streq(v, INPUTS_TAG)) { -+ loc = IN_OUTPUT_INPUT; -+ in_output = false; -+ } else { -+ pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown field (%s)", filename, n, v); -+ ret = false; -+ goto finish; -+ } -+ break; -+ -+ case IN_OUTPUT_INPUT: -+ pa_assert(module); -+ -+ if (in_output) { -+ output = &module->outputs[module->outputs_size]; -+ module->outputs_size++; -+ strncpy(output->name, v, AUDIO_HARDWARE_MODULE_ID_MAX_LEN); -+ output->module = module; -+ loc = IN_CONFIG; -+ pa_log_debug("config: %s: New output: %s", module->name, output->name); -+ } else { -+ input = &module->inputs[module->inputs_size]; -+ module->inputs_size++; -+ strncpy(input->name, v, AUDIO_HARDWARE_MODULE_ID_MAX_LEN); -+ input->module = module; -+ loc = IN_CONFIG; -+ pa_log_debug("config: %s: New input: %s", module->name, input->name); -+ } -+ break; -+ -+ case IN_CONFIG: -+ pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown field in config (%s)", filename, n, v); -+ ret = false; -+ goto finish; -+ } -+ -+ continue; -+ } -+ -+ /* Exit section */ -+ if (ln[strlen(ln)-1] == '}') { -+ if (loc == IN_ROOT) { -+ pa_log(__FILE__ ": [%s:%u] failed to parse line - extra closing bracket", filename, n); -+ ret = false; -+ goto finish; -+ } -+ -+ loc--; -+ if (loc == IN_MODULE) { -+ if (in_output) -+ output = NULL; -+ else -+ input = NULL; -+ } -+ if (loc == IN_ROOT) -+ module = NULL; -+ -+ in_global = false; -+ -+ continue; -+ } -+ -+ /* Parse global configuration */ -+ if (in_global) { -+ bool success = false; -+ -+ d = ln+strspn(ln, WHITESPACE); -+ v = d; -+ d = v+strcspn(v, WHITESPACE); -+ -+ val = d+strspn(d, WHITESPACE); -+ d[0] = '\0'; -+ d = val+strcspn(val, WHITESPACE); -+ d[0] = '\0'; -+ -+ if (pa_streq(v, ATTACHED_OUTPUT_DEVICES_TAG)) -+ success = parse_devices(val, true, &config->global_config.attached_output_devices); -+ else if (pa_streq(v, DEFAULT_OUTPUT_DEVICE_TAG)) -+ success = parse_devices(val, true, &config->global_config.default_output_device); -+ else if (pa_streq(v, ATTACHED_INPUT_DEVICES_TAG)) -+ success = parse_devices(val, false, &config->global_config.attached_input_devices); -+ else if (pa_streq(v, SPEAKER_DRC_ENABLED_TAG)) { -+ pa_log(__FILE__ ": speaker drc is not yet supported, skipping", filename); -+ success = true; -+ } else { -+ pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown config entry %s", filename, n, v); -+ success = false; -+ } -+ -+ if (!success) { -+ ret = false; -+ goto finish; -+ } -+ } -+ -+ /* Parse per-output or per-input configuration */ -+ if (loc == IN_CONFIG) { -+ bool success = false; -+ -+ pa_assert(module); -+ -+ d = ln+strspn(ln, WHITESPACE); -+ v = d; -+ d = v+strcspn(v, WHITESPACE); -+ -+ val = d+strspn(d, WHITESPACE); -+ d[0] = '\0'; -+ d = val+strcspn(val, WHITESPACE); -+ d[0] = '\0'; -+ -+ -+ if ((in_output && !output) || (!in_output && !input)) { -+ pa_log(__FILE__ ": [%s:%u] failed to parse line", filename, n); -+ ret = false; -+ goto finish; -+ } -+ -+ if (pa_streq(v, SAMPLING_RATES_TAG)) -+ success = parse_sampling_rates(val, in_output ? output->sampling_rates : input->sampling_rates); -+ else if (pa_streq(v, FORMATS_TAG)) -+ success = parse_formats(val, in_output ? &output->formats : &input->formats); -+ else if (pa_streq(v, CHANNELS_TAG)) { -+ if (in_output) -+ success = (parse_channels(val, true, &output->channel_masks) > 0); -+ else -+ success = (parse_channels(val, false, &input->channel_masks) > 0); -+ } else if (pa_streq(v, DEVICES_TAG)) { -+ if (in_output) -+ success = parse_devices(val, true, &output->devices); -+ else -+ success = parse_devices(val, false, &input->devices); -+ } else if (pa_streq(v, FLAGS_TAG)) { -+ if (in_output) -+ success = parse_flags(val, &output->flags); -+ else { -+ pa_log(__FILE__ ": [%s:%u] failed to parse line - output flags inside input definition", filename, n); -+ success = false; -+ } -+ } else { -+ pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown config entry %s", filename, n, v); -+ success = false; -+ } -+ -+ if (!success) { -+ ret = false; -+ goto finish; -+ } -+ } -+ } -+ -+ pa_log_info("Parsed config file (%s): %u modules.", filename, config->hw_modules_size); -+ -+finish: -+ if (f) { -+ pa_lock_fd(fileno(f), 0); -+ fclose(f); -+ } -+ -+ return ret; -+} -+ -+ -+const pa_droid_config_output *pa_droid_config_find_output(const pa_droid_config_hw_module *module, const char *name) { -+ pa_assert(module); -+ pa_assert(name); -+ -+ for (unsigned i = 0; i < module->outputs_size; i++) { -+ if (pa_streq(name, module->outputs[i].name)) -+ return &module->outputs[i]; -+ } -+ -+ return NULL; -+} -+ -+const pa_droid_config_input *pa_droid_config_find_input(const pa_droid_config_hw_module *module, const char *name) { -+ pa_assert(module); -+ pa_assert(name); -+ -+ for (unsigned i = 0; i < module->inputs_size; i++) { -+ if (pa_streq(name, module->inputs[i].name)) -+ return &module->inputs[i]; -+ } -+ -+ return NULL; -+} -+ -+const pa_droid_config_hw_module *pa_droid_config_find_module(const pa_droid_config_audio *config, const char* module_id) { -+ pa_assert(config); -+ pa_assert(module_id); -+ -+ for (unsigned i = 0; i < config->hw_modules_size; i++) { -+ if (pa_streq(module_id, config->hw_modules[i].name)) -+ return &config->hw_modules[i]; -+ } -+ -+ return NULL; -+} -+ -+pa_droid_profile *pa_droid_profile_new(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input) { -+ pa_droid_profile *p; -+ -+ pa_assert(ps); -+ pa_assert(output); -+ -+ p = pa_xnew0(pa_droid_profile, 1); -+ p->profile_set = ps; -+ p->module = output->module; -+ p->name = pa_sprintf_malloc("%s%s%s", output->name, input ? "-" : "", input ? input->name : ""); -+ p->description = pa_sprintf_malloc("%s output%s%s%s", output->name, -+ input ? " and " : "", -+ input ? input->name : "", -+ input ? " input." : ""); -+ p->priority = DEFAULT_PRIORITY; -+ if (pa_streq(output->name, "primary")) { -+ p->priority += DEFAULT_PRIORITY; -+ -+ if (input && pa_streq(input->name, "primary")) -+ p->priority += DEFAULT_PRIORITY; -+ } -+ -+ if (output) -+ p->output = pa_droid_mapping_get(ps, PA_DIRECTION_OUTPUT, output); -+ if (input) -+ p->input = pa_droid_mapping_get(ps, PA_DIRECTION_INPUT, input); -+ -+ pa_hashmap_put(ps->profiles, p->name, p); -+ -+ return p; -+} -+ -+static void add_profile(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input) { -+ pa_droid_profile *ap; -+ -+ pa_log_debug("New profile: %s-%s", output->name, input ? input->name : "no input"); -+ -+ ap = pa_droid_profile_new(ps, output, input); -+ -+ pa_hashmap_put(ps->profiles, ap->name, ap); -+} -+ -+pa_droid_profile_set *pa_droid_profile_set_new(const pa_droid_config_hw_module *module) { -+ pa_droid_profile_set *ps; -+ -+ pa_assert(module); -+ -+ ps = pa_xnew0(pa_droid_profile_set, 1); -+ ps->config = module->config; -+ ps->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_droid_profile_free); -+ ps->output_mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_droid_mapping_free); -+ ps->input_mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_droid_mapping_free); -+ ps->all_ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) droid_port_free); -+ -+ /* Each distinct hw module output matches one profile. If there are multiple inputs -+ * combinations are made so that all possible outputs and inputs can be selected. -+ * So for outputs "primary" and "hdmi" and input "primary" profiles -+ * "primary-primary" and "hdmi-primary" are created. */ -+ -+ for (unsigned o = 0; o < module->outputs_size; o++) { -+ -+ if (module->inputs_size > 0) { -+ for (unsigned i = 0; i < module->inputs_size; i++) { -+ add_profile(ps, &module->outputs[o], &module->inputs[i]); -+ } -+ } else -+ add_profile(ps, &module->outputs[o], NULL); -+ } -+ -+ return ps; -+} -+ -+void pa_droid_mapping_free(pa_droid_mapping *am) { -+ pa_assert(am); -+ -+ pa_xfree(am->name); -+ pa_proplist_free(am->proplist); -+ pa_idxset_free(am->ports, NULL); -+ pa_xfree(am); -+} -+ -+void pa_droid_profile_free(pa_droid_profile *ap) { -+ pa_assert(ap); -+ -+ pa_xfree(ap->name); -+ pa_xfree(ap->description); -+ pa_xfree(ap); -+} -+ -+void pa_droid_profile_set_free(pa_droid_profile_set *ps) { -+ pa_assert(ps); -+ -+ if (ps->output_mappings) -+ pa_hashmap_free(ps->output_mappings); -+ -+ if (ps->input_mappings) -+ pa_hashmap_free(ps->input_mappings); -+ -+ if (ps->all_ports) -+ pa_hashmap_free(ps->all_ports); -+ -+ if (ps->profiles) -+ pa_hashmap_free(ps->profiles); -+ -+ pa_xfree(ps); -+} -+ -+static pa_droid_port *create_o_port(pa_droid_mapping *am, uint32_t device, const char *name, const char *description) { -+ pa_droid_port *p; -+ char *desc; -+ -+ pa_assert(am); -+ pa_assert(name); -+ -+ pa_log_debug(" New output port %s", name); -+ p = pa_xnew0(pa_droid_port, 1); -+ -+ p->mapping = am; -+ p->name = pa_xstrdup(name); -+ if (description) { -+ p->description = pa_xstrdup(description); -+ } else { -+ desc = pa_replace(name, "output-", "Output to "); -+ p->description = pa_replace(desc, "_", " "); -+ pa_xfree(desc); -+ } -+ p->priority = DEFAULT_PRIORITY; -+ p->device = device; -+ -+ if (am->profile_set->config->global_config.attached_output_devices & device) -+ p->priority += DEFAULT_PRIORITY; -+ -+ if (am->profile_set->config->global_config.default_output_device & device) -+ p->priority += DEFAULT_PRIORITY; -+ -+ if (check_port_availability(p->name)) -+ p->priority += (DEFAULT_PRIORITY * 3); -+ -+ return p; -+} -+ -+static void add_o_ports(pa_droid_mapping *am) { -+ pa_droid_port *p; -+ const char *name; -+ uint32_t devices; -+ uint32_t combo_devices; -+ uint32_t i = 0; -+ -+ pa_assert(am); -+ -+ devices = am->output->devices; -+ -+ devices &= ~AUDIO_DEVICE_OUT_DEFAULT; -+ -+ /* IHF combo devices, these devices are combined with IHF */ -+ combo_devices = AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADPHONE; -+ -+ while (devices) { -+ uint32_t cur_device = (1 << i++); -+ -+ if (devices & cur_device) { -+ -+ pa_assert_se(pa_droid_output_port_name(cur_device, &name)); -+ -+ if (!(p = pa_hashmap_get(am->profile_set->all_ports, name))) { -+ -+ p = create_o_port(am, cur_device, name, NULL); -+ pa_hashmap_put(am->profile_set->all_ports, p->name, p); -+ } else -+ pa_log_debug(" Output port %s from cache", name); -+ -+ pa_idxset_put(am->ports, p, NULL); -+ -+ devices &= ~cur_device; -+ } -+ } -+ -+ /* Combo devices, route to multiple routing targets simultaneously. */ -+ if (am->output->devices & combo_devices) { -+ pa_assert_se(pa_droid_output_port_name(combo_devices, &name)); -+ if (!(p = pa_hashmap_get(am->profile_set->all_ports, name))) { -+ p = create_o_port(am, combo_devices, name, NULL); -+ /* Reset priority to default. */ -+ p->priority = DEFAULT_PRIORITY; -+ -+ pa_hashmap_put(am->profile_set->all_ports, p->name, p); -+ } else -+ pa_log_debug(" Output port %s from cache", name); -+ -+ pa_idxset_put(am->ports, p, NULL); -+ } -+ -+ if (!(p = pa_hashmap_get(am->profile_set->all_ports, PA_DROID_OUTPUT_PARKING))) { -+ /* Create parking port for output mapping to be used when audio_mode_t changes. */ -+ p = create_o_port(am, 0, PA_DROID_OUTPUT_PARKING, "Parking port"); -+ /* Reset priority to half of default */ -+ p->priority = DEFAULT_PRIORITY / 2; -+ -+ pa_hashmap_put(am->profile_set->all_ports, p->name, p); -+ } else -+ pa_log_debug(" Output port %s from cache", PA_DROID_OUTPUT_PARKING); -+ -+ pa_idxset_put(am->ports, p, NULL); -+} -+ -+static void add_i_ports(pa_droid_mapping *am) { -+ pa_droid_port *p; -+ const char *name; -+ char *desc; -+ uint32_t devices; -+ uint32_t i = 0; -+ -+ pa_assert(am); -+ -+ devices = am->input->devices; -+#ifdef HAL_V2 -+ devices &= ~AUDIO_DEVICE_IN_DEFAULT; -+#endif -+ -+ while (devices) { -+ uint32_t cur_device = (1 << i++); -+ -+ if (devices & cur_device) { -+ -+#ifdef HAL_V2 -+ cur_device |= AUDIO_DEVICE_BIT_IN; -+#endif -+ -+ pa_assert_se(pa_droid_input_port_name(cur_device, &name)); -+ -+ if (!(p = pa_hashmap_get(am->profile_set->all_ports, name))) { -+ pa_log_debug(" New input port %s", name); -+ p = pa_xnew0(pa_droid_port, 1); -+ -+ p->mapping = am; -+ p->name = pa_xstrdup(name); -+ desc = pa_replace(name, "input-", "Input from "); -+ p->description = pa_replace(desc, "_", " "); -+ pa_xfree(desc); -+ p->priority = DEFAULT_PRIORITY; -+ p->device = cur_device; -+ -+ if (am->profile_set->config->global_config.attached_input_devices & cur_device & ~AUDIO_DEVICE_BIT_IN) -+ p->priority += DEFAULT_PRIORITY; -+ -+ /* Make builtin mic the default input device */ -+ if (cur_device == AUDIO_DEVICE_IN_BUILTIN_MIC) -+ p->priority += DEFAULT_PRIORITY; -+ -+ if (check_port_availability(p->name)) -+ p->priority += (DEFAULT_PRIORITY * 3); -+ -+ pa_hashmap_put(am->profile_set->all_ports, p->name, p); -+ } else -+ pa_log_debug(" Input port %s from cache", name); -+ -+ pa_idxset_put(am->ports, p, NULL); -+ -+ devices &= ~cur_device; -+ } -+ } -+ -+ if (!(p = pa_hashmap_get(am->profile_set->all_ports, PA_DROID_INPUT_PARKING))) { -+ pa_log_debug(" New input port %s", PA_DROID_INPUT_PARKING); -+ /* Create parking port for input mapping to be used when audio_mode_t changes. */ -+ p = pa_xnew0(pa_droid_port, 1); -+ p->mapping = am; -+ p->name = pa_sprintf_malloc(PA_DROID_INPUT_PARKING); -+ p->description = pa_sprintf_malloc("Parking port"); -+ p->priority = 50; -+ p->device = 0; /* No routing */ -+ -+ pa_hashmap_put(am->profile_set->all_ports, p->name, p); -+ } else -+ pa_log_debug(" Input port %s from cache", PA_DROID_INPUT_PARKING); -+ -+ pa_idxset_put(am->ports, p, NULL); -+} -+ -+pa_droid_mapping *pa_droid_mapping_get(pa_droid_profile_set *ps, pa_direction_t direction, const void *data) { -+ pa_droid_mapping *am; -+ pa_hashmap *map; -+ const char *name; -+ const pa_droid_config_output *output = NULL; -+ const pa_droid_config_input *input = NULL; -+ -+ if (direction == PA_DIRECTION_OUTPUT) { -+ output = (pa_droid_config_output *) data; -+ map = ps->output_mappings; -+ name = output->name; -+ } else { -+ input = (pa_droid_config_input *) data; -+ map = ps->input_mappings; -+ name = input->name; -+ } -+ -+ if ((am = pa_hashmap_get(map, name))) { -+ pa_log_debug(" %s mapping %s from cache", input ? "Input" : "Output", name); -+ return am; -+ } -+ pa_log_debug(" New %s mapping %s", input ? "input" : "output", name); -+ -+ am = pa_xnew0(pa_droid_mapping, 1); -+ am->profile_set = ps; -+ am->name = pa_xstrdup(name); -+ am->proplist = pa_proplist_new(); -+ am->direction = direction; -+ am->output = output; -+ am->input = input; -+ am->ports = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);; -+ -+ if (am->direction == PA_DIRECTION_OUTPUT) -+ add_o_ports(am); -+ else -+ add_i_ports(am); -+ -+ pa_hashmap_put(map, am->name, am); -+ -+ return am; -+} -+ -+bool pa_droid_output_port_name(audio_devices_t value, const char **to_str) { -+ return string_convert_num_to_str(string_conversion_table_output_device_fancy, (uint32_t) value, to_str); -+} -+ -+bool pa_droid_input_port_name(audio_devices_t value, const char **to_str) { -+ return string_convert_num_to_str(string_conversion_table_input_device_fancy, (uint32_t) value, to_str); -+} -+ -+static int add_ports(pa_core *core, pa_card_profile *cp, pa_hashmap *ports, pa_droid_mapping *am, pa_hashmap *extra) { -+ pa_droid_port *p; -+ pa_device_port *dp; -+ pa_droid_port_data *data; -+ uint32_t idx; -+ int count = 0; -+ -+ pa_log_debug("Ports for %s%s: %s", cp ? "card " : "", am->direction == PA_DIRECTION_OUTPUT ? "output" : "input", am->name); -+ -+ PA_IDXSET_FOREACH(p, am->ports, idx) { -+ if (!(dp = pa_hashmap_get(ports, p->name))) { -+ pa_log_debug(" New port %s", p->name); -+ -+ pa_device_port_new_data port_data; -+ pa_device_port_new_data_init(&port_data); -+ pa_device_port_new_data_set_name(&port_data, p->name); -+ pa_device_port_new_data_set_description(&port_data, p->description); -+ pa_device_port_new_data_set_direction(&port_data, p->mapping->direction); -+ dp = pa_device_port_new(core, &port_data, sizeof(pa_droid_port_data)); -+ pa_device_port_new_data_done(&port_data); -+ dp->priority = p->priority; -+ -+ pa_hashmap_put(ports, dp->name, dp); -+ dp->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_droid_profile_free); -+ -+ data = PA_DEVICE_PORT_DATA(dp); -+ data->device = p->device; -+ } else -+ pa_log_debug(" Port %s from cache", p->name); -+ -+ /* If port/jack detection is available, start as not available by default */ -+ dp->available = check_port_availability(p->name) ? PA_AVAILABLE_NO : PA_AVAILABLE_UNKNOWN; -+ -+ if (cp) -+ pa_hashmap_put(dp->profiles, cp->name, cp); -+ -+ count++; -+ -+ if (extra) { -+ pa_hashmap_put(extra, dp->name, dp); -+ pa_device_port_ref(dp); -+ } -+ } -+ -+ return count; -+} -+ -+ -+void pa_droid_add_ports(pa_hashmap *p, pa_droid_mapping *am, pa_card *card) { -+ pa_assert(p); -+ -+ add_ports(card->core, NULL, card->ports, am, p); -+} -+ -+void pa_droid_add_card_ports(pa_card_profile *cp, pa_hashmap *ports, pa_droid_mapping *am, pa_core *core) { -+ pa_assert(cp); -+ pa_assert(am); -+ pa_assert(core); -+ -+ add_ports(core, cp, ports, am, NULL); -+} -+ -+static char *shared_name_get(const char *module_id) { -+ pa_assert(module_id); -+ return pa_sprintf_malloc("droid-hardware-module-%s", module_id); -+} -+ -+static pa_droid_hw_module *droid_hw_module_open(pa_core *core, pa_droid_config_audio *config, const char *module_id) { -+ const pa_droid_config_hw_module *module; -+ pa_droid_hw_module *hw = NULL; -+ struct hw_module_t *hwmod = NULL; -+ audio_hw_device_t *device = NULL; -+ int ret; -+ -+ pa_assert(core); -+ pa_assert(module_id); -+ -+ if (!config) { -+ pa_log("No configuration provided for opening module with id %s", module_id); -+ goto fail; -+ } -+ -+ if (!(module = pa_droid_config_find_module(config, module_id))) { -+ pa_log("Couldn't find module with id %s", module_id); -+ goto fail; -+ } -+ -+ hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, module->name, (const hw_module_t**) &hwmod); -+ if (!hwmod) { -+ pa_log("Failed to get hw module id: %s name: %s, trying alternative.", AUDIO_HARDWARE_MODULE_ID, module->name); -+ hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID2, module->name, (const hw_module_t**) &hwmod); -+ if (!hwmod) { -+ pa_log("Failed to get hw module id: %s name: %s.", AUDIO_HARDWARE_MODULE_ID2, module->name); -+ goto fail; -+ } -+ } -+ -+ pa_log_info("Loaded hw module %s", module->name); -+ -+ ret = audio_hw_device_open(hwmod, &device); -+ if (!device) { -+ pa_log("Failed to open device (errno %d).", ret); -+ goto fail; -+ } -+ -+ if ((ret = device->init_check(device)) != 0) { -+ pa_log("Failed init_check() (errno %d)", ret); -+ goto fail; -+ } -+ -+ hw = pa_xnew0(pa_droid_hw_module, 1); -+ PA_REFCNT_INIT(hw); -+ hw->core = core; -+ hw->hwmod = hwmod; -+ hw->hw_mutex = pa_mutex_new(true, false); -+ hw->device = device; -+ hw->config = config; /* We take ownership of config struct. */ -+ hw->enabled_module = pa_droid_config_find_module(hw->config, module_id); -+ hw->module_id = hw->enabled_module->name; -+ hw->shared_name = shared_name_get(hw->module_id); -+ -+ pa_assert_se(pa_shared_set(core, hw->shared_name, hw) >= 0); -+ -+ return hw; -+ -+fail: -+ if (device) -+ audio_hw_device_close(device); -+ -+ if (hw) -+ pa_xfree(hw); -+ -+ return NULL; -+} -+ -+pa_droid_hw_module *pa_droid_hw_module_get(pa_core *core, pa_droid_config_audio *config, const char *module_id) { -+ pa_droid_hw_module *hw; -+ char *shared_name; -+ -+ pa_assert(core); -+ pa_assert(module_id); -+ -+ shared_name = shared_name_get(module_id); -+ if ((hw = pa_shared_get(core, shared_name))) -+ hw = pa_droid_hw_module_ref(hw); -+ else -+ hw = droid_hw_module_open(core, config, module_id); -+ -+ pa_xfree(shared_name); -+ return hw; -+} -+ -+pa_droid_hw_module *pa_droid_hw_module_ref(pa_droid_hw_module *hw) { -+ pa_assert(hw); -+ pa_assert(PA_REFCNT_VALUE(hw) >= 1); -+ -+ PA_REFCNT_INC(hw); -+ return hw; -+} -+ -+static void droid_hw_module_close(pa_droid_hw_module *hw) { -+ pa_assert(hw); -+ -+ pa_log_info("Closing hw module %s", hw->enabled_module->name); -+ -+ if (hw->config) -+ pa_xfree(hw->config); -+ -+ if (hw->device) -+ audio_hw_device_close(hw->device); -+ -+ if (hw->hw_mutex) -+ pa_mutex_free(hw->hw_mutex); -+ -+ if (hw->shared_name) -+ pa_xfree(hw->shared_name); -+ -+ pa_xfree(hw); -+} -+ -+void pa_droid_hw_module_unref(pa_droid_hw_module *hw) { -+ -+ pa_assert(hw); -+ pa_assert(PA_REFCNT_VALUE(hw) >= 1); -+ -+ if (PA_REFCNT_DEC(hw) > 0) -+ return; -+ -+ pa_assert_se(pa_shared_remove(hw->core, hw->shared_name) >= 0); -+ droid_hw_module_close(hw); -+} -+ -+pa_droid_config_audio *pa_droid_config_load(pa_modargs *ma) { -+ pa_droid_config_audio *config; -+ const char *config_location; -+ -+ pa_assert(ma); -+ -+ config = pa_xnew0(pa_droid_config_audio, 1); -+ -+ if ((config_location = pa_modargs_get_value(ma, "config", NULL))) { -+ if (!pa_parse_droid_audio_config(config_location, config)) { -+ pa_log("Failed to parse configuration from %s", config_location); -+ goto fail; -+ } -+ } else { -+ config_location = AUDIO_POLICY_VENDOR_CONFIG_FILE; -+ -+ if (!pa_parse_droid_audio_config(config_location, config)) { -+ pa_log_debug("Failed to parse configuration from vendor %s", config_location); -+ -+ config_location = AUDIO_POLICY_CONFIG_FILE; -+ -+ if (!pa_parse_droid_audio_config(config_location, config)) { -+ pa_log("Failed to parse configuration from %s", config_location); -+ goto fail; -+ } -+ } -+ } -+ -+ return config; -+ -+fail: -+ pa_xfree(config); -+ return NULL; -+} -+ -+void pa_droid_hw_module_lock(pa_droid_hw_module *hw) { -+ pa_assert(hw); -+ -+ pa_mutex_lock(hw->hw_mutex); -+} -+ -+bool pa_droid_hw_module_try_lock(pa_droid_hw_module *hw) { -+ pa_assert(hw); -+ -+ return pa_mutex_try_lock(hw->hw_mutex); -+} -+ -+void pa_droid_hw_module_unlock(pa_droid_hw_module *hw) { -+ pa_assert(hw); -+ -+ pa_mutex_unlock(hw->hw_mutex); -+} -Index: pulseaudio/src/modules/droid/droid-util.h -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/droid-util.h -@@ -0,0 +1,256 @@ -+#ifndef foodroidutilfoo -+#define foodroidutilfoo -+ -+/* -+ * Copyright (C) 2013 Jolla Ltd. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#define PROP_DROID_DEVICES "droid.devices" -+#define PROP_DROID_FLAGS "droid.flags" -+#define PROP_DROID_HW_MODULE "droid.hw_module" -+ -+/* Alternative module ID */ -+#define AUDIO_HARDWARE_MODULE_ID2 "libaudio" -+ -+/* From module-device-restore */ -+#define MODULE_DEVICE_RESTORE_SKIP_PROPERTY "module-device-restore.skip" -+ -+typedef struct pa_droid_hw_module pa_droid_hw_module; -+typedef struct pa_droid_card_data pa_droid_card_data; -+typedef void (*common_set_parameters_cb_t)(pa_droid_card_data *card_data, const char *str); -+ -+typedef struct pa_droid_config_audio pa_droid_config_audio; -+typedef struct pa_droid_config_hw_module pa_droid_config_hw_module; -+ -+struct pa_droid_hw_module { -+ PA_REFCNT_DECLARE; -+ -+ pa_core *core; -+ char *shared_name; -+ -+ pa_droid_config_audio *config; -+ const pa_droid_config_hw_module *enabled_module; -+ pa_mutex *hw_mutex; -+ -+ struct hw_module_t *hwmod; -+ audio_hw_device_t *device; -+ -+ const char *module_id; -+ -+ uint32_t stream_out_id; -+ uint32_t stream_in_id; -+ -+}; -+ -+struct pa_droid_card_data { -+ void *userdata; -+ /* General functions */ -+ char *module_id; -+ common_set_parameters_cb_t set_parameters; -+}; -+ -+#define AUDIO_MAX_SAMPLING_RATES (32) -+#define AUDIO_MAX_HW_MODULES (8) -+#define AUDIO_MAX_INPUTS (8) -+#define AUDIO_MAX_OUTPUTS (8) -+ -+typedef struct pa_droid_config_global { -+ audio_devices_t attached_output_devices; -+ audio_devices_t default_output_device; -+ audio_devices_t attached_input_devices; -+} pa_droid_config_global; -+ -+typedef struct pa_droid_config_output { -+ const pa_droid_config_hw_module *module; -+ -+ char name[AUDIO_HARDWARE_MODULE_ID_MAX_LEN]; -+ uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; -+ audio_channel_mask_t channel_masks; /* 0 -> dynamic */ -+ audio_format_t formats; -+ audio_devices_t devices; -+ audio_output_flags_t flags; -+} pa_droid_config_output; -+ -+typedef struct pa_droid_config_input { -+ const pa_droid_config_hw_module *module; -+ -+ char name[AUDIO_HARDWARE_MODULE_ID_MAX_LEN]; -+ uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; -+ audio_channel_mask_t channel_masks; /* 0 -> dynamic */ -+ audio_format_t formats; -+ audio_devices_t devices; -+} pa_droid_config_input; -+ -+struct pa_droid_config_hw_module { -+ const pa_droid_config_audio *config; -+ -+ char name[AUDIO_HARDWARE_MODULE_ID_MAX_LEN]; -+ pa_droid_config_output outputs[AUDIO_MAX_OUTPUTS]; -+ uint32_t outputs_size; -+ pa_droid_config_input inputs[AUDIO_MAX_INPUTS]; -+ uint32_t inputs_size; -+}; -+ -+struct pa_droid_config_audio { -+ pa_droid_config_global global_config; -+ pa_droid_config_hw_module hw_modules[AUDIO_MAX_HW_MODULES]; -+ uint32_t hw_modules_size; -+}; -+ -+ -+/* Profiles */ -+ -+typedef struct pa_droid_profile_set pa_droid_profile_set; -+typedef struct pa_droid_mapping pa_droid_mapping; -+ -+typedef struct pa_droid_port_data { -+ audio_devices_t device; -+} pa_droid_port_data; -+ -+typedef struct pa_droid_port { -+ pa_droid_mapping *mapping; -+ -+ audio_devices_t device; -+ char *name; -+ char *description; -+ unsigned priority; -+} pa_droid_port; -+ -+struct pa_droid_mapping { -+ pa_droid_profile_set *profile_set; -+ -+ const pa_droid_config_output *output; -+ const pa_droid_config_input *input; -+ -+ char *name; -+ char *description; -+ unsigned priority; -+ pa_proplist *proplist; -+ -+ /* Mapping doesn't own the ports */ -+ pa_idxset *ports; -+ -+ pa_direction_t direction; -+ -+ pa_sink *sink; -+ pa_source *source; -+}; -+ -+typedef struct pa_droid_profile { -+ pa_droid_profile_set *profile_set; -+ -+ const pa_droid_config_hw_module *module; -+ -+ char *name; -+ char *description; -+ unsigned priority; -+ -+ /* Profile doesn't own the mappings */ -+ pa_droid_mapping *output; -+ pa_droid_mapping *input; -+ -+} pa_droid_profile; -+ -+struct pa_droid_profile_set { -+ const pa_droid_config_audio *config; -+ -+ pa_hashmap *all_ports; -+ pa_hashmap *output_mappings; -+ pa_hashmap *input_mappings; -+ pa_hashmap *profiles; -+}; -+ -+#define PA_DROID_OUTPUT_PARKING "output-parking" -+#define PA_DROID_INPUT_PARKING "input-parking" -+ -+/* Open hardware module */ -+/* 'config' can be NULL if it is assumed that hw module with module_id already is open. */ -+/* if opening of hw_module succeeds, config ownership is transferred to hw_module and config -+ * shouldn't be freed. */ -+pa_droid_hw_module *pa_droid_hw_module_get(pa_core *core, pa_droid_config_audio *config, const char *module_id); -+pa_droid_hw_module *pa_droid_hw_module_ref(pa_droid_hw_module *hw); -+void pa_droid_hw_module_unref(pa_droid_hw_module *hw); -+ -+void pa_droid_hw_module_lock(pa_droid_hw_module *hw); -+bool pa_droid_hw_module_try_lock(pa_droid_hw_module *hw); -+void pa_droid_hw_module_unlock(pa_droid_hw_module *hw); -+ -+/* Conversion helpers */ -+typedef enum { -+ CONV_FROM_PA, -+ CONV_FROM_HAL -+} pa_conversion_field_t; -+ -+bool pa_convert_output_channel(uint32_t value, pa_conversion_field_t from, uint32_t *to_value); -+bool pa_convert_input_channel(uint32_t value, pa_conversion_field_t from, uint32_t *to_value); -+bool pa_convert_format(uint32_t value, pa_conversion_field_t from, uint32_t *to_value); -+ -+bool pa_string_convert_output_device_num_to_str(audio_devices_t value, const char **to_str); -+bool pa_string_convert_output_device_str_to_num(const char *str, audio_devices_t *to_value); -+bool pa_string_convert_input_device_num_to_str(audio_devices_t value, const char **to_str); -+bool pa_string_convert_input_device_str_to_num(const char *str, audio_devices_t *to_value); -+ -+bool pa_string_convert_flag_num_to_str(audio_output_flags_t value, const char **to_str); -+bool pa_string_convert_flag_str_to_num(const char *str, audio_output_flags_t *to_value); -+ -+char *pa_list_string_output_device(audio_devices_t devices); -+char *pa_list_string_input_device(audio_devices_t devices); -+char *pa_list_string_flags(audio_output_flags_t flags); -+ -+/* Config parser */ -+bool pa_parse_droid_audio_config(const char *filename, pa_droid_config_audio *config); -+pa_droid_config_audio *pa_droid_config_load(pa_modargs *ma); -+ -+const pa_droid_config_output *pa_droid_config_find_output(const pa_droid_config_hw_module *module, const char *name); -+const pa_droid_config_input *pa_droid_config_find_input(const pa_droid_config_hw_module *module, const char *name); -+const pa_droid_config_hw_module *pa_droid_config_find_module(const pa_droid_config_audio *config, const char* module_id); -+ -+ -+/* Profiles */ -+pa_droid_profile_set *pa_droid_profile_set_new(const pa_droid_config_hw_module *module); -+void pa_droid_profile_set_free(pa_droid_profile_set *ps); -+ -+pa_droid_profile *pa_droid_profile_new(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input); -+void pa_droid_profile_free(pa_droid_profile *p); -+ -+pa_droid_mapping *pa_droid_mapping_get(pa_droid_profile_set *ps, pa_direction_t direction, const void *data); -+void pa_droid_mapping_free(pa_droid_mapping *am); -+ -+/* Add ports from sinks/sources */ -+void pa_droid_add_ports(pa_hashmap *ports, pa_droid_mapping *am, pa_card *card); -+/* Add ports from card */ -+void pa_droid_add_card_ports(pa_card_profile *cp, pa_hashmap *ports, pa_droid_mapping *am, pa_core *core); -+ -+/* Pretty port names */ -+bool pa_droid_output_port_name(audio_devices_t value, const char **to_str); -+bool pa_droid_input_port_name(audio_devices_t value, const char **to_str); -+ -+#endif -Index: pulseaudio/src/modules/droid/module-droid-card.c -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/module-droid-card.c -@@ -0,0 +1,636 @@ -+/* -+ * Copyright (C) 2013 Jolla Ltd. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include -+#include -+ -+#ifdef HAVE_VALGRIND_MEMCHECK_H -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+//#include -+//#include -+ -+#include "droid-util.h" -+#include "droid-sink.h" -+#include "droid-source.h" -+#ifdef HAVE_UDEV -+#include "droid-extcon.h" -+#endif -+ -+#include "module-droid-card-symdef.h" -+ -+PA_MODULE_AUTHOR("Juho Hämäläinen"); -+PA_MODULE_DESCRIPTION("Droid card"); -+PA_MODULE_VERSION(PACKAGE_VERSION); -+PA_MODULE_USAGE( -+ "card_name= " -+ "sink_name= " -+ "source_name= " -+ "namereg_fail= " -+ "rate= " -+ "output_flags= " -+ "module_id= " -+ "voice_source_routing= " -+ "deferred_volume= " -+ "config= " -+ "voice_volume_call_mode= " -+ "voice_property_key= " -+ "voice_property_value= " -+ "voice_virtual_stream= create virtual stream for voice call volume control (default false)" -+); -+ -+static const char* const valid_modargs[] = { -+ "card_name", -+ "sink_name", -+ "source_name", -+ "namereg_fail", -+ "format", -+ "rate", -+ "output_flags", -+ "module_id", -+ "voice_source_routing", -+ "sink_buffer", -+ "source_buffer", -+ "deferred_volume", -+ "mute_routing_before", -+ "mute_routing_after", -+ "config", -+ "voice_volume_call_mode", -+ "voice_property_key", -+ "voice_property_value", -+ "voice_virtual_stream", -+ NULL, -+}; -+ -+#define DEFAULT_MODULE_ID "primary" -+#define DEFAULT_AUDIO_POLICY_CONF "/system/etc/audio_policy.conf" -+#define VOICE_CALL_PROFILE_NAME "voicecall" -+#define VOICE_CALL_PROFILE_DESC "Call mode" -+#define RINGTONE_PROFILE_NAME "ringtone" -+#define RINGTONE_PROFILE_DESC "Ringtone mode" -+#define COMMUNICATION_PROFILE_NAME "communication" -+#define COMMUNICATION_PROFILE_DESC "Communication mode" -+ -+struct virtual_profile { -+ pa_droid_profile *profile; -+ audio_mode_t mode; -+}; -+ -+struct userdata { -+ pa_core *core; -+ pa_module *module; -+ -+ pa_thread *thread; -+ pa_thread_mq thread_mq; -+ pa_rtpoll *rtpoll; -+ -+ pa_droid_profile_set *profile_set; -+ -+ pa_droid_hw_module *hw_module; -+ pa_droid_card_data card_data; -+ -+ struct virtual_profile call_profile; -+ struct virtual_profile comm_profile; -+ struct virtual_profile ring_profile; -+ pa_droid_profile *old_profile; -+ -+#ifdef HAVE_UDEV -+ pa_droid_extcon *extcon; -+#endif -+ -+ bool voice_source_routing; -+ -+ pa_modargs *modargs; -+ pa_card *card; -+}; -+ -+struct profile_data { -+ pa_droid_profile *profile; -+}; -+ -+static void add_disabled_profile(pa_hashmap *profiles) { -+ pa_card_profile *cp; -+ struct profile_data *d; -+ -+ cp = pa_card_profile_new("off", _("Off"), sizeof(struct profile_data)); -+ -+ d = PA_CARD_PROFILE_DATA(cp); -+ d->profile = NULL; -+ -+ pa_hashmap_put(profiles, cp->name, cp); -+} -+ -+/* Special profile for calls */ -+static pa_droid_profile* add_virtual_profile(struct userdata *u, const char *name, const char *description, pa_hashmap *profiles) { -+ pa_droid_profile *ap; -+ pa_card_profile *cp; -+ struct profile_data *d; -+ -+ pa_assert(u); -+ pa_assert(u->profile_set); -+ -+ pa_log_debug("New virtual profile: %s", name); -+ -+ ap = pa_xnew0(pa_droid_profile, 1); -+ ap->profile_set = u->profile_set; -+ ap->name = pa_xstrdup(name); -+ ap->description = pa_xstrdup(description); -+ ap->priority = 50; -+ -+ pa_hashmap_put(u->profile_set->profiles, ap->name, ap); -+ -+ cp = pa_card_profile_new(ap->name, ap->description, sizeof(struct profile_data)); -+ d = PA_CARD_PROFILE_DATA(cp); -+ d->profile = ap; -+ -+ pa_hashmap_put(profiles, cp->name, cp); -+ -+ return ap; -+} -+ -+static void set_parameters_cb(pa_droid_card_data *card_data, const char *str) { -+ struct userdata *u; -+ -+ pa_assert(card_data); -+ pa_assert(str); -+ -+ u = card_data->userdata; -+ -+ if (u) { -+ pa_log_debug("Setting parameters: %s", str); -+ pa_droid_hw_module_lock(u->hw_module); -+ u->hw_module->device->set_parameters(u->hw_module->device, str); -+ pa_droid_hw_module_unlock(u->hw_module); -+ } -+} -+ -+static void set_card_name(pa_modargs *ma, pa_card_new_data *data, const char *module_id) { -+ const char *tmp; -+ char *name; -+ -+ pa_assert(ma); -+ pa_assert(data); -+ pa_assert(module_id); -+ -+ if ((tmp = pa_modargs_get_value(ma, "card_name", NULL))) { -+ pa_card_new_data_set_name(data, tmp); -+ data->namereg_fail = true; -+ return; -+ } -+ -+ name = pa_sprintf_malloc("droid_card.%s", module_id); -+ pa_card_new_data_set_name(data, name); -+ pa_xfree(name); -+ data->namereg_fail = false; -+} -+ -+static void add_profile(struct userdata *u, pa_hashmap *h, pa_hashmap *ports, pa_droid_profile *ap) { -+ pa_card_profile *cp; -+ struct profile_data *d; -+ -+ pa_assert(u); -+ pa_assert(h); -+ pa_assert(ports); -+ pa_assert(ap); -+ -+ pa_log_debug("Card profile %s", ap->name); -+ -+ cp = pa_card_profile_new(ap->name, ap->description, sizeof(struct profile_data)); -+ cp->priority = ap->priority; -+ -+ cp->n_sinks = 1; -+ pa_droid_add_card_ports(cp, ports, ap->output, u->core); -+ cp->max_sink_channels = popcount(ap->output->output->channel_masks); -+ if (ap->input) { -+ pa_droid_add_card_ports(cp, ports, ap->input, u->core); -+ cp->n_sources = 1; -+ cp->max_source_channels = popcount(ap->input->input->channel_masks); -+ } -+ -+ d = PA_CARD_PROFILE_DATA(cp); -+ d->profile = ap; -+ -+ pa_hashmap_put(h, cp->name, cp); -+} -+ -+static void add_profiles(struct userdata *u, pa_hashmap *h, pa_hashmap *ports) { -+ void *state; -+ pa_droid_profile *ap; -+ -+ pa_assert(u); -+ pa_assert(h); -+ pa_assert(ports); -+ -+ PA_HASHMAP_FOREACH(ap, u->profile_set->profiles, state) { -+ add_profile(u, h, ports, ap); -+ } -+} -+ -+static void init_profile(struct userdata *u) { -+ pa_droid_mapping *am; -+ struct profile_data *d; -+ -+ pa_assert(u); -+ -+ pa_log_debug("Init profile."); -+ -+ d = PA_CARD_PROFILE_DATA(u->card->active_profile); -+ -+ if (d->profile && d->profile->output) { -+ am = d->profile->output; -+ am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card); -+ } -+ -+ if (d->profile && d->profile->input) { -+ am = d->profile->input; -+ am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, &u->card_data, am, u->card); -+ } -+} -+ -+static int set_mode(struct userdata *u, audio_mode_t mode) { -+ int ret; -+ const char *mode_str; -+ -+ pa_assert(u); -+ pa_assert(u->hw_module); -+ pa_assert(u->hw_module->device); -+ -+ switch (mode) { -+ case AUDIO_MODE_RINGTONE: -+ mode_str = "AUDIO_MODE_RINGTONE"; -+ break; -+ case AUDIO_MODE_IN_CALL: -+ mode_str = "AUDIO_MODE_IN_CALL"; -+ break; -+ case AUDIO_MODE_IN_COMMUNICATION: -+ mode_str = "AUDIO_MODE_IN_COMMUNICATION"; -+ break; -+ default: -+ mode_str = "AUDIO_MODE_NORMAL"; -+ break; -+ } -+ -+ pa_log_debug("Set mode to %s.", mode_str); -+ -+ pa_droid_hw_module_lock(u->hw_module); -+ if ((ret = u->hw_module->device->set_mode(u->hw_module->device, mode)) < 0) -+ pa_log("Failed to set mode."); -+ pa_droid_hw_module_unlock(u->hw_module); -+ -+ return ret; -+} -+ -+static void park_profile(pa_droid_profile *dp) { -+ pa_assert(dp); -+ -+ if (dp->output && dp->output->sink) -+ pa_sink_set_port(dp->output->sink, PA_DROID_OUTPUT_PARKING, false); -+ if (dp->input && dp->input->source) -+ pa_source_set_port(dp->input->source, PA_DROID_INPUT_PARKING, false); -+} -+ -+static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { -+ struct userdata *u; -+ pa_droid_mapping *am; -+ struct virtual_profile *new_vp = NULL; -+ struct virtual_profile *old_vp = NULL; -+ struct profile_data *nd, *od; -+ pa_queue *sink_inputs = NULL, *source_outputs = NULL; -+ -+ pa_assert(c); -+ pa_assert(new_profile); -+ pa_assert_se(u = c->userdata); -+ -+ nd = PA_CARD_PROFILE_DATA(new_profile); -+ od = PA_CARD_PROFILE_DATA(c->active_profile); -+ -+ if (nd->profile == u->call_profile.profile) -+ new_vp = &u->call_profile; -+ if (nd->profile == u->ring_profile.profile) -+ new_vp = &u->ring_profile; -+ if (nd->profile == u->comm_profile.profile) -+ new_vp = &u->comm_profile; -+ -+ if (new_vp) { -+ pa_log_debug("Setting new virtual profile."); -+ if (u->old_profile == NULL) -+ u->old_profile = od->profile; -+ -+ park_profile(od->profile); -+ -+ set_mode(u, new_vp->mode); -+ -+ /* call mode specialities */ -+ if (new_vp->profile == u->call_profile.profile) { -+ pa_droid_sink_set_voice_control(u->old_profile->output->sink, true); -+ if (!u->voice_source_routing) -+ pa_droid_source_set_routing(u->old_profile->input->source, false); -+ } -+ return 0; -+ } -+ -+ if (od->profile == u->call_profile.profile) -+ old_vp = &u->call_profile; -+ if (od->profile == u->ring_profile.profile) -+ old_vp = &u->ring_profile; -+ if (od->profile == u->comm_profile.profile) -+ old_vp = &u->comm_profile; -+ -+ if (old_vp) { -+ pa_assert(u->old_profile); -+ -+ park_profile(nd->profile); -+ -+ set_mode(u, AUDIO_MODE_NORMAL); -+ -+ /* call mode specialities */ -+ if (old_vp->profile == u->call_profile.profile) { -+ pa_droid_sink_set_voice_control(u->old_profile->output->sink, false); -+ if (!u->voice_source_routing) -+ pa_droid_source_set_routing(u->old_profile->input->source, true); -+ } -+ -+ /* If new profile is the same as from which we switched to -+ * call profile, transfer ownership back to that profile. -+ * Otherwise destroy sinks & sources and switch to new profile. */ -+ if (nd->profile == u->old_profile) { -+ u->old_profile = NULL; -+ return 0; -+ } else { -+ od->profile = u->old_profile; -+ u->old_profile = NULL; -+ -+ /* Continue to sink-input transfer below */ -+ } -+ } -+ -+ /* If there are connected sink inputs/source outputs in old profile's sinks/sources move -+ * them all to new sinks/sources. */ -+ -+ if (od->profile && od->profile->output) { -+ do { -+ am = od->profile->output; -+ -+ if (!am->sink) -+ continue; -+ -+ if (nd->profile && nd->profile->output && am == nd->profile->output) -+ continue; -+ -+ sink_inputs = pa_sink_move_all_start(am->sink, sink_inputs); -+ pa_droid_sink_free(am->sink); -+ am->sink = NULL; -+ } while(0); -+ } -+ -+ if (od->profile && od->profile->input) { -+ do { -+ am = od->profile->input; -+ -+ if (!am->source) -+ continue; -+ -+ if (nd->profile && nd->profile->input && am == nd->profile->input) -+ continue; -+ -+ source_outputs = pa_source_move_all_start(am->source, source_outputs); -+ pa_droid_source_free(am->source); -+ am->source = NULL; -+ } while(0); -+ } -+ -+ if (nd->profile && nd->profile->output) { -+ am = nd->profile->output; -+ -+ if (!am->sink) -+ am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card); -+ -+ if (sink_inputs && am->sink) { -+ pa_sink_move_all_finish(am->sink, sink_inputs, false); -+ sink_inputs = NULL; -+ } -+ } -+ -+ if (nd->profile && nd->profile->input) { -+ am = nd->profile->input; -+ -+ if (!am->source) -+ am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, &u->card_data, am, u->card); -+ -+ if (source_outputs && am->source) { -+ pa_source_move_all_finish(am->source, source_outputs, false); -+ source_outputs = NULL; -+ } -+ } -+ -+ if (sink_inputs) -+ pa_sink_move_all_fail(sink_inputs); -+ -+ if (source_outputs) -+ pa_source_move_all_fail(source_outputs); -+ -+ return 0; -+} -+ -+ -+int pa__init(pa_module *m) { -+ pa_modargs *ma = NULL; -+ pa_card_new_data data; -+ pa_droid_config_audio *config = NULL; -+ const char *module_id; -+ bool namereg_fail = false; -+ bool voice_source_routing = false; -+ -+ pa_assert(m); -+ -+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -+ pa_log("Failed to parse module argumets."); -+ goto fail; -+ } -+ -+ struct userdata *u = pa_xnew0(struct userdata, 1); -+ u->core = m->core; -+ -+ if (!(config = pa_droid_config_load(ma))) -+ goto fail; -+ -+ if (pa_modargs_get_value_boolean(ma, "voice_source_routing", &voice_source_routing) < 0) { -+ pa_log("Failed to parse voice_source_routing argument."); -+ goto fail; -+ } -+ u->voice_source_routing = voice_source_routing; -+ -+ module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID); -+ -+ /* Ownership of config transfers to hw_module if opening of hw module succeeds. */ -+ if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id))) -+ goto fail; -+ -+ u->card_data.set_parameters = set_parameters_cb; -+ u->card_data.module_id = pa_xstrdup(module_id); -+ u->card_data.userdata = u; -+ -+ u->profile_set = pa_droid_profile_set_new(u->hw_module->enabled_module); -+ -+ pa_card_new_data_init(&data); -+ data.driver = __FILE__; -+ data.module = m; -+ -+ set_card_name(ma, &data, u->hw_module->module_id); -+ -+ /* We need to give pa_modargs_get_value_boolean() a pointer to a local -+ * variable instead of using &data.namereg_fail directly, because -+ * data.namereg_fail is a bitfield and taking the address of a bitfield -+ * variable is impossible. */ -+ namereg_fail = data.namereg_fail; -+ if (pa_modargs_get_value_boolean(ma, "namereg_fail", &namereg_fail) < 0) { -+ pa_log("Failed to parse namereg_fail argument."); -+ pa_card_new_data_done(&data); -+ goto fail; -+ } -+ data.namereg_fail = namereg_fail; -+ -+ add_profiles(u, data.profiles, data.ports); -+ -+ if (pa_hashmap_isempty(data.profiles)) { -+ pa_log("Failed to find a working profile."); -+ pa_card_new_data_done(&data); -+ goto fail; -+ } -+ -+ u->call_profile.profile = add_virtual_profile(u, VOICE_CALL_PROFILE_NAME, -+ VOICE_CALL_PROFILE_DESC, data.profiles); -+ u->call_profile.mode = AUDIO_MODE_IN_CALL; -+ u->comm_profile.profile = add_virtual_profile(u, COMMUNICATION_PROFILE_NAME, -+ COMMUNICATION_PROFILE_DESC, data.profiles); -+ u->comm_profile.mode = AUDIO_MODE_IN_COMMUNICATION; -+ u->ring_profile.profile = add_virtual_profile(u, RINGTONE_PROFILE_NAME, -+ RINGTONE_PROFILE_DESC, data.profiles); -+ u->ring_profile.mode = AUDIO_MODE_RINGTONE; -+ -+ add_disabled_profile(data.profiles); -+ -+ pa_proplist_sets(data.proplist, PROP_DROID_HW_MODULE, u->hw_module->module_id); -+ -+ u->card = pa_card_new(m->core, &data); -+ pa_card_new_data_done(&data); -+ -+ if (!u->card) { -+ pa_log("Couldn't create card."); -+ goto fail; -+ } -+ -+ u->card->userdata = u; -+ u->card->set_profile = card_set_profile; -+ -+ u->modargs = ma; -+ u->module = m; -+ -+ m->userdata = u; -+ -+ init_profile(u); -+ -+#ifdef HAVE_UDEV -+ u->extcon = pa_droid_extcon_new(m->core, u->card); -+#endif -+ -+ return 0; -+ -+fail: -+ if (ma) -+ pa_modargs_free(ma); -+ -+ pa_xfree(config); -+ -+ pa__done(m); -+ -+ return -1; -+} -+ -+void pa__done(pa_module *m) { -+ struct userdata *u; -+ -+ pa_assert(m); -+ -+ if ((u = m->userdata)) { -+ -+ if (u->card && u->card->sinks) -+ pa_idxset_remove_all(u->card->sinks, (pa_free_cb_t) pa_droid_sink_free); -+ -+ if (u->card && u->card->sources) -+ pa_idxset_remove_all(u->card->sources, (pa_free_cb_t) pa_droid_source_free); -+ -+#ifdef HAVE_UDEV -+ if (u->extcon) -+ pa_droid_extcon_free(u->extcon); -+#endif -+ -+ if (u->card) -+ pa_card_free(u->card); -+ -+ if (u->modargs) -+ pa_modargs_free(u->modargs); -+ -+ if (u->profile_set) -+ pa_droid_profile_set_free(u->profile_set); -+ -+ if (u->card_data.module_id) -+ pa_xfree(u->card_data.module_id); -+ -+ if (u->hw_module) -+ pa_droid_hw_module_unref(u->hw_module); -+ -+ pa_xfree(u); -+ } -+} -Index: pulseaudio/src/modules/droid/module-droid-sink.c -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/module-droid-sink.c -@@ -0,0 +1,103 @@ -+/* -+ * Copyright (C) 2013 Jolla Ltd. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#ifdef HAVE_VALGRIND_MEMCHECK_H -+#include -+#endif -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "droid-util.h" -+#include "droid-sink.h" -+ -+#include "module-droid-sink-symdef.h" -+ -+PA_MODULE_AUTHOR("Juho Hämäläinen"); -+PA_MODULE_DESCRIPTION("Droid sink"); -+PA_MODULE_USAGE("master_sink= " -+ "sink_name= " -+ "sco_fake_sink="); -+PA_MODULE_VERSION(PACKAGE_VERSION); -+ -+static const char* const valid_modargs[] = { -+ "rate", -+ "flags", -+ "devices", -+ "sink_name", -+ "module_id", -+ "mute_routing_before", -+ "mute_routing_after", -+ "sink_buffer", -+ "deferred_volume", -+ "voice_volume_call_mode", -+ "voice_property_key", -+ "voice_property_value", -+ "voice_virtual_stream", -+ "sco_fake_sink", -+ NULL, -+}; -+ -+void pa__done(pa_module *m) { -+ pa_sink *sink; -+ -+ pa_assert(m); -+ -+ if ((sink = m->userdata)) -+ pa_droid_sink_free(sink); -+} -+ -+int pa__init(pa_module *m) { -+ pa_modargs *ma = NULL; -+ -+ pa_assert(m); -+ -+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -+ pa_log("Failed to parse module argumets."); -+ goto fail; -+ } -+ -+ if (!(m->userdata = pa_droid_sink_new(m, ma, __FILE__, NULL, 0, NULL, NULL))) -+ goto fail; -+ -+ pa_modargs_free(ma); -+ -+ return 0; -+ -+fail: -+ if (ma) -+ pa_modargs_free(ma); -+ -+ pa__done(m); -+ -+ return -1; -+} -Index: pulseaudio/src/modules/droid/module-droid-source.c -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/module-droid-source.c -@@ -0,0 +1,95 @@ -+/* -+ * Copyright (C) 2013 Jolla Ltd. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#ifdef HAVE_VALGRIND_MEMCHECK_H -+#include -+#endif -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "droid-util.h" -+#include "droid-source.h" -+ -+#include "module-droid-source-symdef.h" -+ -+PA_MODULE_AUTHOR("Juho Hämäläinen"); -+PA_MODULE_DESCRIPTION("Droid source"); -+PA_MODULE_USAGE("master_source= " -+ "source_name="); -+PA_MODULE_VERSION(PACKAGE_VERSION); -+ -+static const char* const valid_modargs[] = { -+ "rate", -+ "flags", -+ "devices", -+ "source_name", -+ "module_id", -+ "source_buffer", -+ "deferred_volume", -+ NULL, -+}; -+ -+void pa__done(pa_module *m) { -+ pa_source *source; -+ -+ pa_assert(m); -+ -+ if ((source = m->userdata)) -+ pa_droid_source_free(source); -+} -+ -+int pa__init(pa_module *m) { -+ pa_modargs *ma = NULL; -+ -+ pa_assert(m); -+ -+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -+ pa_log("Failed to parse module argumets."); -+ goto fail; -+ } -+ -+ if (!(m->userdata = pa_droid_source_new(m, ma, __FILE__, NULL, NULL, NULL))) -+ goto fail; -+ -+ pa_modargs_free(ma); -+ -+ return 0; -+ -+fail: -+ if (ma) -+ pa_modargs_free(ma); -+ -+ pa__done(m); -+ -+ return -1; -+} -Index: pulseaudio/po/POTFILES.in -=================================================================== ---- pulseaudio.orig/po/POTFILES.in -+++ pulseaudio/po/POTFILES.in -@@ -16,6 +16,7 @@ src/modules/alsa/module-alsa-sink.c - src/modules/alsa/module-alsa-source.c - src/modules/bluetooth/module-bluez4-device.c - src/modules/bluetooth/module-bluez5-device.c -+src/modules/droid/module-droid-card.c - src/modules/echo-cancel/module-echo-cancel.c - src/modules/gconf/gconf-helper.c - src/modules/gconf/module-gconf.c diff -Nru pulseaudio-10.0/debian/patches/0208-module-bluetooth-device-Allow-leaving-transport-runn.patch pulseaudio-10.0/debian/patches/0208-module-bluetooth-device-Allow-leaving-transport-runn.patch --- pulseaudio-10.0/debian/patches/0208-module-bluetooth-device-Allow-leaving-transport-runn.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0208-module-bluetooth-device-Allow-leaving-transport-runn.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,220 +0,0 @@ -From: Ricardo Salveti de Araujo -Date: Thu, 17 Jul 2014 19:34:44 -0300 -Subject: [PATCH] module-bluez4-device: Allow leaving transport running - while sink and source are suspended. - -There are some cases where keeping the SCO transport running even when -SCO sink and source are suspended is needed. This patch allows keeping -the transport running when SCO sink has property -bluetooth.hsp.prevent.suspend.transport set as true. - -Patch done by Juho Hamalainen - -Corner-case specific patch (pulse droid), not upstreamable. - -Signed-off-by: Ricardo Salveti de Araujo ---- - src/modules/bluetooth/bluez4-util.c | 23 +++++++ - src/modules/bluetooth/module-bluez4-device.c | 85 ++++++++++++++++++++++++- - 2 files changed, 106 insertions(+), 2 deletions(-) - -Index: pulseaudio/src/modules/bluetooth/bluez4-util.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/bluez4-util.c -+++ pulseaudio/src/modules/bluetooth/bluez4-util.c -@@ -26,6 +26,7 @@ - #include - #include - #include -+#include - - #include "bluez4-util.h" - #include "a2dp-codecs.h" -@@ -59,6 +60,8 @@ - " " \ - "" - -+#define RACE_CONDITION_TIME 1000000 // 1 second -+ - struct pa_bluez4_discovery { - PA_REFCNT_DECLARE; - -@@ -487,6 +490,9 @@ static int parse_audio_property(pa_bluez - DBusMessageIter variant_i; - bool is_audio_interface; - pa_bluez4_profile_t p = PA_BLUEZ4_PROFILE_OFF; -+ pa_usec_t tstamp_now; -+ static pa_usec_t tstamp_prev = 0; -+ DBusMessage *m; - - pa_assert(d); - pa_assert(interface); -@@ -517,6 +523,23 @@ static int parse_audio_property(pa_bluez - pa_bluez4_audio_state_t state = audio_state_from_string(value); - - pa_log_debug("Device %s interface %s property 'State' changed to value '%s'", d->path, interface, value); -+ /* Device may change state again (e.g. suspend itself) before previous state change -+ * message has been parsed here. When this take place sink state in here and bluez -+ * will be out-of-sync. This may generate endless transport acquire/release loop -+ * which will be sustained by this module. When we notice this to be ongoing -+ * message is ignored and current state is queried with GetProperties. */ -+ if (pa_streq(interface, "org.bluez.AudioSink") && state == PA_BLUEZ4_AUDIO_STATE_CONNECTED) { -+ tstamp_now = pa_rtclock_now(); -+ if (tstamp_prev != 0 && tstamp_now - tstamp_prev < RACE_CONDITION_TIME) { -+ pa_log_debug("Race condition. Message ignored."); -+ tstamp_prev = 0; -+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.AudioSink", "GetProperties")); -+ send_and_add_to_pending(d->discovery, m, get_properties_reply, d); -+ -+ return 0; -+ } -+ tstamp_prev = tstamp_now; -+ } - - if (state == PA_BLUEZ4_AUDIO_STATE_INVALID) - return -1; -Index: pulseaudio/src/modules/bluetooth/module-bluez4-device.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/module-bluez4-device.c -+++ pulseaudio/src/modules/bluetooth/module-bluez4-device.c -@@ -184,6 +184,9 @@ struct userdata { - pa_modargs *modargs; - - int stream_write_type; -+ -+ pa_hook_slot *sco_sink_proplist_changed_slot; -+ bool prevent_suspend_transport; - }; - - enum { -@@ -377,6 +380,67 @@ static int bt_transport_acquire(struct u - return 0; - } - -+#define HSP_PREVENT_SUSPEND_STR "bluetooth.hsp.prevent.suspend.transport" -+ -+/* Check and update prevent_suspend_transport value from sco sink proplist. -+ * -+ * Return < 0 if sink proplist doesn't contain HSP_PREVENT_SUSPEND_STR value, -+ * 1 if value is 'true' -+ * 0 if value is something else. */ -+static int check_proplist(struct userdata *u) { -+ int ret; -+ const char *str; -+ -+ pa_assert(u); -+ pa_assert(u->hsp.sco_sink); -+ -+ if ((str = pa_proplist_gets(u->hsp.sco_sink->proplist, HSP_PREVENT_SUSPEND_STR))) { -+ if (pa_streq(str, "true")) -+ ret = 1; -+ else -+ ret = 0; -+ } else -+ ret = -1; -+ -+ u->prevent_suspend_transport = ret == 1; -+ -+ pa_log_debug("Set %s %s", HSP_PREVENT_SUSPEND_STR, u->prevent_suspend_transport ? "true" : "false"); -+ -+ return ret; -+} -+ -+/* There are cases where keeping the transport running even when sco sink and source are suspended -+ * is needed. -+ * To work with these cases, check sco.sink for bluetooth.hsp.prevent.suspend.transport value, and -+ * when set to true prevent closing the transport when sink suspends. -+ * Also, if the sink&source are suspended when sco-sink suspend.transport value changes to true, -+ * bring sco transport up. When suspend.transport value changes to false while sink&source are suspended, -+ * tear down the transport. */ -+static pa_hook_result_t update_allow_release_cb(pa_core *c, pa_sink *s, struct userdata *u) { -+ pa_assert(u); -+ pa_assert(s); -+ -+ if (!u->hsp.sco_sink || u->hsp.sco_sink != s) -+ return PA_HOOK_OK; -+ -+ if (check_proplist(u) < 0) -+ return PA_HOOK_OK; -+ -+ if (!USE_SCO_OVER_PCM(u)) { -+ pa_log_debug("SCO sink not available."); -+ return PA_HOOK_OK; -+ } -+ -+ if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->hsp.sco_sink)) && -+ !PA_SOURCE_IS_OPENED(pa_source_get_state(u->hsp.sco_source))) { -+ -+ /* Clear all suspend bits, effectively resuming SCO sink for a while. */ -+ pa_sink_suspend(s, false, PA_SUSPEND_ALL); -+ } -+ -+ return PA_HOOK_OK; -+} -+ - /* Run from IO thread */ - static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { - struct userdata *u = PA_SINK(o)->userdata; -@@ -1425,6 +1489,10 @@ static int sco_over_pcm_state_update(str - if (u->stream_fd < 0) - return 0; - -+ if (check_proplist(u) == 1) { -+ pa_log_debug("Suspend prevention active, not closing SCO over PCM"); -+ return 0; -+ } - pa_log_debug("Closing SCO over PCM"); - - bt_transport_release(u); -@@ -1468,6 +1536,11 @@ static pa_hook_result_t transport_nrec_c - if (t != u->transport) - return PA_HOOK_OK; - -+ if (!u->source) { -+ pa_log_warn("trying to change bluetooth source property, but source doesn't exist."); -+ return PA_HOOK_OK; -+ } -+ - p = pa_proplist_new(); - pa_proplist_sets(p, "bluetooth.nrec", t->nrec ? "1" : "0"); - pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, p); -@@ -1830,7 +1903,7 @@ static int setup_transport(struct userda - pa_bluez4_transport *t; - - pa_assert(u); -- pa_assert(!u->transport); -+ pa_assert(!u->transport_acquired); - pa_assert(u->profile != PA_BLUEZ4_PROFILE_OFF); - - /* check if profile has a transport */ -@@ -1910,7 +1983,8 @@ static void stop_thread(struct userdata - - if (u->transport) { - bt_transport_release(u); -- u->transport = NULL; -+ /* Do not set transport pointer to NULL. When failing to switch -+ * profile NULL u->transport would assert. */ - } - - if (u->sink) { -@@ -2547,6 +2621,10 @@ int pa__init(pa_module *m) { - pa_hook_connect(pa_bluez4_discovery_hook(u->discovery, PA_BLUEZ4_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED), - PA_HOOK_NORMAL, (pa_hook_cb_t) transport_speaker_gain_changed_cb, u); - -+ u->sco_sink_proplist_changed_slot = -+ pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], -+ PA_HOOK_NORMAL, (pa_hook_cb_t) update_allow_release_cb, u); -+ - /* Add the card structure. This will also initialize the default profile */ - if (add_card(u) < 0) - goto fail; -@@ -2626,6 +2704,9 @@ void pa__done(pa_module *m) { - if (u->transport_speaker_changed_slot) - pa_hook_slot_free(u->transport_speaker_changed_slot); - -+ if (u->sco_sink_proplist_changed_slot) -+ pa_hook_slot_free(u->sco_sink_proplist_changed_slot); -+ - if (USE_SCO_OVER_PCM(u)) - restore_sco_volume_callbacks(u); - diff -Nru pulseaudio-10.0/debian/patches/0209-module-switch-on-connect-adding-parameter-to-allow-s.patch pulseaudio-10.0/debian/patches/0209-module-switch-on-connect-adding-parameter-to-allow-s.patch --- pulseaudio-10.0/debian/patches/0209-module-switch-on-connect-adding-parameter-to-allow-s.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0209-module-switch-on-connect-adding-parameter-to-allow-s.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,88 +0,0 @@ -From: Ricardo Salveti de Araujo -Date: Sun, 20 Jul 2014 01:27:09 -0300 -Subject: [PATCH] module-switch-on-connect: adding parameter to allow - skipping abstract devices - -Signed-off-by: Ricardo Salveti de Araujo ---- - src/modules/module-switch-on-connect.c | 27 +++++++++++++++++++++++++-- - 1 file changed, 25 insertions(+), 2 deletions(-) - -Index: pulseaudio/src/modules/module-switch-on-connect.c -=================================================================== ---- pulseaudio.orig/src/modules/module-switch-on-connect.c -+++ pulseaudio/src/modules/module-switch-on-connect.c -@@ -41,22 +41,25 @@ PA_MODULE_VERSION(PACKAGE_VERSION); - PA_MODULE_LOAD_ONCE(true); - PA_MODULE_USAGE( - "only_from_unavailable= " -+ "skip_abstract=" - ); - - static const char* const valid_modargs[] = { - "only_from_unavailable", -+ "skip_abstract", - NULL, - }; - - struct userdata { - bool only_from_unavailable; -+ bool skip_abstract; - }; - - static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) { - pa_sink_input *i; - uint32_t idx; - pa_sink *def; -- const char *s; -+ const char *s, *class; - struct userdata *u = userdata; - - pa_assert(c); -@@ -75,6 +78,12 @@ static pa_hook_result_t sink_put_hook_ca - return PA_HOOK_OK; - } - -+ /* Check if we want or to skip abstract devices (e.g. null) */ -+ if ((class = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_CLASS))) { -+ if (pa_streq(class, "abstract") && (u->skip_abstract)) -+ return PA_HOOK_OK; -+ } -+ - def = pa_namereg_get_default_sink(c); - if (def == sink) - return PA_HOOK_OK; -@@ -111,7 +120,7 @@ static pa_hook_result_t source_put_hook_ - pa_source_output *o; - uint32_t idx; - pa_source *def; -- const char *s; -+ const char *s, *class; - struct userdata *u = userdata; - - pa_assert(c); -@@ -134,6 +143,12 @@ static pa_hook_result_t source_put_hook_ - return PA_HOOK_OK; - } - -+ /* Check if we want or to skip abstract devices (e.g. null) */ -+ if ((class = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_CLASS))) { -+ if (pa_streq(class, "abstract") && (u->skip_abstract)) -+ return PA_HOOK_OK; -+ } -+ - def = pa_namereg_get_default_source(c); - if (def == source) - return PA_HOOK_OK; -@@ -179,6 +194,11 @@ int pa__init(pa_module*m) { - - m->userdata = u = pa_xnew0(struct userdata, 1); - -+ if (pa_modargs_get_value_boolean(ma, "skip_abstract", &u->skip_abstract) < 0) { -+ pa_log_error("skip_abstract= expects a boolean argument, assuming false by default"); -+ u->skip_abstract = false; -+ } -+ - /* A little bit later than module-rescue-streams... */ - pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+30, (pa_hook_cb_t) sink_put_hook_callback, u); - pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+20, (pa_hook_cb_t) source_put_hook_callback, u); diff -Nru pulseaudio-10.0/debian/patches/0210-module-device-restore-adding-property-to-skip.patch pulseaudio-10.0/debian/patches/0210-module-device-restore-adding-property-to-skip.patch --- pulseaudio-10.0/debian/patches/0210-module-device-restore-adding-property-to-skip.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0210-module-device-restore-adding-property-to-skip.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,70 +0,0 @@ -From: Ricardo Salveti de Araujo -Date: Fri, 15 Aug 2014 02:15:16 -0300 -Subject: [PATCH] module-device-restore: adding property to skip store/restore - -Useful when the user wants to control the volume/port/mute without -storing/restoring the values, at least temporarily (until the property -gets unset). - -Signed-off-by: Ricardo Salveti de Araujo - -Index: pulseaudio/src/modules/module-device-restore.c -=================================================================== ---- pulseaudio.orig/src/modules/module-device-restore.c -+++ pulseaudio/src/modules/module-device-restore.c -@@ -51,6 +51,7 @@ - #include - #include - #include -+#include - - #include "module-device-restore-symdef.h" - -@@ -65,6 +66,7 @@ PA_MODULE_USAGE( - "restore_formats="); - - #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) -+#define MODULE_DEVICE_RESTORE_SKIP_PROPERTY "module-device-restore.skip" - - static const char* const valid_modargs[] = { - "restore_volume", -@@ -602,6 +604,9 @@ static void subscribe_callback(pa_core * - if (!(sink = pa_idxset_get_by_index(c->sinks, idx))) - return; - -+ if (pa_proplist_gets(sink->proplist, MODULE_DEVICE_RESTORE_SKIP_PROPERTY)) -+ return; -+ - type = PA_DEVICE_TYPE_SINK; - name = pa_sprintf_malloc("sink:%s", sink->name); - if (sink->active_port) -@@ -641,6 +646,9 @@ static void subscribe_callback(pa_core * - if (!(source = pa_idxset_get_by_index(c->sources, idx))) - return; - -+ if (pa_proplist_gets(source->proplist, MODULE_DEVICE_RESTORE_SKIP_PROPERTY)) -+ return; -+ - type = PA_DEVICE_TYPE_SOURCE; - name = pa_sprintf_malloc("source:%s", source->name); - if (source->active_port) -@@ -807,6 +815,9 @@ static pa_hook_result_t sink_port_hook_c - pa_assert(u); - pa_assert(u->restore_volume || u->restore_muted); - -+ if (pa_proplist_gets(sink->proplist, MODULE_DEVICE_RESTORE_SKIP_PROPERTY)) -+ return PA_HOOK_OK; -+ - name = pa_sprintf_malloc("sink:%s", sink->name); - - if ((e = perportentry_read(u, name, (sink->active_port ? sink->active_port->name : NULL)))) { -@@ -949,6 +960,9 @@ static pa_hook_result_t source_port_hook - pa_assert(u); - pa_assert(u->restore_volume || u->restore_muted); - -+ if (pa_proplist_gets(source->proplist, MODULE_DEVICE_RESTORE_SKIP_PROPERTY)) -+ return PA_HOOK_OK; -+ - name = pa_sprintf_malloc("source:%s", source->name); - - if ((e = perportentry_read(u, name, (source->active_port ? source->active_port->name : NULL)))) { diff -Nru pulseaudio-10.0/debian/patches/0211-corking-a-sink-input-stream-when-stalled.patch pulseaudio-10.0/debian/patches/0211-corking-a-sink-input-stream-when-stalled.patch --- pulseaudio-10.0/debian/patches/0211-corking-a-sink-input-stream-when-stalled.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0211-corking-a-sink-input-stream-when-stalled.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,117 +0,0 @@ -From: Ricardo Salveti de Araujo -Date: Mon, 24 Nov 2014 11:08:33 -0200 -Subject: [PATCH] Identifying and corking a sink-input stream when it gets stalled - -On Ubuntu Touch the lifecycle will send a SIGSTOP when an app gets moved -to background (after 5 seconds), and in case the app has an active -sink-input stream, pulseaudio will keep consuming the CPU (waiting for -the client to give more data) until the app gets resumed. - -This patch handles only sink-input, and cork/uncork not necessarily -respecting the previous stream state. For the proper implementation, -please follow the discussion at -http://lists.freedesktop.org/archives/pulseaudio-discuss/2014-November/022616.html - -To avoid affecting desktop, an environment variable was used -(PULSE_PLAYBACK_CORK_STALLED), until we get a proper upstream-compatible -implementation. - -Bug-Link: https://bugs.launchpad.net/ubuntu-rtm/+source/pulseaudio/+bug/1391230 - -Index: pulseaudio/src/pulsecore/protocol-native.c -=================================================================== ---- pulseaudio.orig/src/pulsecore/protocol-native.c -+++ pulseaudio/src/pulsecore/protocol-native.c -@@ -126,6 +126,10 @@ typedef struct playback_stream { - - bool is_underrun:1; - bool drain_request:1; -+ -+ bool cork_stalled:1; -+ bool stalled:1; -+ - uint32_t drain_tag; - uint32_t syncid; - -@@ -226,7 +230,9 @@ enum { - PLAYBACK_STREAM_MESSAGE_OVERFLOW, - PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, - PLAYBACK_STREAM_MESSAGE_STARTED, -- PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH -+ PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH, -+ PLAYBACK_STREAM_MESSAGE_CORK, -+ PLAYBACK_STREAM_MESSAGE_UNCORK - }; - - enum { -@@ -691,6 +697,18 @@ static int playback_stream_process_msg(p - break; - } - -+ case PLAYBACK_STREAM_MESSAGE_CORK: { -+ pa_log_debug("Corking '%s'", pa_strnull(pa_proplist_gets(s->sink_input->proplist, PA_PROP_MEDIA_NAME))); -+ pa_sink_input_cork(s->sink_input, true); -+ break; -+ } -+ -+ case PLAYBACK_STREAM_MESSAGE_UNCORK: { -+ pa_log_debug("Uncorking '%s'", pa_strnull(pa_proplist_gets(s->sink_input->proplist, PA_PROP_MEDIA_NAME))); -+ pa_sink_input_cork(s->sink_input, false); -+ break; -+ } -+ - case PLAYBACK_STREAM_MESSAGE_UNDERFLOW: { - pa_tagstruct *t; - -@@ -1043,6 +1061,12 @@ static playback_stream* playback_stream_ - s->sink_input = sink_input; - s->is_underrun = true; - s->drain_request = false; -+ /* Only cork when stalled if indeed required, until an upstream compatible way is implemented */ -+ if (getenv("PULSE_PLAYBACK_CORK_STALLED")) -+ s->cork_stalled = true; -+ else -+ s->cork_stalled = false; -+ s->stalled = false; - pa_atomic_store(&s->missing, 0); - s->buffer_attr_req = *a; - s->adjust_latency = adjust_latency; -@@ -1283,6 +1307,12 @@ static void handle_seek(playback_stream - /* We just ended an underrun, let's ask the sink - * for a complete rewind rewrite */ - -+ /* First make sure the stream is not stalled (corked in our case) */ -+ if (s->cork_stalled && s->stalled) { -+ s->stalled = false; -+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNCORK, NULL, 0, NULL, NULL); -+ } -+ - pa_log_debug("Requesting rewind due to end of underrun."); - pa_sink_input_request_rewind(s->sink_input, - (size_t) (s->sink_input->thread_info.underrun_for == (uint64_t) -1 ? 0 : -@@ -1462,6 +1492,15 @@ static int sink_input_process_msg(pa_msg - return pa_sink_input_process_msg(o, code, userdata, offset, chunk); - } - -+static void handle_stalled_sink_input(playback_stream *s) { -+ /* Mark it as stalled (corked) if underrun for more than 5 seconds */ -+ if (s->is_underrun && s->sink_input->thread_info.underrun_for > 500000) { -+ pa_log_debug("Marking '%s' as stalled", pa_strnull(pa_proplist_gets(s->sink_input->proplist, PA_PROP_MEDIA_NAME))); -+ s->stalled = true; -+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_CORK, NULL, 0, NULL, NULL); -+ } -+} -+ - static bool handle_input_underrun(playback_stream *s, bool force) { - bool send_drain; - -@@ -1513,6 +1552,9 @@ static int sink_input_pop_cb(pa_sink_inp - if (!handle_input_underrun(s, false)) - s->is_underrun = false; - -+ if (s->cork_stalled) -+ handle_stalled_sink_input(s); -+ - /* This call will not fail with prebuf=0, hence we check for - underrun explicitly in handle_input_underrun */ - if (pa_memblockq_peek(s->memblockq, chunk) < 0) diff -Nru pulseaudio-10.0/debian/patches/0501-bluetooth-bluez5-ofono-add-support-for-HFP-gateway-r.patch pulseaudio-10.0/debian/patches/0501-bluetooth-bluez5-ofono-add-support-for-HFP-gateway-r.patch --- pulseaudio-10.0/debian/patches/0501-bluetooth-bluez5-ofono-add-support-for-HFP-gateway-r.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0501-bluetooth-bluez5-ofono-add-support-for-HFP-gateway-r.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,215 +0,0 @@ -From 86873062559af762e78a39febb16452326d0406d Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Sun, 1 Nov 2015 16:27:20 +0100 -Subject: [PATCH 501/507] bluetooth: bluez5: ofono: add support for HFP gateway - role - ---- - src/modules/bluetooth/backend-ofono.c | 88 +++++++++++++++++++++++------------ - src/modules/bluetooth/bluez5-util.c | 14 ++++++ - src/modules/bluetooth/bluez5-util.h | 1 + - 3 files changed, 73 insertions(+), 30 deletions(-) - -Index: pulseaudio/src/modules/bluetooth/backend-ofono.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/backend-ofono.c -+++ pulseaudio/src/modules/bluetooth/backend-ofono.c -@@ -37,6 +37,7 @@ - #define OFONO_SERVICE "org.ofono" - #define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent" - #define HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager" -+#define HF_AUDIO_CARD_INTERFACE OFONO_SERVICE ".HandsfreeAudioCard" - - #define HF_AUDIO_AGENT_PATH "/HandsfreeAudioAgent" - -@@ -151,13 +152,15 @@ static int socket_accept(int sock) - - static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) { - struct hf_audio_card *card = t->userdata; -- int err; - - pa_assert(card); - -- if (!optional) { -+ if (!optional && card->fd < 0) { - DBusMessage *m; - -+ pa_log_debug("Acquiring transport from ofono for card %s", -+ card->path); -+ - pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.ofono.HandsfreeAudioCard", "Connect")); - pa_assert_se(dbus_connection_send(pa_dbus_connection_get(card->backend->connection), m, NULL)); - -@@ -176,12 +179,6 @@ static int hf_audio_agent_transport_acqu - - t->codec = card->codec; - -- err = socket_accept(card->fd); -- if (err < 0) { -- pa_log_error("Deferred setup failed on fd %d: %s", card->fd, pa_cstrerror(-err)); -- return -1; -- } -- - return card->fd; - } - -@@ -190,18 +187,28 @@ static void hf_audio_agent_transport_rel - - pa_assert(card); - -+ pa_log_debug("Trying to release transport for card %s (fd %d)", -+ card->path, card->fd); -+ - if (t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) { - pa_log_info("Transport %s already released", t->path); - return; - } - -- if (card->fd < 0) -- return; -+ if (card->fd > 0) { -+ pa_log_debug("Transport available for card %s (fd %d), releasing now", -+ card->path, card->fd); -+ -+ /* shutdown to make sure connection is dropped immediately */ -+ shutdown(card->fd, SHUT_RDWR); -+ close(card->fd); -+ card->fd = -1; - -- /* shutdown to make sure connection is dropped immediately */ -- shutdown(card->fd, SHUT_RDWR); -- close(card->fd); -- card->fd = -1; -+ pa_log_debug("Successfully released transport for card %s", card->path); -+ -+ pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_IDLE); -+ } -+} - } - - static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char *path, DBusMessageIter *props_i) { -@@ -209,6 +216,7 @@ static void hf_audio_agent_card_found(pa - const char *key, *value; - struct hf_audio_card *card; - pa_bluetooth_device *d; -+ pa_bluetooth_profile_t profile = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY; - - pa_assert(backend); - pa_assert(path); -@@ -227,22 +235,29 @@ static void hf_audio_agent_card_found(pa - dbus_message_iter_next(&i); - dbus_message_iter_recurse(&i, &value_i); - -- if ((c = dbus_message_iter_get_arg_type(&value_i)) != DBUS_TYPE_STRING) { -- pa_log_error("Invalid properties for %s: expected 's', received '%c'", path, c); -- goto fail; -- } -+ if ((c = dbus_message_iter_get_arg_type(&value_i)) == DBUS_TYPE_STRING) { -+ dbus_message_iter_get_basic(&value_i, &value); - -- dbus_message_iter_get_basic(&value_i, &value); -+ if (pa_streq(key, "Type")) { -+ if (pa_streq(value, "gateway")) -+ profile = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT; -+ else if (pa_streq(value, "handsfree")) -+ profile = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY; -+ } else if (pa_streq(key, "RemoteAddress")) { -+ pa_xfree(card->remote_address); -+ card->remote_address = pa_xstrdup(value); -+ } else if (pa_streq(key, "LocalAddress")) { -+ pa_xfree(card->local_address); -+ card->local_address = pa_xstrdup(value); -+ } - -- if (pa_streq(key, "RemoteAddress")) { -- pa_xfree(card->remote_address); -- card->remote_address = pa_xstrdup(value); -- } else if (pa_streq(key, "LocalAddress")) { -- pa_xfree(card->local_address); -- card->local_address = pa_xstrdup(value); -- } -+ pa_log_debug("%s: %s", key, value); - -- pa_log_debug("%s: %s", key, value); -+ } else if ((c = dbus_message_iter_get_arg_type(&value_i)) == DBUS_TYPE_UINT16) { -+ /* Ignore for now */ -+ } else { -+ pa_log_error("Invalid properties for %s: expected 's' or 'q', received '%c'", path, c); -+ } - - dbus_message_iter_next(props_i); - } -@@ -253,7 +268,7 @@ static void hf_audio_agent_card_found(pa - goto fail; - } - -- card->transport = pa_bluetooth_transport_new(d, backend->ofono_bus_id, path, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY, NULL, 0); -+ card->transport = pa_bluetooth_transport_new(d, backend->ofono_bus_id, path, profile, NULL, 0); - card->transport->acquire = hf_audio_agent_transport_acquire; - card->transport->release = hf_audio_agent_transport_release; - card->transport->userdata = card; -@@ -529,12 +544,25 @@ static DBusMessage *hf_audio_agent_new_c - - card = pa_hashmap_get(backend->cards, path); - -- if (!card || codec != HFP_AUDIO_CODEC_CVSD || card->transport->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) { -- pa_log_warn("New audio connection invalid arguments (path=%s fd=%d, codec=%d)", path, fd, codec); -+ if (!card || codec != HFP_AUDIO_CODEC_CVSD) { -+ pa_log_warn("New audio connection invalid arguments (path=%s fd=%d, codec=%d, transport [state=%s, profile=%s])", -+ path, fd, codec, -+ card ? pa_bluetooth_transport_state_to_string(card->transport->state) : "unknown", -+ card ? pa_bluetooth_profile_to_string(card->transport->profile) : "unknown"); - pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call")); - return r; - } - -+ if (card->transport->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) { -+ pa_log_warn("Could not activate new audio connection as it is already active!? " -+ "(path=%s fd=%d, codec=%d, transport [state=%s, profile=%s])", -+ path, fd, codec, -+ pa_bluetooth_transport_state_to_string(card->transport->state), -+ pa_bluetooth_profile_to_string(card->transport->profile)); -+ pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Transport is already active")); -+ return r; -+ } -+ - pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", path, fd, codec); - - card->fd = fd; -Index: pulseaudio/src/modules/bluetooth/bluez5-util.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/bluez5-util.c -+++ pulseaudio/src/modules/bluetooth/bluez5-util.c -@@ -1273,6 +1273,20 @@ const char *pa_bluetooth_profile_to_stri - return NULL; - } - -+const char *pa_bluetooth_transport_state_to_string(pa_bluetooth_transport_state_t state) -+{ -+ switch (state) { -+ case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED: -+ return "disconnected"; -+ case PA_BLUETOOTH_TRANSPORT_STATE_IDLE: -+ return "idle"; -+ case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING: -+ return "playing"; -+ } -+ -+ return NULL; -+} -+ - static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) { - pa_bluetooth_discovery *y = userdata; - pa_bluetooth_device *d; -Index: pulseaudio/src/modules/bluetooth/bluez5-util.h -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/bluez5-util.h -+++ pulseaudio/src/modules/bluetooth/bluez5-util.h -@@ -153,6 +153,7 @@ pa_bluetooth_device* pa_bluetooth_discov - pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook); - - const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile); -+const char *pa_bluetooth_transport_state_to_string(pa_bluetooth_transport_state_t state); - - #define HEADSET_BACKEND_OFONO 0 - #define HEADSET_BACKEND_NATIVE 1 diff -Nru pulseaudio-10.0/debian/patches/0502-bluetooth-bluez5-bring-back-SCO-over-PCM-support.patch pulseaudio-10.0/debian/patches/0502-bluetooth-bluez5-bring-back-SCO-over-PCM-support.patch --- pulseaudio-10.0/debian/patches/0502-bluetooth-bluez5-bring-back-SCO-over-PCM-support.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0502-bluetooth-bluez5-bring-back-SCO-over-PCM-support.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,753 +0,0 @@ -From 63e70a2730f8a1a4ffe9301a9a33d6072eba8b2b Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Sun, 1 Nov 2015 16:38:39 +0100 -Subject: [PATCH 502/507] bluetooth: bluez5: bring back SCO over PCM support - ---- - src/modules/bluetooth/module-bluez5-device.c | 402 ++++++++++++++++++++----- - src/modules/bluetooth/module-bluez5-discover.c | 19 +- - 2 files changed, 346 insertions(+), 75 deletions(-) - -Index: pulseaudio/src/modules/bluetooth/module-bluez5-device.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/module-bluez5-device.c -+++ pulseaudio/src/modules/bluetooth/module-bluez5-device.c -@@ -30,6 +30,7 @@ - #include - #include - -+#include - #include - #include - #include -@@ -43,6 +44,8 @@ - #include - #include - #include -+#include -+#include - - #include "a2dp-codecs.h" - #include "bluez5-util.h" -@@ -54,7 +57,9 @@ PA_MODULE_AUTHOR("João Paulo Rechi Vita - PA_MODULE_DESCRIPTION("BlueZ 5 Bluetooth audio sink and source"); - PA_MODULE_VERSION(PACKAGE_VERSION); - PA_MODULE_LOAD_ONCE(false); --PA_MODULE_USAGE("path="); -+PA_MODULE_USAGE("path= " -+ "sco_sink= " -+ "sco_source= "); - - #define MAX_PLAYBACK_CATCH_UP_USEC (100 * PA_USEC_PER_MSEC) - #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC) -@@ -66,8 +71,11 @@ PA_MODULE_USAGE("path=profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT && (u->hsp.sco_sink && u->hsp.sco_source)) - static const char* const valid_modargs[] = { - "path", -+ "sco_sink", -+ "sco_source", - NULL - }; - -@@ -96,15 +104,27 @@ typedef struct sbc_info { - size_t buffer_size; /* Size of the buffer */ - } sbc_info_t; - -+struct hsp_info { -+ pa_sink *sco_sink; -+ void (*sco_sink_set_volume)(pa_sink *s); -+ pa_source *sco_source; -+ void (*sco_source_set_volume)(pa_source *s); -+}; -+ - struct userdata { - pa_module *module; - pa_core *core; - -+ pa_modargs *modargs; -+ - pa_hook_slot *device_connection_changed_slot; - pa_hook_slot *transport_state_changed_slot; - pa_hook_slot *transport_speaker_gain_changed_slot; - pa_hook_slot *transport_microphone_gain_changed_slot; - -+ pa_hook_slot *sink_state_changed_slot; -+ pa_hook_slot *source_state_changed_slot; -+ - pa_bluetooth_discovery *discovery; - pa_bluetooth_device *device; - pa_bluetooth_transport *transport; -@@ -136,6 +156,10 @@ struct userdata { - pa_memchunk write_memchunk; - pa_sample_spec sample_spec; - struct sbc_info sbc_info; -+ struct hsp_info hsp; -+ -+ bool transport_acquire_pending; -+ pa_io_event *stream_event; - }; - - typedef enum pa_bluetooth_form_factor { -@@ -712,6 +736,11 @@ static void teardown_stream(struct userd - u->rtpoll_item = NULL; - } - -+ if (u->stream_event) { -+ u->core->mainloop->io_free(u->stream_event); -+ u->stream_event = NULL; -+ } -+ - if (u->stream_fd >= 0) { - pa_close(u->stream_fd); - u->stream_fd = -1; -@@ -733,18 +762,29 @@ static void teardown_stream(struct userd - static int transport_acquire(struct userdata *u, bool optional) { - pa_assert(u->transport); - -- if (u->transport_acquired) -+ if (u->transport_acquire_pending) -+ return -1; -+ -+ if (u->transport_acquired) { -+ pa_log_debug("Transport already acquired"); - return 0; -+ } -+ -+ u->transport_acquire_pending = true; - - pa_log_debug("Acquiring transport %s", u->transport->path); - - u->stream_fd = u->transport->acquire(u->transport, optional, &u->read_link_mtu, &u->write_link_mtu); -- if (u->stream_fd < 0) -+ if (u->stream_fd < 0) { -+ u->transport_acquire_pending = false; - return -1; -+ } - - u->transport_acquired = true; - pa_log_info("Transport %s acquired: fd %d", u->transport->path, u->stream_fd); - -+ u->transport_acquire_pending = false; -+ - return 0; - } - -@@ -766,6 +806,10 @@ static void transport_release(struct use - - /* Run from I/O thread */ - static void transport_config_mtu(struct userdata *u) { -+ -+ pa_log_debug("Configuring MTU for transport of profile %s", -+ pa_bluetooth_profile_to_string(u->profile)); -+ - if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) { - u->read_block_size = u->read_link_mtu; - u->write_block_size = u->write_link_mtu; -@@ -779,6 +823,9 @@ static void transport_config_mtu(struct - / u->sbc_info.frame_length * u->sbc_info.codesize; - } - -+ if (USE_SCO_OVER_PCM(u)) -+ return; -+ - if (u->sink) { - pa_sink_set_max_request_within_thread(u->sink, u->write_block_size); - pa_sink_set_fixed_latency_within_thread(u->sink, -@@ -794,7 +841,7 @@ static void transport_config_mtu(struct - pa_bytes_to_usec(u->read_block_size, &u->sample_spec)); - } - --/* Run from I/O thread */ -+/* Run from I/O thread except in SCO over PCM */ - static void setup_stream(struct userdata *u) { - struct pollfd *pollfd; - int one; -@@ -943,46 +990,53 @@ static int add_source(struct userdata *u - - pa_assert(u->transport); - -- pa_source_new_data_init(&data); -- data.module = u->module; -- data.card = u->card; -- data.driver = __FILE__; -- data.name = pa_sprintf_malloc("bluez_source.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile)); -- data.namereg_fail = false; -- pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); -- pa_source_new_data_set_sample_spec(&data, &u->sample_spec); -- if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) -- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); -- -- connect_ports(u, &data, PA_DIRECTION_INPUT); -+ if (USE_SCO_OVER_PCM(u)) { -+ u->source = u->hsp.sco_source; -+ pa_proplist_sets(u->source->proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); -+ } else { -+ pa_source_new_data_init(&data); -+ data.module = u->module; -+ data.card = u->card; -+ data.driver = __FILE__; -+ data.name = pa_sprintf_malloc("bluez_source.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile)); -+ data.namereg_fail = false; -+ pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); -+ pa_source_new_data_set_sample_spec(&data, &u->sample_spec); -+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) -+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); -+ -+ connect_ports(u, &data, PA_DIRECTION_INPUT); -+ -+ if (!u->transport_acquired) { -+ switch (u->profile) { -+ case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: -+ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: -+ data.suspend_cause = PA_SUSPEND_USER; -+ break; -+ case PA_BLUETOOTH_PROFILE_A2DP_SINK: -+ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: -+ case PA_BLUETOOTH_PROFILE_OFF: -+ pa_assert_not_reached(); -+ break; -+ } -+ } - -- if (!u->transport_acquired) -- switch (u->profile) { -- case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: -- case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: -- data.suspend_cause = PA_SUSPEND_USER; -- break; -- case PA_BLUETOOTH_PROFILE_A2DP_SINK: -- case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: -- case PA_BLUETOOTH_PROFILE_OFF: -- pa_assert_not_reached(); -- break; -+ u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); -+ pa_source_new_data_done(&data); -+ if (!u->source) { -+ pa_log_error("Failed to create source"); -+ return -1; - } - -- u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); -- pa_source_new_data_done(&data); -- if (!u->source) { -- pa_log_error("Failed to create source"); -- return -1; -+ u->source->userdata = u; -+ u->source->parent.process_msg = source_process_msg; - } - -- u->source->userdata = u; -- u->source->parent.process_msg = source_process_msg; -- - if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) { - pa_source_set_set_volume_callback(u->source, source_set_volume_cb); - u->source->n_volume_steps = 16; - } -+ - return 0; - } - -@@ -1100,52 +1154,67 @@ static int add_sink(struct userdata *u) - - pa_assert(u->transport); - -- pa_sink_new_data_init(&data); -- data.module = u->module; -- data.card = u->card; -- data.driver = __FILE__; -- data.name = pa_sprintf_malloc("bluez_sink.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile)); -- data.namereg_fail = false; -- pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); -- pa_sink_new_data_set_sample_spec(&data, &u->sample_spec); -- if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) -- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); -- -- connect_ports(u, &data, PA_DIRECTION_OUTPUT); -+ if (USE_SCO_OVER_PCM(u)) { -+ pa_proplist *p; -+ u->sink = u->hsp.sco_sink; -+ p = pa_proplist_new(); -+ pa_proplist_sets(p, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); -+ pa_proplist_update(u->sink->proplist, PA_UPDATE_MERGE, p); -+ pa_proplist_free(p); -+ } else { -+ pa_sink_new_data_init(&data); -+ data.module = u->module; -+ data.card = u->card; -+ data.driver = __FILE__; -+ data.name = pa_sprintf_malloc("bluez_sink.%s.%s", u->device->address, pa_bluetooth_profile_to_string(u->profile)); -+ data.namereg_fail = false; -+ pa_proplist_sets(data.proplist, "bluetooth.protocol", pa_bluetooth_profile_to_string(u->profile)); -+ pa_sink_new_data_set_sample_spec(&data, &u->sample_spec); -+ if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) -+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); -+ -+ connect_ports(u, &data, PA_DIRECTION_OUTPUT); -+ -+ if (!u->transport_acquired) { -+ switch (u->profile) { -+ case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: -+ data.suspend_cause = PA_SUSPEND_USER; -+ break; -+ case PA_BLUETOOTH_PROFILE_A2DP_SINK: -+ /* Profile switch should have failed */ -+ case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: -+ case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: -+ case PA_BLUETOOTH_PROFILE_OFF: -+ pa_assert_not_reached(); -+ break; -+ } -+ } - -- if (!u->transport_acquired) -- switch (u->profile) { -- case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: -- data.suspend_cause = PA_SUSPEND_USER; -- break; -- case PA_BLUETOOTH_PROFILE_A2DP_SINK: -- /* Profile switch should have failed */ -- case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: -- case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: -- case PA_BLUETOOTH_PROFILE_OFF: -- pa_assert_not_reached(); -- break; -+ u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); -+ pa_sink_new_data_done(&data); -+ if (!u->sink) { -+ pa_log_error("Failed to create sink"); -+ return -1; - } - -- u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); -- pa_sink_new_data_done(&data); -- if (!u->sink) { -- pa_log_error("Failed to create sink"); -- return -1; -+ u->sink->userdata = u; -+ u->sink->parent.process_msg = sink_process_msg; - } - -- u->sink->userdata = u; -- u->sink->parent.process_msg = sink_process_msg; -- - if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) { - pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); - u->sink->n_volume_steps = 16; - } -+ - return 0; - } - - /* Run from main thread */ - static void transport_config(struct userdata *u) { -+ -+ pa_log_debug("Configuring transport for profile %s", -+ pa_bluetooth_profile_to_string(u->profile)); -+ - if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) { - u->sample_spec.format = PA_SAMPLE_S16LE; - u->sample_spec.channels = 1; -@@ -1264,11 +1333,18 @@ static int setup_transport(struct userda - pa_bluetooth_transport *t; - - pa_assert(u); -- pa_assert(!u->transport); -+ pa_assert(!u->transport_acquired); - pa_assert(u->profile != PA_BLUETOOTH_PROFILE_OFF); - -+ pa_log_debug("profile %s", pa_bluetooth_profile_to_string(u->profile)); -+ - /* check if profile has a transport */ - t = u->device->transports[u->profile]; -+ -+ pa_log_debug("profile %s transport %p transport state %s", -+ pa_bluetooth_profile_to_string(u->profile), -+ t, t ? pa_bluetooth_transport_state_to_string(t->state) : "unknown"); -+ - if (!t || t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) { - pa_log_warn("Profile has no transport"); - return -1; -@@ -1305,11 +1381,16 @@ static int init_profile(struct userdata - pa_assert(u); - pa_assert(u->profile != PA_BLUETOOTH_PROFILE_OFF); - -+ pa_log_debug("Initializing profile %s", pa_bluetooth_profile_to_string(u->profile)); -+ - if (setup_transport(u) < 0) - return -1; - - pa_assert(u->transport); - -+ pa_log_debug("Transport for profile %s successfully setup", -+ pa_bluetooth_profile_to_string(u->profile)); -+ - if (get_profile_direction (u->profile) & PA_DIRECTION_OUTPUT) - if (add_sink(u) < 0) - r = -1; -@@ -1517,6 +1598,63 @@ finish: - pa_log_debug("IO thread shutting down"); - } - -+static int sco_over_pcm_state_update(struct userdata *u, bool changed) -+{ -+ pa_assert(u); -+ pa_assert(USE_SCO_OVER_PCM(u)); -+ -+ pa_log_debug("Updating SCO over PCM state (profile %s, changed %s, stream fd %d)", -+ pa_bluetooth_profile_to_string(u->profile), -+ changed ? "yes" : "no", u->stream_fd); -+ -+ if (PA_SINK_IS_OPENED(pa_sink_get_state(u->hsp.sco_sink)) || -+ PA_SOURCE_IS_OPENED(pa_source_get_state(u->hsp.sco_source))) { -+ -+ if (u->stream_fd >= 0) -+ return 0; -+ -+ pa_log_debug("Resuming SCO over PCM"); -+ -+ if (init_profile(u) < 0) { -+ pa_log("Can't resume SCO over PCM"); -+ return -1; -+ } -+ -+ setup_stream(u); -+ -+ return 0; -+ } -+ -+ if (changed) { -+ if (u->stream_fd < 0) -+ return 0; -+ -+ if (check_proplist(u) == 1) { -+ pa_log_debug("Suspend prevention active, not closing SCO over PCM"); -+ return 0; -+ } -+ -+ pa_log_debug("Closing SCO over PCM"); -+ -+ transport_release(u); -+ } -+ -+ return 0; -+} -+ -+static void stream_died_cb(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) -+{ -+ struct userdata *u = userdata; -+ -+ pa_assert(u); -+ pa_assert(u->transport); -+ -+ pa_log_warn("SCO stream went down"); -+ -+ pa_bluetooth_transport_set_state(u->transport, PA_BLUETOOTH_TRANSPORT_STATE_IDLE); -+ -+} -+ - /* Run from main thread */ - static int start_thread(struct userdata *u) { - pa_assert(u); -@@ -1531,6 +1669,25 @@ static int start_thread(struct userdata - return -1; - } - -+ if (USE_SCO_OVER_PCM(u)) { -+ if (sco_over_pcm_state_update(u, false) < 0) -+ return -1; -+ -+ pa_log_debug("Installing monitor for SCO stream"); -+ -+ u->stream_event = u->core->mainloop->io_new(u->core->mainloop, -+ u->stream_fd, PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR, stream_died_cb, u); -+ if (!u->stream_event) { -+ pa_log_error("Failed to setup monitoring for SCO stream"); -+ return -1; -+ } -+ -+ pa_sink_ref(u->sink); -+ pa_source_ref(u->source); -+ -+ return 0; -+ } -+ - if (!(u->thread = pa_thread_new("bluetooth", thread_func, u))) { - pa_log_error("Failed to create IO thread"); - return -1; -@@ -1561,10 +1718,10 @@ static int start_thread(struct userdata - static void stop_thread(struct userdata *u) { - pa_assert(u); - -- if (u->sink) -+ if (u->sink && !USE_SCO_OVER_PCM(u)) - pa_sink_unlink(u->sink); - -- if (u->source) -+ if (u->source && !USE_SCO_OVER_PCM(u)) - pa_source_unlink(u->source); - - if (u->thread) { -@@ -1854,6 +2011,22 @@ static pa_card_profile *create_card_prof - return cp; - } - -+static void save_sco_volume_callbacks(struct userdata *u) { -+ pa_assert(u); -+ pa_assert(USE_SCO_OVER_PCM(u)); -+ -+ u->hsp.sco_sink_set_volume = u->hsp.sco_sink->set_volume; -+ u->hsp.sco_source_set_volume = u->hsp.sco_source->set_volume; -+} -+ -+static void restore_sco_volume_callbacks(struct userdata *u) { -+ pa_assert(u); -+ pa_assert(USE_SCO_OVER_PCM(u)); -+ -+ pa_sink_set_set_volume_callback(u->hsp.sco_sink, u->hsp.sco_sink_set_volume); -+ pa_source_set_set_volume_callback(u->hsp.sco_source, u->hsp.sco_source_set_volume); -+} -+ - /* Run from main thread */ - static int set_profile_cb(pa_card *c, pa_card_profile *new_profile) { - struct userdata *u; -@@ -1865,6 +2038,10 @@ static int set_profile_cb(pa_card *c, pa - - p = PA_CARD_PROFILE_DATA(new_profile); - -+ pa_log_debug("Setting new profile %s for card (current %s)", -+ pa_bluetooth_profile_to_string(*p), -+ pa_bluetooth_profile_to_string(u->profile)); -+ - if (*p != PA_BLUETOOTH_PROFILE_OFF) { - const pa_bluetooth_device *d = u->device; - -@@ -1987,6 +2164,11 @@ static int add_card(struct userdata *u) - p = PA_CARD_PROFILE_DATA(u->card->active_profile); - u->profile = *p; - -+ if (USE_SCO_OVER_PCM(u)) -+ save_sco_volume_callbacks(u); -+ -+ pa_log_debug("Created card (current profile %s)", -+ pa_bluetooth_profile_to_string(u->profile)); - return 0; - } - -@@ -1996,13 +2178,15 @@ static void handle_transport_state_chang - bool release = false; - pa_card_profile *cp; - pa_device_port *port; -- pa_available_t oldavail; - - pa_assert(u); - pa_assert(t); - pa_assert_se(cp = pa_hashmap_get(u->card->profiles, pa_bluetooth_profile_to_string(t->profile))); - -- oldavail = cp->available; -+ pa_log_debug("State of transport for profile %s changed to %s", -+ pa_bluetooth_profile_to_string(t->profile), -+ pa_bluetooth_transport_state_to_string(t->state)); -+ - pa_card_profile_set_available(cp, transport_state_to_availability(t->state)); - - /* Update port availability */ -@@ -2013,9 +2197,13 @@ static void handle_transport_state_chang - - /* Acquire or release transport as needed */ - acquire = (t->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == t->profile); -- release = (oldavail != PA_AVAILABLE_NO && t->state != PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == t->profile); -+ release = (t->state != PA_BLUETOOTH_TRANSPORT_STATE_PLAYING && u->profile == t->profile); - - if (acquire && transport_acquire(u, true) >= 0) { -+ -+ pa_log_debug("Acquiring transport for profile %s", -+ pa_bluetooth_profile_to_string(t->profile)); -+ - if (u->source) { - pa_log_debug("Resuming source %s because its transport state changed to playing", u->source->name); - -@@ -2043,6 +2231,9 @@ static void handle_transport_state_chang - * BlueZ should probably release the transport automatically, and in - * that case we would just mark the transport as released */ - -+ pa_log_debug("Releasing transport for profile %s", -+ pa_bluetooth_profile_to_string(t->profile)); -+ - /* Remote side closed the stream so we consider it PA_SUSPEND_USER */ - if (u->source) { - pa_log_debug("Suspending source %s because the remote end closed the stream", u->source->name); -@@ -2075,6 +2266,10 @@ static pa_hook_result_t transport_state_ - pa_assert(t); - pa_assert(u); - -+ pa_log_debug("State of transport for profile %s has changed to %s", -+ pa_bluetooth_profile_to_string(t->profile), -+ pa_bluetooth_transport_state_to_string(t->state)); -+ - if (t == u->transport && t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) - pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); - -@@ -2132,6 +2327,36 @@ static pa_hook_result_t transport_microp - return PA_HOOK_OK; - } - -+static pa_hook_result_t sink_state_changed_cb(pa_core *c, pa_sink *s, struct userdata *u) { -+ pa_assert(c); -+ pa_sink_assert_ref(s); -+ pa_assert(u); -+ -+ pa_log_debug("Sink %s state has changed", s->name); -+ -+ if (!USE_SCO_OVER_PCM(u) || s != u->hsp.sco_sink) -+ return PA_HOOK_OK; -+ -+ sco_over_pcm_state_update(u, true); -+ -+ return PA_HOOK_OK; -+} -+ -+static pa_hook_result_t source_state_changed_cb(pa_core *c, pa_source *s, struct userdata *u) { -+ pa_assert(c); -+ pa_source_assert_ref(s); -+ pa_assert(u); -+ -+ pa_log_debug("Source %s state has changed", s->name); -+ -+ if (!USE_SCO_OVER_PCM(u) || s != u->hsp.sco_source) -+ return PA_HOOK_OK; -+ -+ sco_over_pcm_state_update(u, true); -+ -+ return PA_HOOK_OK; -+} -+ - /* Run from main thread context */ - static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { - struct bluetooth_msg *m = BLUETOOTH_MSG(obj); -@@ -2175,6 +2400,18 @@ int pa__init(pa_module* m) { - goto fail_free_modargs; - } - -+ if (pa_modargs_get_value(ma, "sco_sink", NULL) && -+ !(u->hsp.sco_sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_sink", NULL), PA_NAMEREG_SINK))) { -+ pa_log("SCO sink not found"); -+ goto fail; -+ } -+ -+ if (pa_modargs_get_value(ma, "sco_source", NULL) && -+ !(u->hsp.sco_source = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_source", NULL), PA_NAMEREG_SOURCE))) { -+ pa_log("SCO source not found"); -+ goto fail; -+ } -+ - if ((u->discovery = pa_shared_get(u->core, "bluetooth-discovery"))) - pa_bluetooth_discovery_ref(u->discovery); - else { -@@ -2187,7 +2424,7 @@ int pa__init(pa_module* m) { - goto fail_free_modargs; - } - -- pa_modargs_free(ma); -+ u->modargs = ma; - - u->device_connection_changed_slot = - pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED), -@@ -2263,12 +2500,20 @@ void pa__done(pa_module *m) { - if (u->transport_microphone_gain_changed_slot) - pa_hook_slot_free(u->transport_microphone_gain_changed_slot); - -+ if (u->sink_state_changed_slot) -+ pa_hook_slot_free(u->sink_state_changed_slot); -+ -+ if (u->source_state_changed_slot) -+ pa_hook_slot_free(u->source_state_changed_slot); - if (u->sbc_info.buffer) - pa_xfree(u->sbc_info.buffer); - - if (u->sbc_info.sbc_initialized) - sbc_finish(&u->sbc_info.sbc); - -+ if (USE_SCO_OVER_PCM(u)) -+ restore_sco_volume_callbacks(u); -+ - if (u->msg) - pa_xfree(u->msg); - -@@ -2281,6 +2526,8 @@ void pa__done(pa_module *m) { - pa_xfree(u->output_port_name); - pa_xfree(u->input_port_name); - -+ if (u->modargs) -+ pa_modargs_free(u->modargs); - pa_xfree(u); - } - -Index: pulseaudio/src/modules/bluetooth/module-bluez5-discover.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/module-bluez5-discover.c -+++ pulseaudio/src/modules/bluetooth/module-bluez5-discover.c -@@ -38,15 +38,20 @@ PA_MODULE_VERSION(PACKAGE_VERSION); - PA_MODULE_LOAD_ONCE(true); - PA_MODULE_USAGE( - "headset=ofono|native|auto" -+ "sco_sink= " -+ "sco_source= " - ); - - static const char* const valid_modargs[] = { - "headset", -+ "sco_sink", -+ "sco_source", - NULL - }; - - struct userdata { - pa_module *module; -+ pa_modargs *modargs; - pa_core *core; - pa_hashmap *loaded_device_paths; - pa_hook_slot *device_connection_changed_slot; -@@ -73,6 +78,16 @@ static pa_hook_result_t device_connectio - pa_module *m; - char *args = pa_sprintf_malloc("path=%s", d->path); - -+ if (pa_modargs_get_value(u->modargs, "sco_sink", NULL) && -+ pa_modargs_get_value(u->modargs, "sco_source", NULL)) { -+ char *tmp; -+ -+ tmp = pa_sprintf_malloc("%s sco_sink=\"%s\" sco_source=\"%s\"", args, -+ pa_modargs_get_value(u->modargs, "sco_sink", NULL), -+ pa_modargs_get_value(u->modargs, "sco_source", NULL)); -+ pa_xfree(args); -+ args = tmp; -+ } - pa_log_debug("Loading module-bluez5-device %s", args); - m = pa_module_load(u->module->core, "module-bluez5-device", args); - pa_xfree(args); -@@ -123,6 +138,7 @@ int pa__init(pa_module *m) { - - m->userdata = u = pa_xnew0(struct userdata, 1); - u->module = m; -+ u->modargs = ma; - u->core = m->core; - u->loaded_device_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - -@@ -133,7 +149,6 @@ 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); - -- pa_modargs_free(ma); - return 0; - - fail: -@@ -160,5 +175,7 @@ void pa__done(pa_module *m) { - if (u->loaded_device_paths) - pa_hashmap_free(u->loaded_device_paths); - -+ if (u->modargs) -+ pa_modargs_free(u->modargs); - pa_xfree(u); - } diff -Nru pulseaudio-10.0/debian/patches/0503-bluetooth-bluez5-ofono-add-support-for-spekaer-micro.patch pulseaudio-10.0/debian/patches/0503-bluetooth-bluez5-ofono-add-support-for-spekaer-micro.patch --- pulseaudio-10.0/debian/patches/0503-bluetooth-bluez5-ofono-add-support-for-spekaer-micro.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0503-bluetooth-bluez5-ofono-add-support-for-spekaer-micro.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,78 +0,0 @@ -From 8b4525c069d19f783ea2a70415621da71d1fede6 Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Sun, 1 Nov 2015 16:40:16 +0100 -Subject: [PATCH 503/507] bluetooth: bluez5: ofono: add support for - spekaer/microphone gain setting - ---- - src/modules/bluetooth/backend-ofono.c | 47 +++++++++++++++++++++++++++++++++++ - 1 file changed, 47 insertions(+) - -diff --git a/src/modules/bluetooth/backend-ofono.c b/src/modules/bluetooth/backend-ofono.c -index c77be54..1c10716 100644 ---- a/src/modules/bluetooth/backend-ofono.c -+++ b/src/modules/bluetooth/backend-ofono.c -@@ -209,6 +209,51 @@ static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) { - pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_IDLE); - } - } -+ -+static void set_property(pa_dbus_connection *conn, const char *bus, const char *path, const char *interface, -+ const char *prop_name, int prop_type, void *prop_value) { -+ DBusMessage *m; -+ DBusMessageIter i; -+ -+ pa_assert(conn); -+ pa_assert(path); -+ pa_assert(interface); -+ pa_assert(prop_name); -+ -+ pa_assert_se(m = dbus_message_new_method_call(bus, path, interface, "SetProperty")); -+ dbus_message_iter_init_append(m, &i); -+ dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &prop_name); -+ pa_dbus_append_basic_variant(&i, prop_type, prop_value); -+ -+ dbus_message_set_no_reply(m, true); -+ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(conn), m, NULL)); -+ dbus_message_unref(m); -+} -+ -+static void hf_audio_card_set_speaker_gain(pa_bluetooth_transport *t, uint16_t gain) -+{ -+ struct hf_audio_card *card = t->userdata; -+ -+ pa_assert(card); -+ -+ pa_log_debug("Setting speaker gain for card %s to %u", -+ card->path, gain); -+ -+ set_property(card->backend->connection, OFONO_SERVICE, card->path, -+ HF_AUDIO_CARD_INTERFACE, "SpeakerGain", DBUS_TYPE_UINT16, &gain); -+} -+ -+static void hf_audio_card_set_microphone_gain(pa_bluetooth_transport *t, uint16_t gain) -+{ -+ struct hf_audio_card *card = t->userdata; -+ -+ pa_assert(card); -+ -+ pa_log_debug("Setting microphone gain for card %s to %u", -+ card->path, gain); -+ -+ set_property(card->backend->connection, OFONO_SERVICE, card->path, -+ HF_AUDIO_CARD_INTERFACE, "MicrophoneGain", DBUS_TYPE_UINT16, &gain); - } - - static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char *path, DBusMessageIter *props_i) { -@@ -271,6 +316,8 @@ static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char - card->transport = pa_bluetooth_transport_new(d, backend->ofono_bus_id, path, profile, NULL, 0); - card->transport->acquire = hf_audio_agent_transport_acquire; - card->transport->release = hf_audio_agent_transport_release; -+ card->transport->set_speaker_gain = hf_audio_card_set_speaker_gain; -+ card->transport->set_microphone_gain = hf_audio_card_set_microphone_gain; - card->transport->userdata = card; - - pa_bluetooth_transport_put(card->transport); --- -2.6.2 - diff -Nru pulseaudio-10.0/debian/patches/0504-bluetooth-bluez5-add-support-for-both-mode.patch pulseaudio-10.0/debian/patches/0504-bluetooth-bluez5-add-support-for-both-mode.patch --- pulseaudio-10.0/debian/patches/0504-bluetooth-bluez5-add-support-for-both-mode.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0504-bluetooth-bluez5-add-support-for-both-mode.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,75 +0,0 @@ -From 9d69ecf36d2ef447208d287566727322edf82e2f Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Sun, 1 Nov 2015 16:41:04 +0100 -Subject: [PATCH 504/507] bluetooth: bluez5: add support for both mode - ---- - src/modules/bluetooth/bluez5-util.c | 4 +++- - src/modules/bluetooth/bluez5-util.h | 1 + - src/modules/bluetooth/module-bluez5-discover.c | 10 ++++++++-- - 3 files changed, 12 insertions(+), 3 deletions(-) - -Index: pulseaudio/src/modules/bluetooth/bluez5-util.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/bluez5-util.c -+++ pulseaudio/src/modules/bluetooth/bluez5-util.c -@@ -1030,7 +1030,9 @@ static void get_managed_objects_reply(DB - - if (!y->ofono_backend && y->headset_backend != HEADSET_BACKEND_NATIVE) - y->ofono_backend = pa_bluetooth_ofono_backend_new(y->core, y); -- if (!y->ofono_backend && !y->native_backend && y->headset_backend != HEADSET_BACKEND_OFONO) -+ if (!y->native_backend && y->headset_backend == HEADSET_BACKEND_BOTH) -+ y->native_backend = pa_bluetooth_native_backend_new(y->core, y); -+ else if (!y->ofono_backend && !y->native_backend) - y->native_backend = pa_bluetooth_native_backend_new(y->core, y); - - finish: -Index: pulseaudio/src/modules/bluetooth/bluez5-util.h -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/bluez5-util.h -+++ pulseaudio/src/modules/bluetooth/bluez5-util.h -@@ -158,6 +158,7 @@ const char *pa_bluetooth_transport_state - #define HEADSET_BACKEND_OFONO 0 - #define HEADSET_BACKEND_NATIVE 1 - #define HEADSET_BACKEND_AUTO 2 -+#define HEADSET_BACKEND_BOTH 3 - - pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core, int headset_backend); - pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y); -Index: pulseaudio/src/modules/bluetooth/module-bluez5-discover.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/module-bluez5-discover.c -+++ pulseaudio/src/modules/bluetooth/module-bluez5-discover.c -@@ -37,7 +37,7 @@ PA_MODULE_DESCRIPTION("Detect available - PA_MODULE_VERSION(PACKAGE_VERSION); - PA_MODULE_LOAD_ONCE(true); - PA_MODULE_USAGE( -- "headset=ofono|native|auto" -+ "headset=ofono|native|auto|both " - "sco_sink= " - "sco_source= " - ); -@@ -106,7 +106,11 @@ static pa_hook_result_t device_connectio - } - - #ifdef HAVE_BLUEZ_5_NATIVE_HEADSET -+#ifdef HAVE_BLUEZ_5_OFONO_HEADSET -+const char *default_headset_backend = "both"; -+#else - const char *default_headset_backend = "native"; -+#endif - #else - const char *default_headset_backend = "ofono"; - #endif -@@ -131,8 +135,10 @@ int pa__init(pa_module *m) { - headset_backend = HEADSET_BACKEND_NATIVE; - else if (pa_streq(headset_str, "auto")) - headset_backend = HEADSET_BACKEND_AUTO; -+ else if (pa_streq(headset_str, "both")) -+ headset_backend = HEADSET_BACKEND_BOTH; - else { -- pa_log("headset parameter must be either ofono, native or auto (found %s)", headset_str); -+ pa_log("headset parameter must be either ofono, native, auto or both (found %s)", headset_str); - goto fail; - } - diff -Nru pulseaudio-10.0/debian/patches/0505-bluetooth-bluez5-let-user-specify-a-default-profile-.patch pulseaudio-10.0/debian/patches/0505-bluetooth-bluez5-let-user-specify-a-default-profile-.patch --- pulseaudio-10.0/debian/patches/0505-bluetooth-bluez5-let-user-specify-a-default-profile-.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0505-bluetooth-bluez5-let-user-specify-a-default-profile-.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,102 +0,0 @@ -From c4f7b88b81042506fd1920c8daa5d80b4e715276 Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Sun, 1 Nov 2015 16:42:53 +0100 -Subject: [PATCH 505/507] bluetooth: bluez5: let user specify a default profile - to set - ---- - src/modules/bluetooth/module-bluez5-device.c | 32 ++++++++++++++++++++++++++++ - 1 file changed, 32 insertions(+) - -Index: pulseaudio/src/modules/bluetooth/module-bluez5-device.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/module-bluez5-device.c -+++ pulseaudio/src/modules/bluetooth/module-bluez5-device.c -@@ -58,6 +58,7 @@ PA_MODULE_DESCRIPTION("BlueZ 5 Bluetooth - PA_MODULE_VERSION(PACKAGE_VERSION); - PA_MODULE_LOAD_ONCE(false); - PA_MODULE_USAGE("path= " -+ "profile= " - "sco_sink= " - "sco_source= "); - -@@ -74,6 +75,7 @@ PA_MODULE_USAGE("path=profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT && (u->hsp.sco_sink && u->hsp.sco_source)) - static const char* const valid_modargs[] = { - "path", -+ "profile", - "sco_sink", - "sco_source", - NULL -@@ -158,6 +160,7 @@ struct userdata { - struct sbc_info sbc_info; - struct hsp_info hsp; - -+ char *default_profile; - bool transport_acquire_pending; - pa_io_event *stream_event; - }; -@@ -2098,6 +2101,7 @@ static int add_card(struct userdata *u) - pa_bluetooth_profile_t *p; - const char *uuid; - void *state; -+ const char *default_profile; - - pa_assert(u); - pa_assert(u->device); -@@ -2149,6 +2153,16 @@ static int add_card(struct userdata *u) - *p = PA_BLUETOOTH_PROFILE_OFF; - pa_hashmap_put(data.profiles, cp->name, cp); - -+ if ((default_profile = pa_modargs_get_value(u->modargs, "profile", NULL))) { -+ if (pa_hashmap_get(data.profiles, default_profile)) { -+ pa_card_set_profile(u->card, pa_hashmap_get(data.profiles, default_profile), false); -+ pa_log_debug("Using %s profile as default", default_profile); -+ u->default_profile = pa_xstrdup(default_profile); -+ } -+ else -+ pa_log_warn("Profile '%s' not valid or not supported by device.", default_profile); -+ } -+ - u->card = pa_card_new(u->core, &data); - pa_card_new_data_done(&data); - if (!u->card) { -@@ -2162,6 +2176,15 @@ static int add_card(struct userdata *u) - pa_card_put(u->card); - - p = PA_CARD_PROFILE_DATA(u->card->active_profile); -+ -+ if (*p != PA_BLUETOOTH_PROFILE_OFF && (!d->transports[*p] || -+ d->transports[*p]->state == PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)) { -+ pa_log_warn("Default profile not connected, selecting off profile"); -+ u->card->active_profile = pa_hashmap_get(u->card->profiles, "off"); -+ u->card->save_profile = false; -+ } -+ -+ p = PA_CARD_PROFILE_DATA(u->card->active_profile); - u->profile = *p; - - if (USE_SCO_OVER_PCM(u)) -@@ -2276,6 +2299,11 @@ static pa_hook_result_t transport_state_ - if (t->device == u->device) - handle_transport_state_change(u, t); - -+ /* For the case that we've currently the 'off' profile set we need to move -+ * on to a possible configured default profile. */ -+ if (u->profile == PA_BLUETOOTH_PROFILE_OFF && pa_bluetooth_device_any_transport_connected(u->device) && u->default_profile) -+ pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, u->default_profile), false); -+ - return PA_HOOK_OK; - } - -@@ -2528,6 +2556,10 @@ void pa__done(pa_module *m) { - - if (u->modargs) - pa_modargs_free(u->modargs); -+ -+ if (u->default_profile) -+ pa_xfree(u->default_profile); -+ - pa_xfree(u); - } - diff -Nru pulseaudio-10.0/debian/patches/0506-bluetooth-bluez5-prevent-SCO-sink-source-to-be-suspe.patch pulseaudio-10.0/debian/patches/0506-bluetooth-bluez5-prevent-SCO-sink-source-to-be-suspe.patch --- pulseaudio-10.0/debian/patches/0506-bluetooth-bluez5-prevent-SCO-sink-source-to-be-suspe.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0506-bluetooth-bluez5-prevent-SCO-sink-source-to-be-suspe.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,200 +0,0 @@ -From 0eec59e95eb6bb396aaa97fddcf71d10813a3b7d Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Sun, 1 Nov 2015 16:44:45 +0100 -Subject: [PATCH 506/507] bluetooth: bluez5: prevent SCO sink/source to be - suspended - -This only works in conjunction with changes to the droid module -as this will set the property according to the current profile -selected for the droid card. ---- - src/modules/bluetooth/module-bluez5-device.c | 83 ++++++++++++++++++++++++++ - src/modules/bluetooth/module-bluez5-discover.c | 13 ++++ - 2 files changed, 96 insertions(+) - -Index: pulseaudio/src/modules/bluetooth/module-bluez5-device.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/module-bluez5-device.c -+++ pulseaudio/src/modules/bluetooth/module-bluez5-device.c -@@ -73,6 +73,7 @@ PA_MODULE_USAGE("path=profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT && (u->hsp.sco_sink && u->hsp.sco_source)) -+ - static const char* const valid_modargs[] = { - "path", - "profile", -@@ -127,6 +128,9 @@ struct userdata { - pa_hook_slot *sink_state_changed_slot; - pa_hook_slot *source_state_changed_slot; - -+ pa_hook_slot *sco_sink_proplist_changed_slot; -+ bool prevent_suspend_transport; -+ - pa_bluetooth_discovery *discovery; - pa_bluetooth_device *device; - pa_bluetooth_transport *transport; -@@ -1043,6 +1047,69 @@ static int add_source(struct userdata *u - return 0; - } - -+#define HSP_PREVENT_SUSPEND_STR "bluetooth.hsp.prevent.suspend.transport" -+ -+/* Check and update prevent_suspend_transport value from sco sink proplist. -+ * -+ * Return < 0 if sink proplist doesn't contain HSP_PREVENT_SUSPEND_STR value, -+ * 1 if value is 'true' -+ * 0 if value is something else. */ -+static int check_proplist(struct userdata *u) { -+ int ret; -+ const char *str; -+ -+ pa_assert(u); -+ pa_assert(u->hsp.sco_sink); -+ -+ if ((str = pa_proplist_gets(u->hsp.sco_sink->proplist, HSP_PREVENT_SUSPEND_STR))) { -+ if (pa_streq(str, "true")) -+ ret = 1; -+ else -+ ret = 0; -+ } else -+ ret = -1; -+ -+ u->prevent_suspend_transport = ret == 1; -+ -+ pa_log_debug("Set %s %s (ret %d)", HSP_PREVENT_SUSPEND_STR, u->prevent_suspend_transport ? "true" : "false", ret); -+ -+ return ret; -+} -+ -+/* There are cases where keeping the transport running even when sco sink and source are suspended -+ * is needed. -+ * To work with these cases, check sco.sink for bluetooth.hsp.prevent.suspend.transport value, and -+ * when set to true prevent closing the transport when sink suspends. -+ * Also, if the sink&source are suspended when sco-sink suspend.transport value changes to true, -+ * bring sco transport up. When suspend.transport value changes to false while sink&source are suspended, -+ * tear down the transport. */ -+static pa_hook_result_t update_allow_release_cb(pa_core *c, pa_sink *s, struct userdata *u) { -+ pa_assert(u); -+ pa_assert(s); -+ -+ if (!u->hsp.sco_sink || u->hsp.sco_sink != s) -+ return PA_HOOK_OK; -+ -+ if (check_proplist(u) < 0) -+ return PA_HOOK_OK; -+ -+ if (!USE_SCO_OVER_PCM(u)) { -+ pa_log_debug("SCO sink not available."); -+ return PA_HOOK_OK; -+ } -+ -+ if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->hsp.sco_sink)) && -+ !PA_SOURCE_IS_OPENED(pa_source_get_state(u->hsp.sco_source))) { -+ -+ pa_log_debug("Resuming SCO sink"); -+ -+ /* Clear all suspend bits, effectively resuming SCO sink for a while. */ -+ pa_sink_suspend(s, false, PA_SUSPEND_ALL); -+ } -+ -+ return PA_HOOK_OK; -+} -+ - /* Run from IO thread */ - static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { - struct userdata *u = PA_SINK(o)->userdata; -@@ -2050,6 +2117,13 @@ static int set_profile_cb(pa_card *c, pa - - if (!d->transports[*p] || d->transports[*p]->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) { - pa_log_warn("Refused to switch profile to %s: Not connected", new_profile->name); -+ -+ /* For the rare case that we were requested to switch to A2DP -+ * but that failed (due the profile got disconnected) we switch -+ * to off */ -+ if (*p == PA_BLUETOOTH_PROFILE_A2DP_SINK) -+ pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); -+ - return -PA_ERR_IO; - } - } -@@ -2192,6 +2266,7 @@ static int add_card(struct userdata *u) - - pa_log_debug("Created card (current profile %s)", - pa_bluetooth_profile_to_string(u->profile)); -+ - return 0; - } - -@@ -2468,6 +2543,10 @@ 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); - -+ u->sco_sink_proplist_changed_slot = -+ pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], -+ PA_HOOK_NORMAL, (pa_hook_cb_t) update_allow_release_cb, u); -+ - if (add_card(u) < 0) - goto fail; - -@@ -2533,6 +2612,10 @@ void pa__done(pa_module *m) { - - if (u->source_state_changed_slot) - pa_hook_slot_free(u->source_state_changed_slot); -+ -+ if (u->sco_sink_proplist_changed_slot) -+ pa_hook_slot_free(u->sco_sink_proplist_changed_slot); -+ - if (u->sbc_info.buffer) - pa_xfree(u->sbc_info.buffer); - -Index: pulseaudio/src/modules/bluetooth/module-bluez5-discover.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/module-bluez5-discover.c -+++ pulseaudio/src/modules/bluetooth/module-bluez5-discover.c -@@ -38,12 +38,14 @@ PA_MODULE_VERSION(PACKAGE_VERSION); - PA_MODULE_LOAD_ONCE(true); - PA_MODULE_USAGE( - "headset=ofono|native|auto|both " -+ "profile= " - "sco_sink= " - "sco_source= " - ); - - static const char* const valid_modargs[] = { - "headset", -+ "profile", - "sco_sink", - "sco_source", - NULL -@@ -78,6 +80,15 @@ static pa_hook_result_t device_connectio - pa_module *m; - char *args = pa_sprintf_malloc("path=%s", d->path); - -+ if (pa_modargs_get_value(u->modargs, "profile", NULL)) { -+ char *profile; -+ -+ profile = pa_sprintf_malloc("%s profile=\"%s\"", args, -+ pa_modargs_get_value(u->modargs, "profile", NULL)); -+ pa_xfree(args); -+ args = profile; -+ } -+ - if (pa_modargs_get_value(u->modargs, "sco_sink", NULL) && - pa_modargs_get_value(u->modargs, "sco_source", NULL)) { - char *tmp; -@@ -88,6 +99,7 @@ static pa_hook_result_t device_connectio - pa_xfree(args); - args = tmp; - } -+ - pa_log_debug("Loading module-bluez5-device %s", args); - m = pa_module_load(u->module->core, "module-bluez5-device", args); - pa_xfree(args); -@@ -183,5 +195,6 @@ void pa__done(pa_module *m) { - - if (u->modargs) - pa_modargs_free(u->modargs); -+ - pa_xfree(u); - } diff -Nru pulseaudio-10.0/debian/patches/0507-bluetooth-bluez5-drop-save-restore-of-SCO-sink-sourc.patch pulseaudio-10.0/debian/patches/0507-bluetooth-bluez5-drop-save-restore-of-SCO-sink-sourc.patch --- pulseaudio-10.0/debian/patches/0507-bluetooth-bluez5-drop-save-restore-of-SCO-sink-sourc.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0507-bluetooth-bluez5-drop-save-restore-of-SCO-sink-sourc.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,68 +0,0 @@ -From 0603a31eb0a5d6a0247b1f754501230f97749aca Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Sun, 1 Nov 2015 16:51:01 +0100 -Subject: [PATCH 507/507] bluetooth: bluez5: drop save/restore of SCO - sink/source volume set callbacks - -Still needs to be verified that this can be dropped. ---- - src/modules/bluetooth/module-bluez5-device.c | 24 ------------------------ - 1 file changed, 24 deletions(-) - -Index: pulseaudio/src/modules/bluetooth/module-bluez5-device.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/module-bluez5-device.c -+++ pulseaudio/src/modules/bluetooth/module-bluez5-device.c -@@ -109,9 +109,7 @@ typedef struct sbc_info { - - struct hsp_info { - pa_sink *sco_sink; -- void (*sco_sink_set_volume)(pa_sink *s); - pa_source *sco_source; -- void (*sco_source_set_volume)(pa_source *s); - }; - - struct userdata { -@@ -2081,22 +2079,6 @@ static pa_card_profile *create_card_prof - return cp; - } - --static void save_sco_volume_callbacks(struct userdata *u) { -- pa_assert(u); -- pa_assert(USE_SCO_OVER_PCM(u)); -- -- u->hsp.sco_sink_set_volume = u->hsp.sco_sink->set_volume; -- u->hsp.sco_source_set_volume = u->hsp.sco_source->set_volume; --} -- --static void restore_sco_volume_callbacks(struct userdata *u) { -- pa_assert(u); -- pa_assert(USE_SCO_OVER_PCM(u)); -- -- pa_sink_set_set_volume_callback(u->hsp.sco_sink, u->hsp.sco_sink_set_volume); -- pa_source_set_set_volume_callback(u->hsp.sco_source, u->hsp.sco_source_set_volume); --} -- - /* Run from main thread */ - static int set_profile_cb(pa_card *c, pa_card_profile *new_profile) { - struct userdata *u; -@@ -2261,9 +2243,6 @@ static int add_card(struct userdata *u) - p = PA_CARD_PROFILE_DATA(u->card->active_profile); - u->profile = *p; - -- if (USE_SCO_OVER_PCM(u)) -- save_sco_volume_callbacks(u); -- - pa_log_debug("Created card (current profile %s)", - pa_bluetooth_profile_to_string(u->profile)); - -@@ -2622,9 +2601,6 @@ void pa__done(pa_module *m) { - if (u->sbc_info.sbc_initialized) - sbc_finish(&u->sbc_info.sbc); - -- if (USE_SCO_OVER_PCM(u)) -- restore_sco_volume_callbacks(u); -- - if (u->msg) - pa_xfree(u->msg); - diff -Nru pulseaudio-10.0/debian/patches/0508-bluetooth-bluez5-add-guards-to-prevent-HFP-and-HSP-c.patch pulseaudio-10.0/debian/patches/0508-bluetooth-bluez5-add-guards-to-prevent-HFP-and-HSP-c.patch --- pulseaudio-10.0/debian/patches/0508-bluetooth-bluez5-add-guards-to-prevent-HFP-and-HSP-c.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0508-bluetooth-bluez5-add-guards-to-prevent-HFP-and-HSP-c.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,46 +0,0 @@ -From d654af2e527d21a60b414c6649988454cf01068d Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Thu, 26 Nov 2015 07:30:51 +0100 -Subject: [PATCH] bluetooth: bluez5: add guards to prevent HFP and HSP - connected together - ---- - src/modules/bluetooth/backend-native.c | 18 +++++++++++++++++- - 1 file changed, 17 insertions(+), 1 deletion(-) - -Index: pulseaudio/src/modules/bluetooth/backend-native.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/backend-native.c -+++ pulseaudio/src/modules/bluetooth/backend-native.c -@@ -342,6 +342,23 @@ static DBusMessage *profile_new_connecti - goto fail; - } - -+ p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT; -+ /* We might have support for HSP/HFP at the same time and therefor -+ * need to check if the other side is already setup or not. If it -+ * is we fail here as having both active at the same time is not -+ * supported */ -+ if (d->transports[p]) { -+ pa_log_error("Transport for profile %s already registered.", -+ pa_bluetooth_profile_to_string(p)); -+ goto fail; -+ } -+ -+ if (pa_hashmap_get(d->uuids, PA_BLUETOOTH_UUID_HFP_HF)) { -+ pa_log_error("Remote device %s supports HFP as well which is preferred over HSP. Aborting.", -+ d->address); -+ goto fail; -+ } -+ - pa_assert_se(dbus_message_iter_next(&arg_i)); - - pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_UNIX_FD); -@@ -351,7 +368,6 @@ static DBusMessage *profile_new_connecti - - sender = dbus_message_get_sender(m); - -- p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT; - pathfd = pa_sprintf_malloc ("%s/fd%d", path, fd); - t = pa_bluetooth_transport_new(d, sender, pathfd, p, NULL, 0); - pa_xfree(pathfd); diff -Nru pulseaudio-10.0/debian/patches/0509-bluetooth-bluez5-don-t-reactivate-default-profile-wh.patch pulseaudio-10.0/debian/patches/0509-bluetooth-bluez5-don-t-reactivate-default-profile-wh.patch --- pulseaudio-10.0/debian/patches/0509-bluetooth-bluez5-don-t-reactivate-default-profile-wh.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0509-bluetooth-bluez5-don-t-reactivate-default-profile-wh.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,26 +0,0 @@ -From f8213ddec83cd0780ae77ad423581d9a65857d1f Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Thu, 26 Nov 2015 08:54:20 +0100 -Subject: [PATCH] bluetooth: bluez5: don't reactivate default profile when off - is active - ---- - src/modules/bluetooth/module-bluez5-device.c | 5 ----- - 1 file changed, 5 deletions(-) - -Index: pulseaudio/src/modules/bluetooth/module-bluez5-device.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/module-bluez5-device.c -+++ pulseaudio/src/modules/bluetooth/module-bluez5-device.c -@@ -2353,11 +2353,6 @@ static pa_hook_result_t transport_state_ - if (t->device == u->device) - handle_transport_state_change(u, t); - -- /* For the case that we've currently the 'off' profile set we need to move -- * on to a possible configured default profile. */ -- if (u->profile == PA_BLUETOOTH_PROFILE_OFF && pa_bluetooth_device_any_transport_connected(u->device) && u->default_profile) -- pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, u->default_profile), false); -- - return PA_HOOK_OK; - } - diff -Nru pulseaudio-10.0/debian/patches/0510-Further-fixes-for-HFP-A2DP-with-BlueZ-5.x.patch pulseaudio-10.0/debian/patches/0510-Further-fixes-for-HFP-A2DP-with-BlueZ-5.x.patch --- pulseaudio-10.0/debian/patches/0510-Further-fixes-for-HFP-A2DP-with-BlueZ-5.x.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0510-Further-fixes-for-HFP-A2DP-with-BlueZ-5.x.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,153 +0,0 @@ -From bf107ef8e9bfda2f2609dfedd717726f387ae8a6 Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Mon, 30 Nov 2015 13:57:44 +0100 -Subject: [PATCH] Further fixes for HFP/A2DP with BlueZ 5.x - ---- - src/modules/bluetooth/backend-native.c | 3 +- - src/modules/bluetooth/bluez5-util.c | 6 ++++ - src/modules/bluetooth/bluez5-util.h | 2 ++ - src/modules/bluetooth/module-bluez5-device.c | 44 +++++++++++++++++++++++----- - 4 files changed, 46 insertions(+), 9 deletions(-) - -Index: pulseaudio/src/modules/bluetooth/backend-native.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/backend-native.c -+++ pulseaudio/src/modules/bluetooth/backend-native.c -@@ -353,7 +353,8 @@ static DBusMessage *profile_new_connecti - goto fail; - } - -- if (pa_hashmap_get(d->uuids, PA_BLUETOOTH_UUID_HFP_HF)) { -+ if (pa_hashmap_get(d->uuids, PA_BLUETOOTH_UUID_HFP_HF) && -+ pa_bluetooth_device_is_transport_connected(d, PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT)) { - pa_log_error("Remote device %s supports HFP as well which is preferred over HSP. Aborting.", - d->address); - goto fail; -Index: pulseaudio/src/modules/bluetooth/bluez5-util.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/bluez5-util.c -+++ pulseaudio/src/modules/bluetooth/bluez5-util.c -@@ -411,6 +411,12 @@ bool pa_bluetooth_device_any_transport_c - return false; - } - -+bool pa_bluetooth_device_is_transport_connected(const pa_bluetooth_device *d, pa_bluetooth_profile_t profile) { -+ pa_assert(d); -+ -+ return d->transports[profile] && d->transports[profile]->state == PA_BLUETOOTH_TRANSPORT_STATE_IDLE; -+} -+ - static int transport_state_from_string(const char* value, pa_bluetooth_transport_state_t *state) { - pa_assert(value); - pa_assert(state); -Index: pulseaudio/src/modules/bluetooth/bluez5-util.h -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/bluez5-util.h -+++ pulseaudio/src/modules/bluetooth/bluez5-util.h -@@ -146,6 +146,8 @@ void pa_bluetooth_transport_unlink(pa_bl - void pa_bluetooth_transport_free(pa_bluetooth_transport *t); - - bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d); -+bool pa_bluetooth_device_is_transport_connected(const pa_bluetooth_device *d, pa_bluetooth_profile_t profile); -+ - - 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); -Index: pulseaudio/src/modules/bluetooth/module-bluez5-device.c -=================================================================== ---- pulseaudio.orig/src/modules/bluetooth/module-bluez5-device.c -+++ pulseaudio/src/modules/bluetooth/module-bluez5-device.c -@@ -165,6 +165,8 @@ struct userdata { - char *default_profile; - bool transport_acquire_pending; - pa_io_event *stream_event; -+ -+ pa_defer_event *set_default_profile_event; - }; - - typedef enum pa_bluetooth_form_factor { -@@ -2099,13 +2101,6 @@ static int set_profile_cb(pa_card *c, pa - - if (!d->transports[*p] || d->transports[*p]->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) { - pa_log_warn("Refused to switch profile to %s: Not connected", new_profile->name); -- -- /* For the rare case that we were requested to switch to A2DP -- * but that failed (due the profile got disconnected) we switch -- * to off */ -- if (*p == PA_BLUETOOTH_PROFILE_A2DP_SINK) -- pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); -- - return -PA_ERR_IO; - } - } -@@ -2239,6 +2234,10 @@ static int add_card(struct userdata *u) - u->card->active_profile = pa_hashmap_get(u->card->profiles, "off"); - u->card->save_profile = false; - } -+ else { -+ pa_xfree(u->default_profile); -+ u->default_profile = NULL; -+ } - - p = PA_CARD_PROFILE_DATA(u->card->active_profile); - u->profile = *p; -@@ -2338,6 +2337,23 @@ static pa_hook_result_t device_connectio - return PA_HOOK_OK; - } - -+static void set_default_profile_cb(pa_mainloop_api *api, pa_defer_event *e, void *user_data) { -+ struct userdata *u = user_data; -+ -+ pa_assert(u); -+ -+ if (!u->default_profile) -+ return; -+ -+ pa_log_debug("Setting default profile %s", u->default_profile); -+ -+ pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, u->default_profile), false) >= 0); -+ pa_xfree(u->default_profile); -+ u->default_profile = NULL; -+ -+ api->defer_enable(e, 0); -+} -+ - /* 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); -@@ -2347,7 +2363,13 @@ static pa_hook_result_t transport_state_ - pa_bluetooth_profile_to_string(t->profile), - pa_bluetooth_transport_state_to_string(t->state)); - -- if (t == u->transport && t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) -+ if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT && t->state == PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) -+ pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "headset_head_unit"), false) >= 0); -+ -+ else if (u->profile == PA_BLUETOOTH_PROFILE_OFF && pa_bluetooth_device_any_transport_connected(u->device) && u->default_profile) -+ u->core->mainloop->defer_enable(u->set_default_profile_event, 1); -+ -+ else if (t == u->transport && t->state <= PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) - pa_assert_se(pa_card_set_profile(u->card, pa_hashmap_get(u->card->profiles, "off"), false) >= 0); - - if (t->device == u->device) -@@ -2527,6 +2549,9 @@ int pa__init(pa_module* m) { - if (!(u->msg = pa_msgobject_new(bluetooth_msg))) - goto fail; - -+ u->set_default_profile_event = u->core->mainloop->defer_new(u->core->mainloop, set_default_profile_cb, u); -+ u->core->mainloop->defer_enable(u->set_default_profile_event, 0); -+ - u->msg->parent.process_msg = device_process_msg; - u->msg->card = u->card; - -@@ -2614,6 +2639,9 @@ void pa__done(pa_module *m) { - if (u->default_profile) - pa_xfree(u->default_profile); - -+ if (u->set_default_profile_event) -+ u->core->mainloop->defer_free(u->set_default_profile_event); -+ - pa_xfree(u); - } - diff -Nru pulseaudio-10.0/debian/patches/0600-droid-sync-with-upstream-for-Android-5-support-and-b.patch pulseaudio-10.0/debian/patches/0600-droid-sync-with-upstream-for-Android-5-support-and-b.patch --- pulseaudio-10.0/debian/patches/0600-droid-sync-with-upstream-for-Android-5-support-and-b.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0600-droid-sync-with-upstream-for-Android-5-support-and-b.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,5249 +0,0 @@ -From f821497cf8d1031c66ae3a50759045023b43a615 Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Thu, 14 Jan 2016 09:49:37 +0100 -Subject: [PATCH] droid: sync with upstream for Android 5 support and building - for multiple Android versions - -Origin: upstream, https://github.com/mer-hybris/pulseaudio-modules-droid -Forwarded: not-needed ---- - configure.ac | 20 + - src/Makefile.am | 136 +++- - src/modules/droid/droid-sink.c | 391 +++++---- - src/modules/droid/droid-source.c | 307 +++---- - src/modules/droid/droid-source.h | 3 + - src/modules/droid/droid-util-42.h | 5 +- - src/modules/droid/droid-util-44.h | 358 +++++++++ - src/modules/droid/droid-util-51.h | 412 ++++++++++ - src/modules/droid/droid-util.c | 1248 +++++++++++++++++++++++------ - src/modules/droid/droid-util.h | 117 ++- - src/modules/droid/module-droid-card.c | 585 +++++++++++--- - src/modules/droid/module-droid-discover.c | 95 +++ - src/modules/droid/module-droid-sink.c | 27 +- - src/modules/droid/module-droid-source.c | 12 +- - src/modules/module-device-restore.c | 1 + - 15 files changed, 2925 insertions(+), 792 deletions(-) - create mode 100644 src/modules/droid/droid-util-44.h - create mode 100644 src/modules/droid/droid-util-51.h - create mode 100644 src/modules/droid/module-droid-discover.c - -Index: pulseaudio/configure.ac -=================================================================== ---- pulseaudio.orig/configure.ac -+++ pulseaudio/configure.ac -@@ -858,6 +858,23 @@ AS_IF([test "x$enable_android_hal" = "xy - AM_CONDITIONAL([HAVE_ANDROID], [test "x$HAVE_ANDROID" = "x1"]) - AS_IF([test "x$HAVE_ANDROID" = "x1"], AC_DEFINE([HAVE_ANDROID], 1, [Have Android Audio HAL?])) - -+AS_IF([test "x$HAVE_ANDROID" = "x1"], -+ [PKG_CHECK_MODULES(LIBANDROID_PROPERTIES, [libandroid-properties], HAVE_ANDROID_PROPERTIES=1, HAVE_ANDROID_PROPERTIES=0)], -+ HAVE_ANDROID_PROPERTIES=0) -+AS_IF([test "x$HAVE_ANDROID" = "x1"], -+ [AS_IF([test "x$HAVE_ANDROID_PROPERTIES" = "x0"],[AC_MSG_ERROR([*** libandroid-properties not found])])], -+ []) -+ -+AS_IF([test "x$HAVE_ANDROID" = "x1"], -+ [PKG_CHECK_MODULES(ANDROID_HEADERS_19, [android-headers-19], HAVE_ANDROID_HEADERS_19=1, HAVE_ANDROID_HEADERS_19=0)], -+ HAVE_ANDROID_HEADERS_19=0) -+AM_CONDITIONAL([HAVE_ANDROID_HEADERS_19], [test "x$HAVE_ANDROID_HEADERS_19" = "x1"]) -+ -+AS_IF([test "x$HAVE_ANDROID" = "x1"], -+ [PKG_CHECK_MODULES(ANDROID_HEADERS_22, [android-headers-22], HAVE_ANDROID_HEADERS_22=1, HAVE_ANDROID_HEADERS_22=0)], -+ HAVE_ANDROID_HEADERS_22=0) -+AM_CONDITIONAL([HAVE_ANDROID_HEADERS_22], [test "x$HAVE_ANDROID_HEADERS_22" = "x1"]) -+ - #### EsounD support (optional) #### - - AC_ARG_ENABLE([esound], -@@ -1571,6 +1588,8 @@ AS_IF([test "x$HAVE_OSS_OUTPUT" = "x1"], - AS_IF([test "x$HAVE_OSS_WRAPPER" = "x1"], ENABLE_OSS_WRAPPER=yes, ENABLE_OSS_WRAPPER=no) - AS_IF([test "x$HAVE_ALSA" = "x1"], ENABLE_ALSA=yes, ENABLE_ALSA=no) - AS_IF([test "x$HAVE_ANDROID" = "x1"], ENABLE_ANDROID=yes, ENABLE_ANDROID=no) -+AS_IF([test "x$HAVE_ANDROID_HEADERS_19" = "x1"], ENABLE_ANDROID_19=yes, ENABLE_ANDROID_19=no) -+AS_IF([test "x$HAVE_ANDROID_HEADERS_22" = "x1"], ENABLE_ANDROID_22=yes, ENABLE_ANDROID_22=no) - AS_IF([test "x$HAVE_COREAUDIO" = "x1"], ENABLE_COREAUDIO=yes, ENABLE_COREAUDIO=no) - AS_IF([test "x$HAVE_SOLARIS" = "x1"], ENABLE_SOLARIS=yes, ENABLE_SOLARIS=no) - AS_IF([test "x$HAVE_WAVEOUT" = "x1"], ENABLE_WAVEOUT=yes, ENABLE_WAVEOUT=no) -@@ -1635,6 +1654,8 @@ echo " - Enable EsounD: ${ENABLE_ESOUND} - Enable Alsa: ${ENABLE_ALSA} - Enable Android Audio HAL: ${ENABLE_ANDROID} -+ API Level 19: ${ENABLE_ANDROID_19} -+ API Level 22: ${ENABLE_ANDROID_22} - Enable CoreAudio: ${ENABLE_COREAUDIO} - Enable Solaris: ${ENABLE_SOLARIS} - Enable WaveOut: ${ENABLE_WAVEOUT} -Index: pulseaudio/src/Makefile.am -=================================================================== ---- pulseaudio.orig/src/Makefile.am -+++ pulseaudio/src/Makefile.am -@@ -1308,12 +1308,25 @@ modlibexec_LTLIBRARIES += \ - - if HAVE_ANDROID - modlibexec_LTLIBRARIES += \ -- libdroid-util.la \ -- libdroid-sink.la \ -- libdroid-source.la \ -- module-droid-sink.la \ -- module-droid-source.la \ -- module-droid-card.la -+ module-droid-discover.la -+if HAVE_ANDROID_HEADERS_19 -+modlibexec_LTLIBRARIES += \ -+ libdroid-util-19.la \ -+ libdroid-sink-19.la \ -+ libdroid-source-19.la \ -+ module-droid-sink-19.la \ -+ module-droid-source-19.la \ -+ module-droid-card-19.la -+endif -+if HAVE_ANDROID_HEADERS_22 -+modlibexec_LTLIBRARIES += \ -+ libdroid-util-22.la \ -+ libdroid-sink-22.la \ -+ libdroid-source-22.la \ -+ module-droid-sink-22.la \ -+ module-droid-source-22.la \ -+ module-droid-card-22.la -+endif - endif - - dist_alsaprofilesets_DATA = \ -@@ -1583,9 +1596,19 @@ SYMDEF_FILES = \ - - if HAVE_ANDROID - SYMDEF_FILES += \ -- module-droid-sink-symdef.h \ -- module-droid-source-symdef.h \ -- module-droid-card-symdef.h -+ module-droid-discover-symdef.h -+if HAVE_ANDROID_HEADERS_19 -+SYMDEF_FILES += \ -+ module-droid-sink-19-symdef.h \ -+ module-droid-source-19-symdef.h \ -+ module-droid-card-19-symdef.h -+endif -+if HAVE_ANDROID_HEADERS_22 -+SYMDEF_FILES += \ -+ module-droid-sink-22-symdef.h \ -+ module-droid-source-22-symdef.h \ -+ module-droid-card-22-symdef.h -+endif - endif - - if HAVE_ESOUND -@@ -1898,42 +1921,86 @@ libalsa_util_la_CFLAGS += $(DBUS_CFLAGS) - endif - - if HAVE_ANDROID --libdroid_util_la_SOURCES = modules/droid/droid-util.c modules/droid/droid-util.h --libdroid_util_la_LDFLAGS = -avoid-version --libdroid_util_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) --libdroid_util_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS) -- --libdroid_sink_la_SOURCES = modules/droid/droid-sink.c modules/droid/droid-sink.h --libdroid_sink_la_LDFLAGS = -avoid-version --libdroid_sink_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la --libdroid_sink_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS) -- --libdroid_source_la_SOURCES = modules/droid/droid-source.c modules/droid/droid-source.h --libdroid_source_la_LDFLAGS = -avoid-version --libdroid_source_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la --libdroid_source_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS) -- --module_droid_sink_la_SOURCES = modules/droid/module-droid-sink.c --module_droid_sink_la_LDFLAGS = $(MODULE_LDFLAGS) --module_droid_sink_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la libdroid-sink.la --module_droid_sink_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS) -- --module_droid_source_la_SOURCES = modules/droid/module-droid-source.c --module_droid_source_la_LDFLAGS = $(MODULE_LDFLAGS) --module_droid_source_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la libdroid-source.la --module_droid_source_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS) -- --module_droid_card_la_SOURCES = modules/droid/module-droid-card.c --module_droid_card_la_LDFLAGS = $(MODULE_LDFLAGS) --module_droid_card_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util.la libdroid-sink.la libdroid-source.la --module_droid_card_la_CFLAGS = $(AM_CFLAGS) $(LIBHARDWARE_CFLAGS) -+module_droid_discover_la_SOURCES = modules/droid/module-droid-discover.c -+module_droid_discover_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_droid_discover_la_LIBADD = $(MODULE_LIBADD) $(LIBANDROID_PROPERTIES_LIBS) -+module_droid_discover_la_CFLAGS = $(AM_CFLAGS) $(LIBANDROID_PROPERTIES_CFLAGS) -+ -+if HAVE_ANDROID_HEADERS_19 -+libdroid_util_19_la_SOURCES = modules/droid/droid-util.c modules/droid/droid-util.h -+libdroid_util_19_la_LDFLAGS = -avoid-version -+libdroid_util_19_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) -+libdroid_util_19_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_19_CFLAGS) -+ -+libdroid_sink_19_la_SOURCES = modules/droid/droid-sink.c modules/droid/droid-sink.h -+libdroid_sink_19_la_LDFLAGS = -avoid-version -+libdroid_sink_19_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-19.la -+libdroid_sink_19_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_19_CFLAGS) -+ -+libdroid_source_19_la_SOURCES = modules/droid/droid-source.c modules/droid/droid-source.h -+libdroid_source_19_la_LDFLAGS = -avoid-version -+libdroid_source_19_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-19.la -+libdroid_source_19_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_19_CFLAGS) -+ -+module_droid_sink_19_la_SOURCES = modules/droid/module-droid-sink.c -+module_droid_sink_19_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_droid_sink_19_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-19.la libdroid-sink-19.la -+module_droid_sink_19_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_19_CFLAGS) -+ -+module_droid_source_19_la_SOURCES = modules/droid/module-droid-source.c -+module_droid_source_19_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_droid_source_19_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-19.la libdroid-source-19.la -+module_droid_source_19_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_19_CFLAGS) -+ -+module_droid_card_19_la_SOURCES = modules/droid/module-droid-card.c -+module_droid_card_19_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_droid_card_19_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-19.la libdroid-sink-19.la libdroid-source-19.la -+module_droid_card_19_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_19_CFLAGS) - - if HAVE_UDEV --module_droid_card_la_SOURCES += modules/droid/droid-extcon.c modules/droid/droid-extcon.h --module_droid_card_la_LIBADD += $(UDEV_LIBS) --module_droid_card_la_CFLAGS += $(UDEV_CFLAGS) --endif -+module_droid_card_19_la_SOURCES += modules/droid/droid-extcon.c modules/droid/droid-extcon.h -+module_droid_card_19_la_LIBADD += $(UDEV_LIBS) -+module_droid_card_19_la_CFLAGS += $(UDEV_CFLAGS) -+endif -+endif # HAVE_ANDROID_HEADERS_19 -+if HAVE_ANDROID_HEADERS_22 -+libdroid_util_22_la_SOURCES = modules/droid/droid-util.c modules/droid/droid-util.h -+libdroid_util_22_la_LDFLAGS = -avoid-version -+libdroid_util_22_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) -+libdroid_util_22_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_22_CFLAGS) -+ -+libdroid_sink_22_la_SOURCES = modules/droid/droid-sink.c modules/droid/droid-sink.h -+libdroid_sink_22_la_LDFLAGS = -avoid-version -+libdroid_sink_22_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-22.la -+libdroid_sink_22_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_22_CFLAGS) -+ -+libdroid_source_22_la_SOURCES = modules/droid/droid-source.c modules/droid/droid-source.h -+libdroid_source_22_la_LDFLAGS = -avoid-version -+libdroid_source_22_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-22.la -+libdroid_source_22_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_22_CFLAGS) -+ -+module_droid_sink_22_la_SOURCES = modules/droid/module-droid-sink.c -+module_droid_sink_22_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_droid_sink_22_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-22.la libdroid-sink-22.la -+module_droid_sink_22_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_22_CFLAGS) -+ -+module_droid_source_22_la_SOURCES = modules/droid/module-droid-source.c -+module_droid_source_22_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_droid_source_22_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-22.la libdroid-source-22.la -+module_droid_source_22_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_22_CFLAGS) -+ -+module_droid_card_22_la_SOURCES = modules/droid/module-droid-card.c -+module_droid_card_22_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_droid_card_22_la_LIBADD = $(MODULE_LIBADD) $(LIBHARDWARE_LIBS) libdroid-util-22.la libdroid-sink-22.la libdroid-source-22.la -+module_droid_card_22_la_CFLAGS = $(AM_CFLAGS) $(ANDROID_HEADERS_22_CFLAGS) -+ -+if HAVE_UDEV -+module_droid_card_22_la_SOURCES += modules/droid/droid-extcon.c modules/droid/droid-extcon.h -+module_droid_card_22_la_LIBADD += $(UDEV_LIBS) -+module_droid_card_22_la_CFLAGS += $(UDEV_CFLAGS) - endif -+endif # HAVE_ANDROID_HEADERS_22 -+endif # HAVE_ANDROID - - module_alsa_sink_la_SOURCES = modules/alsa/module-alsa-sink.c - module_alsa_sink_la_LDFLAGS = $(MODULE_LDFLAGS) -Index: pulseaudio/src/modules/droid/droid-sink.c -=================================================================== ---- pulseaudio.orig/src/modules/droid/droid-sink.c -+++ pulseaudio/src/modules/droid/droid-sink.c -@@ -1,6 +1,5 @@ - /* - * Copyright (C) 2013 Jolla Ltd. -- * Copyright (C) 2010 Nokia Corporation. - * - * Contact: Juho Hämäläinen - * -@@ -76,13 +75,14 @@ struct userdata { - - pa_memblockq *memblockq; - pa_memchunk silence; -- size_t buffer_count; - size_t buffer_size; -- pa_usec_t buffer_latency; -- pa_usec_t timestamp; -+ pa_usec_t buffer_time; -+ pa_usec_t write_time; -+ pa_usec_t write_threshold; - - audio_devices_t primary_devices; - audio_devices_t extra_devices; -+ pa_hashmap *extra_devices_map; - - bool use_hw_volume; - bool use_voice_volume; -@@ -101,16 +101,15 @@ struct userdata { - - pa_droid_card_data *card_data; - pa_droid_hw_module *hw_module; -- struct audio_stream_out *stream_out; -- -- char *sco_fake_sink_name; -- struct pa_sink *sco_fake_sink; -+ pa_droid_stream *stream; - }; - - enum { - SINK_MESSAGE_DO_ROUTING = PA_SINK_MESSAGE_MAX, - }; - -+#define PULSEAUDIO_VERSION_MAJOR 6 -+ - #define DEFAULT_MODULE_ID "primary" - - /* sink properties */ -@@ -138,6 +137,7 @@ typedef struct droid_parameter_mapping { - #define DEFAULT_SCO_FAKE_SINK "sink.fake.sco" - #define HSP_PREVENT_SUSPEND_STR "bluetooth.hsp.prevent.suspend.transport" - -+static void parameter_free(droid_parameter_mapping *m); - static void userdata_free(struct userdata *u); - static void set_voice_volume_from_input(struct userdata *u, pa_sink_input *i); - static struct pa_sink *pa_sco_fake_sink_discover(pa_core *core, const char *sink_name); -@@ -149,67 +149,77 @@ static void set_primary_devices(struct u - u->primary_devices = devices; - } - --static void add_extra_devices(struct userdata *u, audio_devices_t devices) { -- pa_assert(u); -- pa_assert(devices); -+static bool add_extra_devices(struct userdata *u, audio_devices_t devices) { -+ void *value; -+ uint32_t count; -+ bool need_update = false; - -- u->extra_devices |= devices; --} -- --static void remove_extra_devices(struct userdata *u, audio_devices_t devices) { - pa_assert(u); -+ pa_assert(u->extra_devices_map); - pa_assert(devices); - -- u->extra_devices &= ~devices; --} -+ if ((value = pa_hashmap_get(u->extra_devices_map, PA_UINT_TO_PTR(devices)))) { -+ count = PA_PTR_TO_UINT(value); -+ count++; -+ pa_hashmap_remove(u->extra_devices_map, PA_UINT_TO_PTR(devices)); -+ pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(count)); - --static void parameter_free(droid_parameter_mapping *m) { -- pa_assert(m); -+ /* added extra device already exists in hashmap, so no need to update route. */ -+ need_update = false; -+ } else { -+ pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(1)); -+ u->extra_devices |= devices; -+ need_update = true; -+ } - -- pa_xfree(m->key); -- pa_xfree(m->value); -- pa_xfree(m); -+ return need_update; - } - --static void set_fake_sco_sink_transport_property(struct userdata *u, const char *value) { -- pa_proplist *pl; -+static bool remove_extra_devices(struct userdata *u, audio_devices_t devices) { -+ void *value; -+ uint32_t count; -+ bool need_update = false; - - pa_assert(u); -- pa_assert(value); -- pa_assert(u->sco_fake_sink); -+ pa_assert(u->extra_devices_map); -+ pa_assert(devices); - -- pl = pa_proplist_new(); -- pa_proplist_sets(pl, HSP_PREVENT_SUSPEND_STR, value); -- pa_sink_update_proplist(u->sco_fake_sink, PA_UPDATE_REPLACE, pl); -- pa_proplist_free(pl); -+ if ((value = pa_hashmap_get(u->extra_devices_map, PA_UINT_TO_PTR(devices)))) { -+ pa_hashmap_remove(u->extra_devices_map, PA_UINT_TO_PTR(devices)); -+ count = PA_PTR_TO_UINT(value); -+ count--; -+ if (count == 0) { -+ u->extra_devices &= ~devices; -+ need_update = true; -+ } else { -+ /* added extra devices still exists in hashmap, so no need to update route. */ -+ pa_hashmap_put(u->extra_devices_map, PA_UINT_TO_PTR(devices), PA_UINT_TO_PTR(count)); -+ need_update = false; -+ } -+ } -+ -+ return need_update; - } - - /* Called from main context during voice calls, and from IO context during media operation. */ --static bool do_routing(struct userdata *u) { -+static void do_routing(struct userdata *u) { - audio_devices_t routing; -- char tmp[32]; - - pa_assert(u); -- pa_assert(u->stream_out); -+ pa_assert(u->stream); - - routing = u->primary_devices | u->extra_devices; - -- pa_snprintf(tmp, sizeof(tmp), "%s=%u;", AUDIO_PARAMETER_STREAM_ROUTING, routing); -- pa_log_debug("Routing: set_parameters(): %s (%#010x)", tmp, routing); -- pa_droid_hw_module_lock(u->hw_module); -- u->stream_out->common.set_parameters(&u->stream_out->common, tmp); -- pa_droid_hw_module_unlock(u->hw_module); -- -- return true; -+ pa_droid_stream_set_output_route(u->stream, routing); - } - - static bool parse_device_list(const char *str, audio_devices_t *dst) { -- char *dev; -- const char *state = NULL; -- - pa_assert(str); - pa_assert(dst); - -+ char *dev; -+ const char *state = NULL; -+ - *dst = 0; - - while ((dev = pa_split(str, "|", &state))) { -@@ -235,15 +245,18 @@ static int thread_write_silence(struct u - - /* Drop our rendered audio and write silence to HAL. */ - pa_memblockq_drop(u->memblockq, u->buffer_size); -+ u->write_time = pa_rtclock_now(); - - /* We should be able to write everything in one go as long as memblock size - * is multiples of buffer_size. Even if we don't write whole buffer size - * here it's okay, as long as mute time isn't configured too strictly. */ - - p = pa_memblock_acquire(u->silence.memblock); -- wrote = u->stream_out->write(u->stream_out, (const uint8_t*) p + u->silence.index, u->silence.length); -+ wrote = u->stream->out->write(u->stream->out, (const uint8_t*) p + u->silence.index, u->silence.length); - pa_memblock_release(u->silence.memblock); - -+ u->write_time = pa_rtclock_now() - u->write_time; -+ - if (wrote < 0) - return -1; - -@@ -260,14 +273,18 @@ static int thread_write(struct userdata - /* We should be able to write everything in one go as long as memblock size - * is multiples of buffer_size. */ - -+ u->write_time = pa_rtclock_now(); -+ - for (;;) { - p = pa_memblock_acquire(c.memblock); -- wrote = u->stream_out->write(u->stream_out, (const uint8_t*) p + c.index, c.length); -+ wrote = u->stream->out->write(u->stream->out, (const uint8_t*) p + c.index, c.length); - pa_memblock_release(c.memblock); - - if (wrote < 0) { - pa_memblockq_drop(u->memblockq, c.length); - pa_memblock_unref(c.memblock); -+ u->write_time = 0; -+ pa_log("failed to write stream (%d)", wrote); - return -1; - } - -@@ -283,6 +300,8 @@ static int thread_write(struct userdata - break; - } - -+ u->write_time = pa_rtclock_now() - u->write_time; -+ - return 0; - } - static void thread_render(struct userdata *u) { -@@ -290,7 +309,7 @@ static void thread_render(struct userdat - size_t missing; - - length = pa_memblockq_get_length(u->memblockq); -- missing = u->buffer_size * u->buffer_count - length; -+ missing = u->buffer_size - length; - - if (missing > 0) { - pa_memchunk c; -@@ -352,22 +371,18 @@ static void thread_func(void *userdata) - - pa_thread_mq_install(&u->thread_mq); - -- u->timestamp = 0; -- - for (;;) { - int ret; - - if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { - -- u->timestamp = pa_rtclock_now(); -- - if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) - process_rewind(u); -- else -- thread_render(u); - - if (pa_rtpoll_timer_elapsed(u->rtpoll)) { -- pa_usec_t now, sleept; -+ pa_usec_t sleept = 0; -+ -+ thread_render(u); - - if (u->routing_counter == u->mute_routing_after) { - do_routing(u); -@@ -378,12 +393,8 @@ static void thread_func(void *userdata) - } else - thread_write(u); - -- now = pa_rtclock_now(); -- -- if (now - u->timestamp > u->buffer_latency / 2) -- sleept = 0; -- else -- sleept = u->buffer_latency / 2 - (now - u->timestamp) ; -+ if (u->write_time > u->write_threshold) -+ sleept = u->buffer_time; - - pa_rtpoll_set_timer_relative(u->rtpoll, sleept); - } -@@ -391,7 +402,11 @@ static void thread_func(void *userdata) - pa_rtpoll_set_timer_disabled(u->rtpoll); - - /* Sleep */ -+#if (PULSEAUDIO_VERSION_MAJOR == 5) -+ if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0) -+#elif (PULSEAUDIO_VERSION_MAJOR == 6) - if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) -+#endif - goto fail; - - if (ret == 0) -@@ -415,9 +430,9 @@ static int suspend(struct userdata *u) { - - pa_assert(u); - pa_assert(u->sink); -- pa_assert(u->stream_out); -+ pa_assert(u->stream->out); - -- ret = u->stream_out->common.standby(&u->stream_out->common); -+ ret = u->stream->out->common.standby(&u->stream->out->common); - - if (ret == 0) { - pa_sink_set_max_request_within_thread(u->sink, 0); -@@ -462,8 +477,8 @@ static int sink_process_msg(pa_msgobject - pa_usec_t r = 0; - - /* HAL reports milliseconds */ -- if (u->stream_out) -- r = u->stream_out->get_latency(u->stream_out) * PA_USEC_PER_MSEC * u->buffer_count; -+ if (u->stream->out) -+ r = u->stream->out->get_latency(u->stream->out) * PA_USEC_PER_MSEC; - - *((pa_usec_t*) data) = r; - -@@ -487,7 +502,6 @@ static int sink_process_msg(pa_msgobject - /* Fall through */ - case PA_SINK_RUNNING: { - int r; -- u->timestamp = 0; - - if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { - if ((r = unsuspend(u)) < 0) -@@ -498,8 +512,13 @@ static int sink_process_msg(pa_msgobject - break; - } - -+ case PA_SINK_UNLINKED: { -+ /* Suspending since some implementations do not want to free running stream. */ -+ suspend(u); -+ break; -+ } -+ - /* not needed */ -- case PA_SINK_UNLINKED: - case PA_SINK_INIT: - case PA_SINK_INVALID_STATE: - ; -@@ -514,7 +533,6 @@ static int sink_process_msg(pa_msgobject - static int sink_set_port_cb(pa_sink *s, pa_device_port *p) { - struct userdata *u = s->userdata; - pa_droid_port_data *data; -- const char *sco_transport_enabled; - - pa_assert(u); - pa_assert(p); -@@ -532,24 +550,9 @@ static int sink_set_port_cb(pa_sink *s, - pa_log_debug("Sink set port %u", data->device); - - set_primary_devices(u, data->device); -- -- /* See if the sco fake sink element is available (only when needed) */ -- if ((u->sco_fake_sink == NULL) && (data->device & AUDIO_DEVICE_OUT_ALL_SCO)) -- u->sco_fake_sink = pa_sco_fake_sink_discover(u->core, u->sco_fake_sink_name); -- -- /* Update the bluetooth hsp transport property before we do the routing */ -- if (u->sco_fake_sink) { -- sco_transport_enabled = pa_proplist_gets(u->sco_fake_sink->proplist, HSP_PREVENT_SUSPEND_STR); -- if (sco_transport_enabled && pa_streq(sco_transport_enabled, "true")) { -- if (data->device & ~AUDIO_DEVICE_OUT_ALL_SCO) -- set_fake_sco_sink_transport_property(u, "false"); -- } else if (data->device & AUDIO_DEVICE_OUT_ALL_SCO) -- set_fake_sco_sink_transport_property(u, "true"); -- } -- - /* If we are in voice call, sink is usually in suspended state and routing change can be applied immediately. -- * When in media use cases, do the routing change in IO thread. */ -- if (u->use_voice_volume) -+ * When in media use cases, do the routing change in IO thread if we are currently in RUNNING or IDLE state. */ -+ if (u->use_voice_volume || !PA_SINK_IS_OPENED(pa_sink_get_state(u->sink))) - do_routing(u); - else { - pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL); -@@ -569,7 +572,7 @@ static void sink_set_volume_cb(pa_sink * - float val = pa_sw_volume_to_linear(r.values[0]); - pa_log_debug("Set hw volume %f", val); - pa_droid_hw_module_lock(u->hw_module); -- if (u->stream_out->set_volume(u->stream_out, val, val) < 0) -+ if (u->stream->out->set_volume(u->stream->out, val, val) < 0) - pa_log_warn("Failed to set hw volume."); - pa_droid_hw_module_unlock(u->hw_module); - } else if (r.channels == 2) { -@@ -578,7 +581,7 @@ static void sink_set_volume_cb(pa_sink * - val[i] = pa_sw_volume_to_linear(r.values[i]); - pa_log_debug("Set hw volume %f : %f", val[0], val[1]); - pa_droid_hw_module_lock(u->hw_module); -- if (u->stream_out->set_volume(u->stream_out, val[0], val[1]) < 0) -+ if (u->stream->out->set_volume(u->stream->out, val[0], val[1]) < 0) - pa_log_warn("Failed to set hw volume."); - pa_droid_hw_module_unlock(u->hw_module); - } -@@ -635,10 +638,9 @@ static void update_volumes(struct userda - - /* set_volume returns 0 if hw volume control is implemented, < 0 otherwise. */ - pa_droid_hw_module_lock(u->hw_module); -- if (u->stream_out->set_volume) { -+ if (u->stream->out->set_volume) { - pa_log_debug("Probe hw volume support for %s", u->sink->name); -- pa_log_debug("Stream out volume set to 1.0f, 1.0f"); -- ret = u->stream_out->set_volume(u->stream_out, 1.0f, 1.0f); -+ ret = u->stream->out->set_volume(u->stream->out, 1.0f, 1.0f); - } - pa_droid_hw_module_unlock(u->hw_module); - -@@ -808,6 +810,7 @@ void pa_droid_sink_set_voice_control(pa_ - - if (u->use_voice_volume) { - pa_log_debug("Using voice volume control for %s", u->sink->name); -+ pa_sink_set_set_volume_callback(u->sink, NULL); - - if (u->voice_virtual_stream) - create_voice_virtual_stream(u); -@@ -818,6 +821,7 @@ void pa_droid_sink_set_voice_control(pa_ - - /* First disable module-device-restore, as we don't want to save the voice volume - * as the default sink volume when restoring to the default mode */ -+#define MODULE_DEVICE_RESTORE_SKIP_PROPERTY "module-device-restore.skip" - pa_proplist_sets(u->sink->proplist, MODULE_DEVICE_RESTORE_SKIP_PROPERTY, "true"); - - /* Then map normal sink volume changes to voice call volume changes */ -@@ -888,9 +892,9 @@ static pa_hook_result_t sink_input_put_h - - pa_log_debug("Add extra route %s (%u).", dev_str, devices); - -- add_extra_devices(u, devices); -- /* post routing change */ -- pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL); -+ /* if this device was not routed to previously post routing change */ -+ if (add_extra_devices(u, devices)) -+ pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL); - } - } - -@@ -919,9 +923,9 @@ static pa_hook_result_t sink_input_unlin - - pa_log_debug("Remove extra route %s (%u).", dev_str, devices); - -- remove_extra_devices(u, devices); -- /* post routing change */ -- pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL); -+ /* if this device no longer exists in extra devices map post routing change */ -+ if (remove_extra_devices(u, devices)) -+ pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_DO_ROUTING, NULL, 0, NULL, NULL); - } - } - -@@ -973,10 +977,8 @@ static pa_hook_result_t sink_proplist_ch - if (changed) { - pa_assert(parameter); - tmp = pa_sprintf_malloc("%s=%s;", parameter->key, parameter->value); -- pa_log_debug("sink proplist changed: set_parameters(): %s", tmp); -- pa_droid_hw_module_lock(u->hw_module); -- u->stream_out->common.set_parameters(&u->stream_out->common, tmp); -- pa_droid_hw_module_unlock(u->hw_module); -+ pa_log_debug("set_parameters(): %s", tmp); -+ pa_droid_stream_set_parameters(u->stream, tmp); - pa_xfree(tmp); - } - } -@@ -984,25 +986,6 @@ static pa_hook_result_t sink_proplist_ch - return PA_HOOK_OK; - } - --static struct pa_sink *pa_sco_fake_sink_discover(pa_core *core, const char *sink_name) { -- struct pa_sink *sink; -- pa_idxset *idxset; -- void *state = NULL; -- -- pa_assert(core); -- pa_assert(sink_name); -- pa_assert_se((idxset = core->sinks)); -- -- while ((sink = pa_idxset_iterate(idxset, &state, NULL)) != NULL) { -- if (pa_streq(sink_name, sink->name)) { -- pa_log_debug("Found fake SCO sink '%s'", sink_name); -- return sink; -- } -- } -- -- return NULL; --} -- - pa_sink *pa_droid_sink_new(pa_module *m, - pa_modargs *ma, - const char *driver, -@@ -1019,23 +1002,20 @@ pa_sink *pa_droid_sink_new(pa_module *m, - pa_sink_new_data data; - const char *module_id = NULL; - const char *tmp; -- /* char *list = NULL; */ -+ char *list = NULL; - uint32_t alternate_sample_rate; -- uint32_t sample_rate; -+ const char *format; - audio_devices_t dev_out; - pa_sample_spec sample_spec; - pa_channel_map channel_map; - bool namereg_fail = false; -- uint32_t total_latency; -+ pa_usec_t latency; - pa_droid_config_audio *config = NULL; /* Only used when sink is created without card */ - int32_t mute_routing_before = 0; - int32_t mute_routing_after = 0; - uint32_t sink_buffer = 0; - int ret; - -- audio_format_t hal_audio_format = 0; -- audio_channel_mask_t hal_channel_mask = 0; -- - pa_assert(m); - pa_assert(ma); - pa_assert(driver); -@@ -1054,8 +1034,36 @@ pa_sink *pa_droid_sink_new(pa_module *m, - sample_spec = m->core->default_sample_spec; - channel_map = m->core->default_channel_map; - -+ /* First parse both sample spec and channel map, then see if sink_* override some -+ * of the values. */ - if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) { -- pa_log("Failed to parse sample specification and channel map."); -+ pa_log("Failed to parse sink sample specification and channel map."); -+ goto fail; -+ } -+ -+ if (pa_modargs_get_value(ma, "sink_channel_map", NULL)) { -+ if (pa_modargs_get_channel_map(ma, "sink_channel_map", &channel_map) < 0) { -+ pa_log("Failed to parse sink channel map."); -+ goto fail; -+ } -+ -+ sample_spec.channels = channel_map.channels; -+ } -+ -+ if ((format = pa_modargs_get_value(ma, "sink_format", NULL))) { -+ if ((sample_spec.format = pa_parse_sample_format(format)) < 0) { -+ pa_log("Failed to parse sink format."); -+ goto fail; -+ } -+ } -+ -+ if (pa_modargs_get_value_u32(ma, "sink_rate", &sample_spec.rate) < 0) { -+ pa_log("Failed to parse sink samplerate"); -+ goto fail; -+ } -+ -+ if (!pa_sample_spec_valid(&sample_spec)) { -+ pa_log("Sample spec is not valid."); - goto fail; - } - -@@ -1097,12 +1105,13 @@ pa_sink *pa_droid_sink_new(pa_module *m, - u->deferred_volume = deferred_volume; - u->rtpoll = pa_rtpoll_new(); - pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); -- u->parameters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) parameter_free); -+ u->parameters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, -+ NULL, (pa_free_cb_t) parameter_free); - u->voice_volume_call_mode = voice_volume_call_mode; - u->voice_virtual_stream = voice_virtual_stream; - u->voice_property_key = pa_xstrdup(pa_modargs_get_value(ma, "voice_property_key", DEFAULT_VOICE_CONTROL_PROPERTY_KEY)); - u->voice_property_value = pa_xstrdup(pa_modargs_get_value(ma, "voice_property_value", DEFAULT_VOICE_CONTROL_PROPERTY_VALUE)); -- u->sco_fake_sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sco_fake_sink", DEFAULT_SCO_FAKE_SINK)); -+ u->extra_devices_map = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - - if (card_data) { - u->card_data = card_data; -@@ -1110,40 +1119,26 @@ pa_sink *pa_droid_sink_new(pa_module *m, - pa_assert_se((u->hw_module = pa_droid_hw_module_get(u->core, NULL, card_data->module_id))); - } else { - /* Sink wasn't created from inside card module, so we'll need to open -- * hw module ourselves. -- * TODO some way to share hw module between other sinks/sources since -- * opening same module from different places likely isn't a good thing. */ -- -- if (!(config = pa_droid_config_load(ma))) -- goto fail; -- -- /* Ownership of config transfers to hw_module if opening of hw module succeeds. */ -- if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id))) -- goto fail; -- } -- -- if (!pa_convert_format(sample_spec.format, CONV_FROM_PA, &hal_audio_format)) { -- pa_log("Sample spec format %u not supported.", sample_spec.format); -- goto fail; -- } -- -- for (int i = 0; i < channel_map.channels; i++) { -- audio_channel_mask_t c; -- if (!pa_convert_output_channel(channel_map.map[i], CONV_FROM_PA, &c)) { -- pa_log("Failed to convert channel map."); -- goto fail; -+ * hw module ourself. -+ * -+ * First let's find out if hw module has already been opened, or if we need to -+ * do it ourself. -+ */ -+ if (!(u->hw_module = pa_droid_hw_module_get(u->core, NULL, module_id))) { -+ -+ /* No hw module object in shared object db, let's open the module now. */ -+ -+ if (!(config = pa_droid_config_load(ma))) -+ goto fail; -+ -+ /* Ownership of config transfers to hw_module if opening of hw module succeeds. */ -+ if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id))) -+ goto fail; - } -- hal_channel_mask |= c; - } - -- struct audio_config config_out = { -- .sample_rate = sample_spec.rate, -- .channel_mask = hal_channel_mask, -- .format = hal_audio_format -- }; -- - /* Default routing */ -- dev_out = AUDIO_DEVICE_OUT_DEFAULT; -+ dev_out = u->hw_module->config->global_config.default_output_device; - - if ((tmp = pa_modargs_get_value(ma, "output_devices", NULL))) { - audio_devices_t tmp_dev; -@@ -1157,26 +1152,14 @@ pa_sink *pa_droid_sink_new(pa_module *m, - if (am) - flags = am->output->flags; - -- pa_droid_hw_module_lock(u->hw_module); -- ret = u->hw_module->device->open_output_stream(u->hw_module->device, -- u->hw_module->stream_out_id++, -- dev_out, -- flags, -- &config_out, -- &u->stream_out); -- pa_droid_hw_module_unlock(u->hw_module); -+ u->stream = pa_droid_open_output_stream(u->hw_module, &sample_spec, &channel_map, flags, dev_out); - -- if (!u->stream_out) { -- pa_log("Failed to open output stream. (errno %d)", ret); -+ if (!u->stream) { -+ pa_log("Failed to open output stream."); - goto fail; - } - -- if ((sample_rate = u->stream_out->common.get_sample_rate(&u->stream_out->common)) != sample_spec.rate) { -- pa_log_warn("Requested sample rate %u but got %u instead.", sample_spec.rate, sample_rate); -- sample_spec.rate = sample_rate; -- } -- -- u->buffer_size = u->stream_out->common.get_buffer_size(&u->stream_out->common); -+ u->buffer_size = u->stream->out->common.get_buffer_size(&u->stream->out->common); - if (sink_buffer) { - if (sink_buffer < u->buffer_size) - pa_log_warn("Requested buffer size %u less than HAL reported buffer size (%u).", sink_buffer, u->buffer_size); -@@ -1190,40 +1173,32 @@ pa_sink *pa_droid_sink_new(pa_module *m, - } - } - -- u->buffer_latency = pa_bytes_to_usec(u->buffer_size, &sample_spec); -- /* Disable internal rewinding for now. */ -- u->buffer_count = 1; -- -- pa_log_info("Created Android stream with device: %u flags: %u sample rate: %u channel mask: %u format: %u buffer size: %u", -- dev_out, -- flags, -- sample_rate, -- config_out.channel_mask, -- config_out.format, -- u->buffer_size); -- -+ u->buffer_time = pa_bytes_to_usec(u->buffer_size, &u->stream->sample_spec); - -+ u->write_threshold = u->buffer_time - u->buffer_time / 6; - u->mute_routing_before = mute_routing_before / u->buffer_size; - u->mute_routing_after = mute_routing_after / u->buffer_size; - if (u->mute_routing_before == 0 && mute_routing_before) -- u->mute_routing_before = u->buffer_size; -+ u->mute_routing_before = 1; - if (u->mute_routing_after == 0 && mute_routing_after) -- u->mute_routing_after = u->buffer_size; -+ u->mute_routing_after = 1; - if (u->mute_routing_before || u->mute_routing_after) - pa_log_debug("Mute playback when routing is changing, %u before and %u after.", - u->mute_routing_before * u->buffer_size, - u->mute_routing_after * u->buffer_size); -- pa_silence_memchunk_get(&u->core->silence_cache, u->core->mempool, &u->silence, &sample_spec, u->buffer_size); -- u->memblockq = pa_memblockq_new("droid-sink", 0, u->buffer_size * u->buffer_count, u->buffer_size * u->buffer_count, &sample_spec, 1, 0, 0, &u->silence); -+ pa_silence_memchunk_get(&u->core->silence_cache, u->core->mempool, &u->silence, &u->stream->sample_spec, u->buffer_size); -+ u->memblockq = pa_memblockq_new("droid-sink", 0, u->buffer_size, u->buffer_size, &u->stream->sample_spec, 1, 0, 0, &u->silence); - - pa_sink_new_data_init(&data); - data.driver = driver; - data.module = m; - data.card = card; - -- set_sink_name(ma, &data, module_id); -+ if (am) -+ set_sink_name(ma, &data, am->output->name); -+ else -+ set_sink_name(ma, &data, module_id); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound"); -- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, "internal"); - - /* We need to give pa_modargs_get_value_boolean() a pointer to a local - * variable instead of using &data.namereg_fail directly, because -@@ -1237,8 +1212,8 @@ pa_sink *pa_droid_sink_new(pa_module *m, - } - data.namereg_fail = namereg_fail; - -- pa_sink_new_data_set_sample_spec(&data, &sample_spec); -- pa_sink_new_data_set_channel_map(&data, &channel_map); -+ pa_sink_new_data_set_sample_spec(&data, &u->stream->sample_spec); -+ pa_sink_new_data_set_channel_map(&data, &u->stream->channel_map); - pa_sink_new_data_set_alternate_sample_rate(&data, alternate_sample_rate); - - /* -@@ -1282,9 +1257,12 @@ pa_sink *pa_droid_sink_new(pa_module *m, - pa_sink_set_rtpoll(u->sink, u->rtpoll); - - /* Rewind internal memblockq */ -- pa_sink_set_max_rewind(u->sink, u->buffer_size * (u->buffer_count - 1)); -+ pa_sink_set_max_rewind(u->sink, 0); - -- thread_name = pa_sprintf_malloc("droid-sink-%s", module_id); -+ if (am) -+ thread_name = pa_sprintf_malloc("droid-sink-%s", am->output->name); -+ else -+ thread_name = pa_sprintf_malloc("droid-sink-%s", module_id); - if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) { - pa_log("Failed to create thread."); - goto fail; -@@ -1292,11 +1270,11 @@ pa_sink *pa_droid_sink_new(pa_module *m, - pa_xfree(thread_name); - thread_name = NULL; - -- /* Latency consists of HAL latency + our memblockq latency */ -- total_latency = u->stream_out->get_latency(u->stream_out) + (uint32_t) pa_bytes_to_usec(u->buffer_size * u->buffer_count, &sample_spec); -- pa_sink_set_fixed_latency(u->sink, total_latency); -- pa_log_debug("Set fixed latency %lu usec", (unsigned long) pa_bytes_to_usec(total_latency, &sample_spec)); -- pa_sink_set_max_request(u->sink, u->buffer_size * u->buffer_count); -+ /* HAL latencies are in milliseconds. */ -+ latency = u->stream->out->get_latency(u->stream->out) * PA_USEC_PER_MSEC; -+ pa_sink_set_fixed_latency(u->sink, latency); -+ pa_log_debug("Set fixed latency %llu usec", latency); -+ pa_sink_set_max_request(u->sink, u->buffer_size); - - if (u->sink->active_port) - sink_set_port_cb(u->sink, u->sink->active_port); -@@ -1337,6 +1315,14 @@ void pa_droid_sink_free(pa_sink *s) { - userdata_free(u); - } - -+static void parameter_free(droid_parameter_mapping *m) { -+ pa_assert(m); -+ -+ pa_xfree(m->key); -+ pa_xfree(m->value); -+ pa_xfree(m); -+} -+ - static void userdata_free(struct userdata *u) { - - if (u->sink) -@@ -1367,11 +1353,8 @@ static void userdata_free(struct userdat - if (u->parameters) - pa_hashmap_free(u->parameters); - -- if (u->hw_module && u->stream_out) { -- pa_droid_hw_module_lock(u->hw_module); -- u->hw_module->device->close_output_stream(u->hw_module->device, u->stream_out); -- pa_droid_hw_module_unlock(u->hw_module); -- } -+ if (u->stream) -+ pa_droid_stream_unref(u->stream); - - if (u->memblockq) - pa_memblockq_free(u->memblockq); -@@ -1382,13 +1365,13 @@ static void userdata_free(struct userdat - if (u->hw_module) - pa_droid_hw_module_unref(u->hw_module); - -- if (u->sco_fake_sink_name) -- pa_xfree(u->sco_fake_sink_name); -- - if (u->voice_property_key) - pa_xfree(u->voice_property_key); - if (u->voice_property_value) - pa_xfree(u->voice_property_value); - -+ if (u->extra_devices_map) -+ pa_hashmap_free(u->extra_devices_map); -+ - pa_xfree(u); - } -Index: pulseaudio/src/modules/droid/droid-source.c -=================================================================== ---- pulseaudio.orig/src/modules/droid/droid-source.c -+++ pulseaudio/src/modules/droid/droid-source.c -@@ -66,7 +66,6 @@ struct userdata { - - pa_memchunk memchunk; - audio_devices_t primary_devices; -- audio_devices_t enabled_devices; - bool routing_changes_enabled; - - size_t buffer_size; -@@ -74,58 +73,65 @@ struct userdata { - - pa_droid_card_data *card_data; - pa_droid_hw_module *hw_module; -- audio_stream_in_t *stream; -+ pa_droid_stream *stream; - }; - -+#define PULSEAUDIO_VERSION_MAJOR 6 -+ - #define DEFAULT_MODULE_ID "primary" - -+#define DROID_AUDIO_SOURCE "droid.audio_source" -+#define DROID_AUDIO_SOURCE_UNDEFINED "undefined" -+ - static void userdata_free(struct userdata *u); - --static bool do_routing(struct userdata *u, audio_devices_t devices) { -- char tmp[32]; -- char *devlist; -+static int do_routing(struct userdata *u, audio_devices_t devices, bool force) { -+ int ret; -+ pa_proplist *p; -+ const char *source_str; -+ audio_devices_t old_device; -+ audio_source_t source; - - pa_assert(u); - pa_assert(u->stream); - -- if (!u->routing_changes_enabled) { -+ if (!force && !u->routing_changes_enabled) { - pa_log_debug("Skipping routing change."); -- return false; -+ return 0; - } - - if (u->primary_devices == devices) - pa_log_debug("Refresh active device routing."); - -- u->enabled_devices &= ~u->primary_devices; -+ old_device = u->primary_devices; - u->primary_devices = devices; -- u->enabled_devices |= u->primary_devices; - -- devlist = pa_list_string_input_device(devices); -- pa_assert(devlist); --#ifdef DROID_DEVICE_I9305 -- pa_snprintf(tmp, sizeof(tmp), "%s=%u", AUDIO_PARAMETER_STREAM_ROUTING, devices & ~AUDIO_DEVICE_BIT_IN); --#else -- pa_snprintf(tmp, sizeof(tmp), "%s=%u", AUDIO_PARAMETER_STREAM_ROUTING, devices); --#endif -- pa_log_debug("set_parameters(): %s (%s : %#010x)", tmp, devlist, devices); -- pa_xfree(devlist); --#ifdef DROID_DEVICE_MAKO --#warning Using mako set_parameters hack. -- u->card_data->set_parameters(u->card_data, tmp); --#else -- u->stream->common.set_parameters(&u->stream->common, tmp); --#endif -+ ret = pa_droid_stream_set_input_route(u->stream, devices, &source); - -- return true; -+ if (ret < 0) -+ u->primary_devices = old_device; -+ else { -+ if (source != (uint32_t) -1) -+ pa_assert_se(pa_droid_audio_source_name(source, &source_str)); -+ else -+ source_str = DROID_AUDIO_SOURCE_UNDEFINED; -+ -+ p = pa_proplist_new(); -+ pa_proplist_sets(p, DROID_AUDIO_SOURCE, source_str); -+ pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, p); -+ pa_proplist_free(p); -+ } -+ -+ return ret; - } - - static bool parse_device_list(const char *str, audio_devices_t *dst) { -- char *dev; -- const char *state = NULL; -- - pa_assert(str); - pa_assert(dst); - -+ char *dev; -+ const char *state = NULL; -+ - *dst = 0; - - while ((dev = pa_split(str, "|", &state))) { -@@ -153,7 +159,7 @@ static int thread_read(struct userdata * - chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) u->buffer_size); - - p = pa_memblock_acquire(chunk.memblock); -- readd = u->stream->read(u->stream, (uint8_t*) p, pa_memblock_get_length(chunk.memblock)); -+ readd = u->stream->in->read(u->stream->in, (uint8_t*) p, pa_memblock_get_length(chunk.memblock)); - pa_memblock_release(chunk.memblock); - - if (readd < 0) { -@@ -179,6 +185,7 @@ static void thread_func(void *userdata) - struct userdata *u = userdata; - - pa_assert(u); -+ pa_assert(u->stream); - - pa_log_debug("Thread starting up."); - -@@ -189,6 +196,8 @@ static void thread_func(void *userdata) - - u->timestamp = pa_rtclock_now(); - -+ u->stream->in->common.standby(&u->stream->in->common); -+ - for (;;) { - int ret; - -@@ -200,7 +209,11 @@ static void thread_func(void *userdata) - pa_rtpoll_set_timer_disabled(u->rtpoll); - - /* Sleep */ -+#if (PULSEAUDIO_VERSION_MAJOR == 5) -+ if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0) -+#elif (PULSEAUDIO_VERSION_MAJOR == 6) - if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) -+#endif - goto fail; - - if (ret == 0) -@@ -225,7 +238,7 @@ static int suspend(struct userdata *u) { - pa_assert(u); - pa_assert(u->stream); - -- ret = u->stream->common.standby(&u->stream->common); -+ ret = u->stream->in->common.standby(&u->stream->in->common); - - if (ret == 0) - pa_log_info("Device suspended."); -@@ -259,8 +272,13 @@ static int source_process_msg(pa_msgobje - break; - } - -+ case PA_SOURCE_UNLINKED: { -+ /* Suspending since some implementations do not want to free running stream. */ -+ suspend(u); -+ break; -+ } -+ - /* not needed */ -- case PA_SOURCE_UNLINKED: - case PA_SOURCE_INIT: - case PA_SOURCE_INVALID_STATE: - ; -@@ -272,7 +290,7 @@ static int source_process_msg(pa_msgobje - return pa_source_process_msg(o, code, data, offset, chunk); - } - --static int source_set_port_cb(pa_source *s, pa_device_port *p) { -+static int droid_source_set_port(pa_source *s, pa_device_port *p, bool force) { - struct userdata *u = s->userdata; - pa_droid_port_data *data; - -@@ -291,11 +309,34 @@ static int source_set_port_cb(pa_source - - pa_log_debug("Source set port %u", data->device); - -- do_routing(u, data->device); -+ return do_routing(u, data->device, force); -+} - -- return 0; -+int pa_droid_source_set_port(pa_source *s, pa_device_port *p) { -+ return droid_source_set_port(s, p, true); - } - -+static int source_set_port_cb(pa_source *s, pa_device_port *p) { -+ return droid_source_set_port(s, p, false); -+} -+ -+static void source_set_voicecall_source_port(struct userdata *u) { -+ pa_device_port *port; -+ pa_droid_port_data *data; -+ void *state; -+ -+ pa_assert(u); -+ pa_assert(u->source); -+ -+ PA_HASHMAP_FOREACH(port, u->source->ports, state) { -+ data = PA_DEVICE_PORT_DATA(port); -+ -+ if (data->device & AUDIO_DEVICE_IN_VOICE_CALL) { -+ pa_source_set_port(u->source, port->name, false); -+ break; -+ } -+ } -+} - - static void source_set_name(pa_modargs *ma, pa_source_new_data *data, const char *module_id) { - const char *tmp; -@@ -318,8 +359,13 @@ static void source_set_name(pa_modargs * - } - } - -+#if (PULSEAUDIO_VERSION_MAJOR == 5) - static void source_get_mute_cb(pa_source *s) { -+#elif (PULSEAUDIO_VERSION_MAJOR == 6) -+static int source_get_mute_cb(pa_source *s, bool *muted) { -+#endif - struct userdata *u = s->userdata; -+ int ret = 0; - bool b; - - pa_assert(u); -@@ -328,12 +374,19 @@ static void source_get_mute_cb(pa_source - pa_droid_hw_module_lock(u->hw_module); - if (u->hw_module->device->get_mic_mute(u->hw_module->device, &b) < 0) { - pa_log("Failed to get mute state."); -- pa_droid_hw_module_unlock(u->hw_module); -- return; -+ ret = -1; - } - pa_droid_hw_module_unlock(u->hw_module); - -- s->muted = b; -+#if (PULSEAUDIO_VERSION_MAJOR == 5) -+ if (ret == 0) -+ s->muted = b; -+#elif (PULSEAUDIO_VERSION_MAJOR == 6) -+ if (ret == 0) -+ *muted = b; -+ -+ return ret; -+#endif - } - - static void source_set_mute_cb(pa_source *s) { -@@ -377,6 +430,7 @@ void pa_droid_source_set_routing(pa_sour - pa_source *pa_droid_source_new(pa_module *m, - pa_modargs *ma, - const char *driver, -+ audio_devices_t device, - pa_droid_card_data *card_data, - pa_droid_mapping *am, - pa_card *card) { -@@ -385,27 +439,24 @@ pa_source *pa_droid_source_new(pa_module - char *thread_name = NULL; - pa_source_new_data data; - const char *module_id = NULL; -- /* const char *tmp; */ -+ const char *tmp; - uint32_t sample_rate; - uint32_t alternate_sample_rate; - audio_devices_t dev_in; - pa_sample_spec sample_spec; - pa_channel_map channel_map; -+ const char *format; - bool namereg_fail = false; - pa_droid_config_audio *config = NULL; /* Only used when source is created without card */ - uint32_t source_buffer = 0; -- char audio_source[32]; -- int ret; -- -- audio_format_t hal_audio_format = 0; -- audio_channel_mask_t hal_channel_mask = 0; -+ bool voicecall_source = false; - - pa_assert(m); - pa_assert(ma); - pa_assert(driver); - - /* When running under card use hw module name for source by default. */ -- if (card && ma) -+ if (am) - module_id = am->input->module->name; - else - module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID); -@@ -413,8 +464,41 @@ pa_source *pa_droid_source_new(pa_module - sample_spec = m->core->default_sample_spec; - channel_map = m->core->default_channel_map; - -+ if (device & AUDIO_DEVICE_IN_VOICE_CALL) { -+ pa_log_info("Enabling voice call record source. Most module arguments are overridden."); -+ voicecall_source = true; -+ } -+ -+ /* First parse both sample spec and channel map, then see if source_* override some -+ * of the values. */ - if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) { -- pa_log("Failed to parse sample specification and channel map."); -+ pa_log("Failed to parse source sample specification and channel map."); -+ goto fail; -+ } -+ -+ if (pa_modargs_get_value(ma, "source_channel_map", NULL)) { -+ if (pa_modargs_get_channel_map(ma, "source_channel_map", &channel_map) < 0) { -+ pa_log("Failed to parse source channel map."); -+ goto fail; -+ } -+ -+ sample_spec.channels = channel_map.channels; -+ } -+ -+ if ((format = pa_modargs_get_value(ma, "source_format", NULL))) { -+ if ((sample_spec.format = pa_parse_sample_format(format)) < 0) { -+ pa_log("Failed to parse source format."); -+ goto fail; -+ } -+ } -+ -+ if (pa_modargs_get_value_u32(ma, "source_rate", &sample_spec.rate) < 0) { -+ pa_log("Failed to parse source_rate."); -+ goto fail; -+ } -+ -+ if (!pa_sample_spec_valid(&sample_spec)) { -+ pa_log("Sample spec is not valid."); - goto fail; - } - -@@ -436,8 +520,8 @@ pa_source *pa_droid_source_new(pa_module - u->rtpoll = pa_rtpoll_new(); - pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); - -- /* Enabled routing changes by default. */ -- u->routing_changes_enabled = true; -+ /* Enabled routing changes by default, except for voicecall source. */ -+ u->routing_changes_enabled = voicecall_source ? false : true; - - if (card_data) { - pa_assert(card); -@@ -446,83 +530,44 @@ pa_source *pa_droid_source_new(pa_module - } else { - /* Stand-alone source */ - -- if (!(config = pa_droid_config_load(ma))) -- goto fail; -- -- /* Ownership of config transfers to hw_module if opening of hw module succeeds. */ -- if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id))) -- goto fail; -+ if (!(u->hw_module = pa_droid_hw_module_get(u->core, NULL, module_id))) { -+ if (!(config = pa_droid_config_load(ma))) -+ goto fail; -+ -+ /* Ownership of config transfers to hw_module if opening of hw module succeeds. */ -+ if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id))) -+ goto fail; -+ } - } - -- if (!pa_convert_format(sample_spec.format, CONV_FROM_PA, &hal_audio_format)) { -- pa_log("Sample spec format %u not supported.", sample_spec.format); -- goto fail; -- } -+ /* Default routing */ -+ if (device) -+ dev_in = device; -+ else { -+ /* FIXME So while setting routing through stream with HALv2 API fails, creation of stream -+ * requires HALv2 style device to work properly. So until that oddity is resolved we always -+ * set AUDIO_DEVICE_IN_BUILTIN_MIC as initial device here. */ -+ pa_log_info("FIXME: Setting AUDIO_DEVICE_IN_BUILTIN_MIC as initial device."); -+ pa_assert_se(pa_string_convert_input_device_str_to_num("AUDIO_DEVICE_IN_BUILTIN_MIC", &dev_in)); - -- for (int i = 0; i < channel_map.channels; i++) { -- audio_channel_mask_t c; -- if (!pa_convert_input_channel(channel_map.map[i], CONV_FROM_PA, &c)) { -- pa_log("Failed to convert channel map."); -- goto fail; -- } -- hal_channel_mask |= c; -- } -+ if ((tmp = pa_modargs_get_value(ma, "input_devices", NULL))) { -+ audio_devices_t tmp_dev; - -- struct audio_config config_in = { -- .sample_rate = sample_spec.rate, -- .channel_mask = hal_channel_mask, -- .format = hal_audio_format -- }; -+ if (parse_device_list(tmp, &tmp_dev) && tmp_dev) -+ dev_in = tmp_dev; - -- /* Default routing */ -- /* FIXME So while setting routing through stream with HALv2 API fails, creation of stream -- * requires HALv2 style device to work properly. So until that oddity is resolved we always -- * set AUDIO_DEVICE_IN_BUILTIN_MIC as initial device here. */ --#if 0 -- pa_assert_se(pa_string_convert_input_device_str_to_num("AUDIO_DEVICE_IN_BUILTIN_MIC", &dev_in)); -- -- if ((tmp = pa_modargs_get_value(ma, "input_devices", NULL))) { -- audio_devices_t tmp_dev; -- -- if (parse_device_list(tmp, &tmp_dev) && tmp_dev) -- dev_in = tmp_dev; -- -- pa_log_debug("Set initial devices %s", tmp); -- } --#else -- pa_log_info("FIXME: Setting AUDIO_DEVICE_IN_BUILTIN_MIC as initial device."); -- dev_in = AUDIO_DEVICE_IN_BUILTIN_MIC; --#endif -- pa_droid_hw_module_lock(u->hw_module); -- ret = u->hw_module->device->open_input_stream(u->hw_module->device, -- u->hw_module->stream_in_id, -- dev_in, -- &config_in, -- &u->stream); -- /* On some devices the first call will fail if the config parameters are -- * not supported, but it'll automatically set the right ones, expecting -- * the caller to call it again, so let's try at least one more time */ -- if (!u->stream) -- ret = u->hw_module->device->open_input_stream(u->hw_module->device, -- u->hw_module->stream_in_id, -- dev_in, -- &config_in, -- &u->stream); -+ pa_log_debug("Set initial devices %s", tmp); -+ } -+ } - -- u->hw_module->stream_in_id++; -- pa_droid_hw_module_unlock(u->hw_module); -+ u->stream = pa_droid_open_input_stream(u->hw_module, &sample_spec, &channel_map, dev_in); - -- if (ret < 0) { -+ if (!u->stream) { - pa_log("Failed to open input stream."); - goto fail; - } - -- if ((sample_rate = u->stream->common.get_sample_rate(&u->stream->common)) != sample_spec.rate) { -- pa_log_warn("Requested sample rate %u but got %u instead.", sample_spec.rate, sample_rate); -- sample_spec.rate = sample_rate; -- } -- -- u->buffer_size = u->stream->common.get_buffer_size(&u->stream->common); -+ u->buffer_size = u->stream->in->common.get_buffer_size(&u->stream->in->common); - if (source_buffer) { - if (source_buffer < u->buffer_size) - pa_log_warn("Requested buffer size %u less than HAL reported buffer size (%u).", source_buffer, u->buffer_size); -@@ -536,18 +581,6 @@ pa_source *pa_droid_source_new(pa_module - } - } - -- pa_log_info("Created Android stream with device: %u sample rate: %u channel mask: %u format: %u buffer size: %u", -- dev_in, -- sample_rate, -- config_in.channel_mask, -- config_in.format, -- u->buffer_size); -- -- /* Setting audio source to MIC by default */ -- pa_snprintf(audio_source, sizeof(audio_source), "%s=%u", AUDIO_PARAMETER_STREAM_INPUT_SOURCE, AUDIO_SOURCE_MIC); -- u->stream->common.set_parameters(&u->stream->common, audio_source); -- pa_log_debug("Setting audio source to AUDIO_SOURCE_MIC by default"); -- - pa_source_new_data_init(&data); - data.driver = driver; - data.module = m; -@@ -567,11 +600,11 @@ pa_source *pa_droid_source_new(pa_module - } - data.namereg_fail = namereg_fail; - -- pa_source_new_data_set_sample_spec(&data, &sample_spec); -- pa_source_new_data_set_channel_map(&data, &channel_map); -+ pa_source_new_data_set_sample_spec(&data, &u->stream->sample_spec); -+ pa_source_new_data_set_channel_map(&data, &u->stream->channel_map); - pa_source_new_data_set_alternate_sample_rate(&data, alternate_sample_rate); - -- if (am) -+ if (am && card) - pa_droid_add_ports(data.ports, am, card); - - u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE); -@@ -604,12 +637,15 @@ pa_source *pa_droid_source_new(pa_module - pa_xfree(thread_name); - thread_name = NULL; - -- pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &sample_spec)); -- pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, &sample_spec)); -+ pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &u->stream->sample_spec)); -+ pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, &u->stream->sample_spec)); - -- if (u->source->active_port) -+ if (!voicecall_source && u->source->active_port) - source_set_port_cb(u->source, u->source->active_port); - -+ if (voicecall_source) -+ source_set_voicecall_source_port(u); -+ - pa_source_put(u->source); - - return u->source; -@@ -653,11 +689,8 @@ static void userdata_free(struct userdat - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - -- if (u->hw_module && u->stream) { -- pa_droid_hw_module_lock(u->hw_module); -- u->hw_module->device->close_input_stream(u->hw_module->device, u->stream); -- pa_droid_hw_module_unlock(u->hw_module); -- } -+ if (u->stream) -+ pa_droid_stream_unref(u->stream); - - // Stand alone source - if (u->hw_module) -Index: pulseaudio/src/modules/droid/droid-source.h -=================================================================== ---- pulseaudio.orig/src/modules/droid/droid-source.h -+++ pulseaudio/src/modules/droid/droid-source.h -@@ -43,14 +43,17 @@ - - #include "droid-util.h" - -+/* If device is non-zero, it will override whatever is set in modargs for input device. */ - pa_source *pa_droid_source_new(pa_module *m, - pa_modargs *ma, - const char *driver, -+ audio_devices_t device, - pa_droid_card_data *card_data, - pa_droid_mapping *am, - pa_card *card); - void pa_droid_source_free(pa_source *s); - - void pa_droid_source_set_routing(pa_source *s, bool enabled); -+int pa_droid_source_set_port(pa_source *s, pa_device_port *p); - - #endif -Index: pulseaudio/src/modules/droid/droid-util-42.h -=================================================================== ---- pulseaudio.orig/src/modules/droid/droid-util-42.h -+++ pulseaudio/src/modules/droid/droid-util-42.h -@@ -22,7 +22,10 @@ - #ifndef _ANDROID_UTIL_V42_H_ - #define _ANDROID_UTIL_V42_H_ - --#define HAL_V2 -+#define DROID_HAL 2 -+ -+#include -+#include - - // PulseAudio value - Android value - -Index: pulseaudio/src/modules/droid/droid-util-44.h -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/droid-util-44.h -@@ -0,0 +1,349 @@ -+/* -+ * Copyright (C) 2013 Jolla Ltd. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifndef _ANDROID_UTIL_V44_H_ -+#define _ANDROID_UTIL_V44_H_ -+ -+#define DROID_HAL 2 -+ -+// Android v4.4 has SPEAKER_DRC_ENABLED_TAG, so might the future versions -+#define DROID_HAVE_DRC -+ -+#include -+#include -+ -+// PulseAudio value - Android value -+ -+uint32_t conversion_table_output_channel[][2] = { -+ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_OUT_MONO }, -+ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_OUT_FRONT_LEFT }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_OUT_FRONT_RIGHT}, -+ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_OUT_FRONT_CENTER }, -+ { PA_CHANNEL_POSITION_SUBWOOFER, AUDIO_CHANNEL_OUT_LOW_FREQUENCY }, -+ { PA_CHANNEL_POSITION_REAR_LEFT, AUDIO_CHANNEL_OUT_BACK_LEFT }, -+ { PA_CHANNEL_POSITION_REAR_RIGHT, AUDIO_CHANNEL_OUT_BACK_RIGHT }, -+ { PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER }, -+ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_OUT_BACK_CENTER }, -+ { PA_CHANNEL_POSITION_SIDE_LEFT, AUDIO_CHANNEL_OUT_SIDE_LEFT }, -+ { PA_CHANNEL_POSITION_SIDE_RIGHT, AUDIO_CHANNEL_OUT_SIDE_RIGHT }, -+ { PA_CHANNEL_POSITION_TOP_CENTER, AUDIO_CHANNEL_OUT_TOP_CENTER }, -+ { PA_CHANNEL_POSITION_TOP_FRONT_LEFT, AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT }, -+ { PA_CHANNEL_POSITION_TOP_FRONT_CENTER, AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER }, -+ { PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT }, -+ { PA_CHANNEL_POSITION_TOP_REAR_LEFT, AUDIO_CHANNEL_OUT_TOP_BACK_LEFT }, -+ { PA_CHANNEL_POSITION_TOP_REAR_CENTER, AUDIO_CHANNEL_OUT_TOP_BACK_CENTER }, -+ { PA_CHANNEL_POSITION_TOP_REAR_RIGHT, AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT } -+}; -+ -+uint32_t conversion_table_input_channel[][2] = { -+ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_MONO }, -+ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_IN_LEFT }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_IN_RIGHT}, -+ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_IN_FRONT }, -+ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_IN_BACK }, -+ /* Following are missing suitable counterparts on PulseAudio side. */ -+ { AUDIO_CHANNEL_IN_LEFT_PROCESSED, AUDIO_CHANNEL_IN_LEFT_PROCESSED }, -+ { AUDIO_CHANNEL_IN_RIGHT_PROCESSED, AUDIO_CHANNEL_IN_RIGHT_PROCESSED }, -+ { AUDIO_CHANNEL_IN_FRONT_PROCESSED, AUDIO_CHANNEL_IN_FRONT_PROCESSED }, -+ { AUDIO_CHANNEL_IN_BACK_PROCESSED, AUDIO_CHANNEL_IN_BACK_PROCESSED }, -+ { AUDIO_CHANNEL_IN_PRESSURE, AUDIO_CHANNEL_IN_PRESSURE }, -+ { AUDIO_CHANNEL_IN_X_AXIS, AUDIO_CHANNEL_IN_X_AXIS }, -+ { AUDIO_CHANNEL_IN_Y_AXIS, AUDIO_CHANNEL_IN_Y_AXIS }, -+ { AUDIO_CHANNEL_IN_Z_AXIS, AUDIO_CHANNEL_IN_Z_AXIS }, -+ { AUDIO_CHANNEL_IN_VOICE_UPLINK, AUDIO_CHANNEL_IN_VOICE_UPLINK }, -+ { AUDIO_CHANNEL_IN_VOICE_DNLINK, AUDIO_CHANNEL_IN_VOICE_DNLINK } -+}; -+ -+uint32_t conversion_table_format[][2] = { -+ { PA_SAMPLE_U8, AUDIO_FORMAT_PCM_8_BIT }, -+ { PA_SAMPLE_S16LE, AUDIO_FORMAT_PCM_16_BIT }, -+ { PA_SAMPLE_S32LE, AUDIO_FORMAT_PCM_32_BIT }, -+ { PA_SAMPLE_S24LE, AUDIO_FORMAT_PCM_8_24_BIT } -+}; -+ -+uint32_t conversion_table_default_audio_source[][2] = { -+#ifdef DROID_DEVICE_HAMMERHEAD -+ { AUDIO_DEVICE_IN_COMMUNICATION, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_AMBIENT, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_BUILTIN_MIC, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_AUX_DIGITAL, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_VOICE_CALL, AUDIO_SOURCE_VOICE_CALL }, -+ { AUDIO_DEVICE_IN_BACK_MIC, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_SOURCE_REMOTE_SUBMIX }, -+ { AUDIO_DEVICE_IN_ANC_HEADSET, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_FM_RX, AUDIO_SOURCE_FM_RX }, -+ { AUDIO_DEVICE_IN_FM_RX_A2DP, AUDIO_SOURCE_FM_RX_A2DP }, -+#endif -+ { AUDIO_DEVICE_IN_ALL, AUDIO_SOURCE_DEFAULT } -+}; -+ -+struct string_conversion { -+ uint32_t value; -+ const char *str; -+}; -+ -+#if defined(STRING_ENTRY) -+#error STRING_ENTRY already defined somewhere, fix this lib. -+#endif -+#define STRING_ENTRY(str) { str, #str } -+/* Output devices */ -+struct string_conversion string_conversion_table_output_device[] = { -+ STRING_ENTRY(AUDIO_DEVICE_OUT_EARPIECE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_SPEAKER), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADPHONE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_AUX_DIGITAL), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_USB_ACCESSORY), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_USB_DEVICE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_REMOTE_SUBMIX), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_DEFAULT), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_A2DP), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_SCO), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_USB), -+#ifdef QCOM_HARDWARE -+ STRING_ENTRY(AUDIO_DEVICE_OUT_FM), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_FM_TX), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ANC_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ANC_HEADPHONE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_PROXY), -+#endif -+ { 0, NULL } -+}; -+ -+struct string_conversion string_conversion_table_output_device_fancy[] = { -+ { AUDIO_DEVICE_OUT_EARPIECE, "output-earpiece" }, -+ { AUDIO_DEVICE_OUT_SPEAKER, "output-speaker" }, -+ { AUDIO_DEVICE_OUT_SPEAKER -+ | AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-speaker+wired_headphone" }, -+ { AUDIO_DEVICE_OUT_WIRED_HEADSET, "output-wired_headset" }, -+ { AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-wired_headphone" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO, "output-bluetooth_sco" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, "output-sco_headset" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, "output-sco_carkit" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, "output-a2dp" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, "output-a2dp_headphones" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, "output-a2dp_speaker" }, -+ { AUDIO_DEVICE_OUT_AUX_DIGITAL, "output-aux_digital" }, -+ { AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, "output-analog_dock_headset" }, -+ { AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, "output-digital_dock_headset" }, -+ { AUDIO_DEVICE_OUT_USB_ACCESSORY, "output-usb_accessory" }, -+ { AUDIO_DEVICE_OUT_USB_DEVICE, "output-usb_device" }, -+ { AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "output-remote_submix" }, -+#ifdef QCOM_HARDWARE -+ { AUDIO_DEVICE_OUT_FM, "output-fm" }, -+ { AUDIO_DEVICE_OUT_FM_TX, "output-fm_tx" }, -+ { AUDIO_DEVICE_OUT_ANC_HEADSET, "output-anc_headset" }, -+ { AUDIO_DEVICE_OUT_ANC_HEADPHONE, "output-anc_headphone" }, -+ { AUDIO_DEVICE_OUT_PROXY, "output-proxy" }, -+#endif -+ { 0, NULL } -+}; -+ -+/* Input devices */ -+struct string_conversion string_conversion_table_input_device[] = { -+ STRING_ENTRY(AUDIO_DEVICE_IN_COMMUNICATION), -+ STRING_ENTRY(AUDIO_DEVICE_IN_AMBIENT), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BUILTIN_MIC), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_WIRED_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_AUX_DIGITAL), -+ STRING_ENTRY(AUDIO_DEVICE_IN_VOICE_CALL), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BACK_MIC), -+ STRING_ENTRY(AUDIO_DEVICE_IN_REMOTE_SUBMIX), -+ STRING_ENTRY(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_USB_ACCESSORY), -+ STRING_ENTRY(AUDIO_DEVICE_IN_USB_DEVICE), -+#ifdef QCOM_HARDWARE -+ STRING_ENTRY(AUDIO_DEVICE_IN_ANC_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX), -+ STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX_A2DP), -+#endif -+ STRING_ENTRY(AUDIO_DEVICE_IN_DEFAULT), -+ /* Combination entries consisting of multiple devices defined above. -+ * These don't require counterpart in string_conversion_table_input_device_fancy. */ -+ STRING_ENTRY(AUDIO_DEVICE_IN_ALL), -+ STRING_ENTRY(AUDIO_DEVICE_IN_ALL_SCO), -+ { 0, NULL } -+}; -+ -+struct string_conversion string_conversion_table_input_device_fancy[] = { -+ { AUDIO_DEVICE_IN_COMMUNICATION, "input-communication" }, -+ { AUDIO_DEVICE_IN_AMBIENT, "input-ambient" }, -+ { AUDIO_DEVICE_IN_BUILTIN_MIC, "input-builtin_mic" }, -+ { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, "input-bluetooth_sco_headset" }, -+ { AUDIO_DEVICE_IN_WIRED_HEADSET, "input-wired_headset" }, -+ { AUDIO_DEVICE_IN_AUX_DIGITAL, "input-aux_digital" }, -+ { AUDIO_DEVICE_IN_VOICE_CALL, "input-voice_call" }, -+ { AUDIO_DEVICE_IN_BACK_MIC, "input-back_mic" }, -+ { AUDIO_DEVICE_IN_REMOTE_SUBMIX, "input-remote_submix" }, -+ { AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET, "input-analog_dock_headset" }, -+ { AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET, "input-digital_dock_headset" }, -+ { AUDIO_DEVICE_IN_USB_ACCESSORY, "input-usb_accessory" }, -+ { AUDIO_DEVICE_IN_USB_DEVICE, "input-usb_device" }, -+#ifdef QCOM_HARDWARE -+ { AUDIO_DEVICE_IN_ANC_HEADSET, "input-anc_headset" }, -+ { AUDIO_DEVICE_IN_FM_RX, "input-fm_rx" }, -+ { AUDIO_DEVICE_IN_FM_RX_A2DP, "input-fm_rx_a2dp" }, -+#endif -+ { AUDIO_DEVICE_IN_DEFAULT, "input-default" }, -+ { 0, NULL } -+}; -+ -+struct string_conversion string_conversion_table_audio_source_fancy[] = { -+ { AUDIO_SOURCE_DEFAULT, "default" }, -+ { AUDIO_SOURCE_MIC, "mic" }, -+ { AUDIO_SOURCE_VOICE_UPLINK, "voice uplink" }, -+ { AUDIO_SOURCE_VOICE_DOWNLINK, "voice downlink" }, -+ { AUDIO_SOURCE_VOICE_CALL, "voice call" }, -+ { AUDIO_SOURCE_CAMCORDER, "camcorder" }, -+ { AUDIO_SOURCE_VOICE_RECOGNITION, "voice recognition" }, -+ { AUDIO_SOURCE_VOICE_COMMUNICATION, "voice communication" }, -+ { AUDIO_SOURCE_REMOTE_SUBMIX, "remote submix" }, -+#ifdef QCOM_HARDWARE -+ { AUDIO_SOURCE_FM_RX, "fm rx" }, -+ { AUDIO_SOURCE_FM_RX_A2DP, "fm rx a2dp" }, -+#endif -+ { (uint32_t)-1, NULL } -+}; -+ -+/* Flags */ -+struct string_conversion string_conversion_table_output_flag[] = { -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_NONE), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_DIRECT), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_PRIMARY), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_FAST), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_DEEP_BUFFER), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_NON_BLOCKING), -+#ifdef QCOM_HARDWARE -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_LPA), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_TUNNEL), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_VOIP_RX), -+#endif -+ { 0, NULL } -+}; -+ -+/* Channels */ -+struct string_conversion string_conversion_table_output_channels[] = { -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_LOW_FREQUENCY), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_STEREO), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_QUAD), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_SURROUND), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_5POINT1), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_7POINT1), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_ALL), -+ { 0, NULL } -+}; -+struct string_conversion string_conversion_table_input_channels[] = { -+ STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_BACK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_BACK_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_PRESSURE), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_X_AXIS), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_Y_AXIS), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_Z_AXIS), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_UPLINK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_DNLINK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_STEREO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_ALL), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT_BACK), -+#ifdef QCOM_HARDWARE -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_CALL_MONO), -+#endif -+ { 0, NULL } -+}; -+ -+/* Formats */ -+struct string_conversion string_conversion_table_format[] = { -+ STRING_ENTRY(AUDIO_FORMAT_DEFAULT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM), -+ STRING_ENTRY(AUDIO_FORMAT_MP3), -+ STRING_ENTRY(AUDIO_FORMAT_AMR_NB), -+ STRING_ENTRY(AUDIO_FORMAT_AMR_WB), -+ STRING_ENTRY(AUDIO_FORMAT_AAC), -+ STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V1), -+ STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V2), -+ STRING_ENTRY(AUDIO_FORMAT_VORBIS), -+ STRING_ENTRY(AUDIO_FORMAT_MAIN_MASK), -+ STRING_ENTRY(AUDIO_FORMAT_SUB_MASK), -+#ifdef QCOM_HARDWARE -+ STRING_ENTRY(AUDIO_FORMAT_EVRC), -+ STRING_ENTRY(AUDIO_FORMAT_QCELP), -+ STRING_ENTRY(AUDIO_FORMAT_AC3), -+ STRING_ENTRY(AUDIO_FORMAT_AC3_PLUS), -+ STRING_ENTRY(AUDIO_FORMAT_DTS), -+ STRING_ENTRY(AUDIO_FORMAT_WMA), -+ STRING_ENTRY(AUDIO_FORMAT_WMA_PRO), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_ADIF), -+ STRING_ENTRY(AUDIO_FORMAT_EVRCB), -+ STRING_ENTRY(AUDIO_FORMAT_EVRCWB), -+ STRING_ENTRY(AUDIO_FORMAT_EAC3), -+ STRING_ENTRY(AUDIO_FORMAT_DTS_LBR), -+ STRING_ENTRY(AUDIO_FORMAT_AMR_WB_PLUS), -+#endif -+ STRING_ENTRY(AUDIO_FORMAT_PCM_16_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_8_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_32_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_8_24_BIT), -+ { 0, NULL } -+}; -+#undef STRING_ENTRY -+ -+#endif -Index: pulseaudio/src/modules/droid/droid-util-51.h -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/droid-util-51.h -@@ -0,0 +1,405 @@ -+/* -+ * Copyright (C) 2015 Jolla Ltd. -+ * -+ * Contact: Juho Hämäläinen -+ * -+ * These PulseAudio Modules are free software; you can redistribute -+ * it and/or modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation -+ * version 2.1 of the License. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -+ * USA. -+ */ -+ -+#ifndef _DROID_UTIL_V51_H_ -+#define _DROID_UTIL_V51_H_ -+ -+#define DROID_HAL 3 -+ -+#define DROID_HAVE_DRC -+ -+#ifdef QCOM_BSP -+#define DROID_AUDIO_HAL_USE_VSID -+#endif -+ -+#include -+#include -+ -+// PulseAudio value - Android value -+ -+uint32_t conversion_table_output_channel[][2] = { -+ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_OUT_MONO }, -+ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_OUT_FRONT_LEFT }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_OUT_FRONT_RIGHT}, -+ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_OUT_FRONT_CENTER }, -+ { PA_CHANNEL_POSITION_SUBWOOFER, AUDIO_CHANNEL_OUT_LOW_FREQUENCY }, -+ { PA_CHANNEL_POSITION_REAR_LEFT, AUDIO_CHANNEL_OUT_BACK_LEFT }, -+ { PA_CHANNEL_POSITION_REAR_RIGHT, AUDIO_CHANNEL_OUT_BACK_RIGHT }, -+ { PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER }, -+ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_OUT_BACK_CENTER }, -+ { PA_CHANNEL_POSITION_SIDE_LEFT, AUDIO_CHANNEL_OUT_SIDE_LEFT }, -+ { PA_CHANNEL_POSITION_SIDE_RIGHT, AUDIO_CHANNEL_OUT_SIDE_RIGHT }, -+ { PA_CHANNEL_POSITION_TOP_CENTER, AUDIO_CHANNEL_OUT_TOP_CENTER }, -+ { PA_CHANNEL_POSITION_TOP_FRONT_LEFT, AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT }, -+ { PA_CHANNEL_POSITION_TOP_FRONT_CENTER, AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER }, -+ { PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT }, -+ { PA_CHANNEL_POSITION_TOP_REAR_LEFT, AUDIO_CHANNEL_OUT_TOP_BACK_LEFT }, -+ { PA_CHANNEL_POSITION_TOP_REAR_CENTER, AUDIO_CHANNEL_OUT_TOP_BACK_CENTER }, -+ { PA_CHANNEL_POSITION_TOP_REAR_RIGHT, AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT } -+}; -+ -+uint32_t conversion_table_input_channel[][2] = { -+ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_MONO }, -+ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_IN_LEFT }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_IN_RIGHT}, -+ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_IN_FRONT }, -+ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_IN_BACK }, -+ /* Following are missing suitable counterparts on PulseAudio side. */ -+ { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_IN_LEFT_PROCESSED }, -+ { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_IN_RIGHT_PROCESSED }, -+ { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_IN_FRONT_PROCESSED }, -+ { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_IN_BACK_PROCESSED }, -+ { PA_CHANNEL_POSITION_SUBWOOFER, AUDIO_CHANNEL_IN_PRESSURE }, -+ { PA_CHANNEL_POSITION_AUX0, AUDIO_CHANNEL_IN_X_AXIS }, -+ { PA_CHANNEL_POSITION_AUX1, AUDIO_CHANNEL_IN_Y_AXIS }, -+ { PA_CHANNEL_POSITION_AUX2, AUDIO_CHANNEL_IN_Z_AXIS }, -+ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_VOICE_UPLINK }, -+ { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_VOICE_DNLINK } -+}; -+ -+uint32_t conversion_table_format[][2] = { -+ { PA_SAMPLE_U8, AUDIO_FORMAT_PCM_8_BIT }, -+ { PA_SAMPLE_S16LE, AUDIO_FORMAT_PCM_16_BIT }, -+ { PA_SAMPLE_S32LE, AUDIO_FORMAT_PCM_32_BIT }, -+ { PA_SAMPLE_S24LE, AUDIO_FORMAT_PCM_8_24_BIT } -+}; -+ -+uint32_t conversion_table_default_audio_source[][2] = { -+ { AUDIO_DEVICE_IN_COMMUNICATION, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_AMBIENT, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_BUILTIN_MIC, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_AUX_DIGITAL, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_VOICE_CALL, AUDIO_SOURCE_VOICE_CALL }, -+ { AUDIO_DEVICE_IN_BACK_MIC, AUDIO_SOURCE_MIC }, -+ { AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_SOURCE_REMOTE_SUBMIX }, -+#ifdef QCOM_HARDWARE -+ { AUDIO_DEVICE_IN_FM_RX, AUDIO_SOURCE_FM_RX }, -+ { AUDIO_DEVICE_IN_FM_RX_A2DP, AUDIO_SOURCE_FM_RX_A2DP }, -+#endif -+ { AUDIO_DEVICE_IN_ALL, AUDIO_SOURCE_DEFAULT } -+}; -+ -+struct string_conversion { -+ uint32_t value; -+ const char *str; -+}; -+ -+#if defined(STRING_ENTRY) -+#error STRING_ENTRY already defined somewhere, fix this lib. -+#endif -+#define STRING_ENTRY(str) { str, #str } -+/* Output devices */ -+struct string_conversion string_conversion_table_output_device[] = { -+ /* Each device listed here needs fancy name counterpart -+ * in string_conversion_table_output_device_fancy. */ -+ STRING_ENTRY(AUDIO_DEVICE_OUT_EARPIECE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_SPEAKER), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_WIRED_HEADPHONE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_AUX_DIGITAL), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_HDMI), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_USB_ACCESSORY), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_USB_DEVICE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_REMOTE_SUBMIX), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_TELEPHONY_TX), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_LINE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_HDMI_ARC), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_SPDIF), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_FM), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_AUX_LINE), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_SPEAKER_SAFE), -+#ifdef QCOM_HARDWARE -+ STRING_ENTRY(AUDIO_DEVICE_OUT_FM_TX), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_PROXY), -+#endif -+ /* Combination entries consisting of multiple devices defined above. -+ * These don't require counterpart in string_conversion_table_output_device_fancy. */ -+ STRING_ENTRY(AUDIO_DEVICE_OUT_DEFAULT), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_A2DP), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_SCO), -+ STRING_ENTRY(AUDIO_DEVICE_OUT_ALL_USB), -+ { 0, NULL } -+}; -+ -+struct string_conversion string_conversion_table_output_device_fancy[] = { -+ { AUDIO_DEVICE_OUT_EARPIECE, "output-earpiece" }, -+ { AUDIO_DEVICE_OUT_SPEAKER, "output-speaker" }, -+ { AUDIO_DEVICE_OUT_SPEAKER -+ | AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-speaker+wired_headphone" }, -+ { AUDIO_DEVICE_OUT_WIRED_HEADSET, "output-wired_headset" }, -+ { AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-wired_headphone" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO, "output-bluetooth_sco" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, "output-sco_headset" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, "output-sco_carkit" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, "output-a2dp" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, "output-a2dp_headphones" }, -+ { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, "output-a2dp_speaker" }, -+ { AUDIO_DEVICE_OUT_AUX_DIGITAL, "output-aux_digital" }, -+ { AUDIO_DEVICE_OUT_HDMI, "output-hdmi" }, -+ { AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, "output-analog_dock_headset" }, -+ { AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, "output-digital_dock_headset" }, -+ { AUDIO_DEVICE_OUT_USB_ACCESSORY, "output-usb_accessory" }, -+ { AUDIO_DEVICE_OUT_USB_DEVICE, "output-usb_device" }, -+ { AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "output-remote_submix" }, -+ { AUDIO_DEVICE_OUT_TELEPHONY_TX, "output-telephony" }, -+ { AUDIO_DEVICE_OUT_LINE, "output-line" }, -+ { AUDIO_DEVICE_OUT_HDMI_ARC, "output-hdmi_arc" }, -+ { AUDIO_DEVICE_OUT_SPDIF, "output-spdif" }, -+ { AUDIO_DEVICE_OUT_FM, "output-fm" }, -+ { AUDIO_DEVICE_OUT_AUX_LINE, "output-aux_line" }, -+ { AUDIO_DEVICE_OUT_SPEAKER_SAFE, "output-speaker_safe" }, -+#ifdef QCOM_HARDWARE -+ { AUDIO_DEVICE_OUT_FM_TX, "output-fm_tx" }, -+ { AUDIO_DEVICE_OUT_PROXY, "output-proxy" }, -+#endif -+ { 0, NULL } -+}; -+ -+/* Input devices */ -+struct string_conversion string_conversion_table_input_device[] = { -+ /* Each device listed here needs fancy name counterpart -+ * in string_conversion_table_input_device_fancy. */ -+ STRING_ENTRY(AUDIO_DEVICE_IN_COMMUNICATION), -+ STRING_ENTRY(AUDIO_DEVICE_IN_AMBIENT), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BUILTIN_MIC), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_WIRED_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_AUX_DIGITAL), -+ STRING_ENTRY(AUDIO_DEVICE_IN_HDMI), -+ STRING_ENTRY(AUDIO_DEVICE_IN_VOICE_CALL), -+ STRING_ENTRY(AUDIO_DEVICE_IN_TELEPHONY_RX), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BACK_MIC), -+ STRING_ENTRY(AUDIO_DEVICE_IN_REMOTE_SUBMIX), -+ STRING_ENTRY(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET), -+ STRING_ENTRY(AUDIO_DEVICE_IN_USB_ACCESSORY), -+ STRING_ENTRY(AUDIO_DEVICE_IN_USB_DEVICE), -+ STRING_ENTRY(AUDIO_DEVICE_IN_FM_TUNER), -+ STRING_ENTRY(AUDIO_DEVICE_IN_TV_TUNER), -+ STRING_ENTRY(AUDIO_DEVICE_IN_LINE), -+ STRING_ENTRY(AUDIO_DEVICE_IN_SPDIF), -+ STRING_ENTRY(AUDIO_DEVICE_IN_BLUETOOTH_A2DP), -+ STRING_ENTRY(AUDIO_DEVICE_IN_LOOPBACK), -+#ifdef QCOM_HARDWARE -+ STRING_ENTRY(AUDIO_DEVICE_IN_PROXY), -+ STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX), -+ STRING_ENTRY(AUDIO_DEVICE_IN_FM_RX_A2DP), -+#endif -+ STRING_ENTRY(AUDIO_DEVICE_IN_DEFAULT), -+ /* Combination entries consisting of multiple devices defined above. -+ * These don't require counterpart in string_conversion_table_input_device_fancy. */ -+ STRING_ENTRY(AUDIO_DEVICE_IN_ALL), -+ STRING_ENTRY(AUDIO_DEVICE_IN_ALL_SCO), -+ STRING_ENTRY(AUDIO_DEVICE_IN_ALL_USB), -+ { 0, NULL } -+}; -+ -+struct string_conversion string_conversion_table_input_device_fancy[] = { -+ { AUDIO_DEVICE_IN_COMMUNICATION, "input-communication" }, -+ { AUDIO_DEVICE_IN_AMBIENT, "input-ambient" }, -+ { AUDIO_DEVICE_IN_BUILTIN_MIC, "input-builtin_mic" }, -+ { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, "input-bluetooth_sco_headset" }, -+ { AUDIO_DEVICE_IN_WIRED_HEADSET, "input-wired_headset" }, -+ { AUDIO_DEVICE_IN_AUX_DIGITAL, "input-aux_digital" }, -+ { AUDIO_DEVICE_IN_HDMI, "input-hdmi" }, -+ { AUDIO_DEVICE_IN_VOICE_CALL, "input-voice_call" }, -+ { AUDIO_DEVICE_IN_TELEPHONY_RX, "input-telephony" }, -+ { AUDIO_DEVICE_IN_BACK_MIC, "input-back_mic" }, -+ { AUDIO_DEVICE_IN_REMOTE_SUBMIX, "input-remote_submix" }, -+ { AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET, "input-analog_dock_headset" }, -+ { AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET, "input-digital_dock_headset" }, -+ { AUDIO_DEVICE_IN_USB_ACCESSORY, "input-usb_accessory" }, -+ { AUDIO_DEVICE_IN_USB_DEVICE, "input-usb_device" }, -+ { AUDIO_DEVICE_IN_FM_TUNER, "input-fm_tuner" }, -+ { AUDIO_DEVICE_IN_TV_TUNER, "input-tv_tuner" }, -+ { AUDIO_DEVICE_IN_LINE, "input-line" }, -+ { AUDIO_DEVICE_IN_SPDIF, "input-spdif" }, -+ { AUDIO_DEVICE_IN_BLUETOOTH_A2DP, "input-bluetooth_a2dp" }, -+ { AUDIO_DEVICE_IN_LOOPBACK, "input-loopback" }, -+#ifdef QCOM_HARDWARE -+ { AUDIO_DEVICE_IN_PROXY, "input-proxy" }, -+ { AUDIO_DEVICE_IN_FM_RX, "input-fm_rx" }, -+ { AUDIO_DEVICE_IN_FM_RX_A2DP, "input-fm_rx_a2dp" }, -+#endif -+ { AUDIO_DEVICE_IN_DEFAULT, "input-default" }, -+ { 0, NULL } -+}; -+ -+struct string_conversion string_conversion_table_audio_source_fancy[] = { -+ { AUDIO_SOURCE_DEFAULT, "default" }, -+ { AUDIO_SOURCE_MIC, "mic" }, -+ { AUDIO_SOURCE_VOICE_UPLINK, "voice uplink" }, -+ { AUDIO_SOURCE_VOICE_DOWNLINK, "voice downlink" }, -+ { AUDIO_SOURCE_VOICE_CALL, "voice call" }, -+ { AUDIO_SOURCE_CAMCORDER, "camcorder" }, -+ { AUDIO_SOURCE_VOICE_RECOGNITION, "voice recognition" }, -+ { AUDIO_SOURCE_VOICE_COMMUNICATION, "voice communication" }, -+ { AUDIO_SOURCE_REMOTE_SUBMIX, "remote submix" }, -+ { AUDIO_SOURCE_FM_TUNER, "fm tuner" }, -+#ifdef QCOM_HARDWARE -+ { AUDIO_SOURCE_FM_RX, "fm rx" }, -+ { AUDIO_SOURCE_FM_RX_A2DP, "fm rx a2dp" }, -+#endif -+ { (uint32_t)-1, NULL } -+}; -+ -+/* Flags */ -+struct string_conversion string_conversion_table_output_flag[] = { -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_NONE), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_DIRECT), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_PRIMARY), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_FAST), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_DEEP_BUFFER), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_NON_BLOCKING), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_HW_AV_SYNC), -+#ifdef QCOM_HARDWARE -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_VOIP_RX), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_INCALL_MUSIC), -+ STRING_ENTRY(AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH), -+#endif -+ { 0, NULL } -+}; -+ -+struct string_conversion string_conversion_table_input_flag[] = { -+ STRING_ENTRY(AUDIO_INPUT_FLAG_NONE), -+ STRING_ENTRY(AUDIO_INPUT_FLAG_FAST), -+ STRING_ENTRY(AUDIO_INPUT_FLAG_HW_HOTWORD), -+ { 0, NULL } -+}; -+ -+/* Channels */ -+struct string_conversion string_conversion_table_output_channels[] = { -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_LOW_FREQUENCY), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_BACK_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_SIDE_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_STEREO), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_QUAD), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_5POINT1), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_5POINT1_BACK), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_5POINT1_SIDE), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_7POINT1), -+ STRING_ENTRY(AUDIO_CHANNEL_OUT_ALL), -+ { 0, NULL } -+}; -+struct string_conversion string_conversion_table_input_channels[] = { -+ STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_BACK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_LEFT_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_RIGHT_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_BACK_PROCESSED), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_PRESSURE), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_X_AXIS), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_Y_AXIS), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_Z_AXIS), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_UPLINK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_VOICE_DNLINK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_MONO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_STEREO), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_ALL), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_FRONT_BACK), -+ STRING_ENTRY(AUDIO_CHANNEL_IN_ALL), -+ { 0, NULL } -+}; -+ -+/* Formats */ -+struct string_conversion string_conversion_table_format[] = { -+ STRING_ENTRY(AUDIO_FORMAT_DEFAULT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM), -+ STRING_ENTRY(AUDIO_FORMAT_MP3), -+ STRING_ENTRY(AUDIO_FORMAT_AMR_NB), -+ STRING_ENTRY(AUDIO_FORMAT_AMR_WB), -+ STRING_ENTRY(AUDIO_FORMAT_AAC), -+ STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V1), -+ STRING_ENTRY(AUDIO_FORMAT_HE_AAC_V2), -+ STRING_ENTRY(AUDIO_FORMAT_VORBIS), -+ STRING_ENTRY(AUDIO_FORMAT_OPUS), -+ STRING_ENTRY(AUDIO_FORMAT_AC3), -+ STRING_ENTRY(AUDIO_FORMAT_E_AC3), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_16_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_8_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_32_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_8_24_BIT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_FLOAT), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_24_BIT_PACKED), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_MAIN), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_LC), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_SSR), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_LTP), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_HE_V1), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_SCALABLE), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_ERLC), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_LD), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_HE_V2), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_ELD), -+#ifdef QCOM_HARDWARE -+ STRING_ENTRY(AUDIO_FORMAT_EVRC), -+ STRING_ENTRY(AUDIO_FORMAT_QCELP), -+ STRING_ENTRY(AUDIO_FORMAT_DTS), -+ STRING_ENTRY(AUDIO_FORMAT_WMA), -+ STRING_ENTRY(AUDIO_FORMAT_WMA_PRO), -+ STRING_ENTRY(AUDIO_FORMAT_AAC_ADIF), -+ STRING_ENTRY(AUDIO_FORMAT_EVRCB), -+ STRING_ENTRY(AUDIO_FORMAT_EVRCWB), -+ STRING_ENTRY(AUDIO_FORMAT_DTS_LBR), -+ STRING_ENTRY(AUDIO_FORMAT_AMR_WB_PLUS), -+ STRING_ENTRY(AUDIO_FORMAT_MP2), -+ STRING_ENTRY(AUDIO_FORMAT_EVRCNW), -+ STRING_ENTRY(AUDIO_FORMAT_PCM_OFFLOAD), -+ STRING_ENTRY(AUDIO_FORMAT_FLAC), -+ STRING_ENTRY(AUDIO_FORMAT_E_AC3_JOC), -+#endif -+ { 0, NULL } -+}; -+#undef STRING_ENTRY -+ -+#endif -Index: pulseaudio/src/modules/droid/droid-util.c -=================================================================== ---- pulseaudio.orig/src/modules/droid/droid-util.c -+++ pulseaudio/src/modules/droid/droid-util.c -@@ -54,26 +54,10 @@ - #include - #include - #include -- --#include --#include -+#include - - #include "droid-util.h" - --#include -- --#ifndef ANDROID_VERSION_MAJOR --#error "ANDROID_VERSION_* not defined." --#endif -- --#if ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 1 --#include "droid-util-41qc.h" --#elif ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR >= 2 --#include "droid-util-42.h" --#else --#error "No valid ANDROID_VERSION found." --#endif -- - #define CONVERT_FUNC(TABL) \ - bool pa_convert_ ## TABL (uint32_t value, pa_conversion_field_t field, uint32_t *to_value) { \ - for (unsigned int i = 0; i < sizeof( conversion_table_ ## TABL )/(sizeof(uint32_t)*2); i++) { \ -@@ -94,12 +78,15 @@ CONVERT_FUNC(input_channel); - - #define DEFAULT_PRIORITY (100) - -+/* Section defining custom global configuration variables. */ -+#define GLOBAL_CONFIG_EXT_TAG "custom_properties" -+ -+static void droid_port_free(pa_droid_port *p); -+ - static bool string_convert_num_to_str(const struct string_conversion *list, const uint32_t value, const char **to_str) { - pa_assert(list); - pa_assert(to_str); - -- pa_log_debug("Trying to convert %x to string.", value); -- - for (unsigned int i = 0; list[i].str; i++) { - if (list[i].value == value) { - *to_str = list[i].str; -@@ -114,8 +101,6 @@ static bool string_convert_str_to_num(co - pa_assert(str); - pa_assert(to_value); - -- pa_log_debug("Trying to convert %s to num.", str); -- - for (unsigned int i = 0; list[i].str; i++) { - if (pa_streq(list[i].str, str)) { - *to_value = list[i].value; -@@ -125,31 +110,17 @@ static bool string_convert_str_to_num(co - return false; - } - --static bool check_port_availability(const char *port) { -- pa_assert(port); -- -- pa_log_debug("Checking availability for port '%s'", port); -- -- for (unsigned int i = 0; port_availability[i]; i++) { -- if (pa_streq(port_availability[i], port)) { -- return true; -- } -- } -- -- return false; --} -- - static char *list_string(struct string_conversion *list, uint32_t flags) { - char *str = NULL; - char *tmp; - --#ifdef HAL_V2 -+#if DROID_HAL >= 2 - if (flags & AUDIO_DEVICE_BIT_IN) - flags &= ~AUDIO_DEVICE_BIT_IN; - #endif - - for (unsigned int i = 0; list[i].str; i++) { --#ifdef HAL_V2 -+#if DROID_HAL >= 2 - if (list[i].value & AUDIO_DEVICE_BIT_IN) { - if (popcount(list[i].value & ~AUDIO_DEVICE_BIT_IN) != 1) - continue; -@@ -172,13 +143,6 @@ static char *list_string(struct string_c - return str; - } - --static void droid_port_free(pa_droid_port *p) { -- pa_assert(p); -- -- pa_xfree(p->name); -- pa_xfree(p->description); -- pa_xfree(p); --} - - /* Output device */ - bool pa_string_convert_output_device_num_to_str(audio_devices_t value, const char **to_str) { -@@ -208,39 +172,67 @@ char *pa_list_string_input_device(audio_ - - /* Flags */ - bool pa_string_convert_flag_num_to_str(audio_output_flags_t value, const char **to_str) { -- return string_convert_num_to_str(string_conversion_table_flag, (uint32_t) value, to_str); -+ return string_convert_num_to_str(string_conversion_table_output_flag, (uint32_t) value, to_str); - } - - bool pa_string_convert_flag_str_to_num(const char *str, audio_output_flags_t *to_value) { -- return string_convert_str_to_num(string_conversion_table_flag, str, (uint32_t*) to_value); -+ return string_convert_str_to_num(string_conversion_table_output_flag, str, (uint32_t*) to_value); - } - - char *pa_list_string_flags(audio_output_flags_t flags) { -- return list_string(string_conversion_table_flag, flags); -+ return list_string(string_conversion_table_output_flag, flags); -+} -+ -+bool pa_input_device_default_audio_source(audio_devices_t input_device, audio_source_t *default_source) -+{ -+#if DROID_HAL >= 2 -+ input_device &= ~AUDIO_DEVICE_BIT_IN; -+#endif -+ -+ /* Note converting HAL values to different HAL values! */ -+ for (unsigned int i = 0; i < sizeof(conversion_table_default_audio_source) / (sizeof(uint32_t) * 2); i++) { -+ if (conversion_table_default_audio_source[i][0] & input_device) { -+ *default_source = conversion_table_default_audio_source[i][1]; -+ return true; -+ } -+ } -+ return false; - } - - /* Config parser */ - - #define WHITESPACE "\n\r \t" - --static int parse_list(const struct string_conversion *table, const char *str, uint32_t *dst) { -+static int parse_list(const struct string_conversion *table, -+ const char *str, -+ uint32_t *dst, -+ char **unknown_entries) { - int count = 0; - char *entry; -+ char *unknown = NULL; - const char *state = NULL; - - pa_assert(table); - pa_assert(str); - pa_assert(dst); -+ pa_assert(unknown_entries); - - *dst = 0; -+ *unknown_entries = NULL; - - while ((entry = pa_split(str, "|", &state))) { - uint32_t d = 0; - - if (!string_convert_str_to_num(table, entry, &d)) { -- pa_log("Unknown entry %s", entry); -- pa_xfree(entry); -- return -1; -+ if (*unknown_entries) { -+ unknown = pa_sprintf_malloc("%s|%s", *unknown_entries, entry); -+ pa_xfree(*unknown_entries); -+ pa_xfree(entry); -+ } else -+ unknown = entry; -+ -+ *unknown_entries = unknown; -+ continue; - } - - *dst |= d; -@@ -252,24 +244,34 @@ static int parse_list(const struct strin - return count; - } - --static bool parse_sampling_rates(const char *str, uint32_t sampling_rates[32]) { -+static bool parse_sampling_rates(const char *fn, const unsigned ln, -+ const char *str, uint32_t sampling_rates[32]) { -+ pa_assert(fn); -+ pa_assert(str); -+ - char *entry; - const char *state = NULL; - -- pa_assert(str); -- - uint32_t pos = 0; - while ((entry = pa_split(str, "|", &state))) { - int32_t val; - -+#if DROID_HAL >= 3 -+ if (pos == 0 && pa_streq(entry, "dynamic")) { -+ sampling_rates[pos++] = (uint32_t) -1; -+ pa_xfree(entry); -+ break; -+ } -+#endif -+ - if (pos == AUDIO_MAX_SAMPLING_RATES) { -- pa_log("Too many sample rate entries (> %d)", AUDIO_MAX_SAMPLING_RATES); -+ pa_log("[%s:%u] Too many sample rate entries (> %d)", fn, ln, AUDIO_MAX_SAMPLING_RATES); - pa_xfree(entry); - return false; - } - - if (pa_atoi(entry, &val) < 0) { -- pa_log("Bad sample rate value %s", entry); -+ pa_log("[%s:%u] Bad sample rate value %s", fn, ln, entry); - pa_xfree(entry); - return false; - } -@@ -285,14 +287,58 @@ static bool parse_sampling_rates(const c - return true; - } - --static bool parse_formats(const char *str, audio_format_t *formats) { -+static bool check_and_log(const char *fn, const unsigned ln, const char *field, -+ const int count, const char *str, char *unknown, -+ const bool must_have_all) { -+ bool fail; -+ -+ pa_assert(fn); -+ pa_assert(field); -+ pa_assert(str); -+ -+ fail = must_have_all && unknown; -+ -+ if (unknown) { -+ pa_log_warn("[%s:%u] Unknown %s entries: %s", fn, ln, field, unknown); -+ pa_xfree(unknown); -+ } -+ -+ if (count == 0 || fail) { -+ pa_log("[%s:%u] Failed to parse %s (%s).", fn, ln, field, str); -+ return false; -+ } -+ -+ return true; -+} -+ -+static bool parse_formats(const char *fn, const unsigned ln, -+ const char *str, audio_format_t *formats) { -+ int count; -+ char *unknown = NULL; -+ -+ pa_assert(fn); - pa_assert(str); - pa_assert(formats); - -- return parse_list(string_conversion_table_format, str, formats) > 0; -+#if DROID_HAL >= 3 -+ /* Needs to be probed later */ -+ if (pa_streq(str, "dynamic")) { -+ *formats = 0; -+ return true; -+ } -+#endif -+ -+ count = parse_list(string_conversion_table_format, str, formats, &unknown); -+ -+ return check_and_log(fn, ln, "formats", count, str, unknown, false); - } - --static int parse_channels(const char *str, bool in_output, audio_channel_mask_t *channels) { -+static int parse_channels(const char *fn, const unsigned ln, -+ const char *str, bool in_output, audio_channel_mask_t *channels) { -+ int count; -+ char *unknown = NULL; -+ -+ pa_assert(fn); - pa_assert(str); - pa_assert(channels); - -@@ -302,41 +348,77 @@ static int parse_channels(const char *st - return true; - } - -- if (in_output) -- return parse_list(string_conversion_table_output_channels, str, channels); -- else -- return parse_list(string_conversion_table_input_channels, str, channels); -+ count = parse_list(in_output ? string_conversion_table_output_channels -+ : string_conversion_table_input_channels, -+ str, channels, &unknown); -+ -+ return check_and_log(fn, ln, in_output ? "output channel_masks" : "input channel_masks", -+ count, str, unknown, false); - } - --static bool parse_devices(const char *str, bool in_output, audio_devices_t *devices) { -+static bool parse_devices(const char *fn, const unsigned ln, -+ const char *str, bool in_output, audio_devices_t *devices, bool must_have_all) { -+ int count; -+ char *unknown = NULL; -+ -+ pa_assert(fn); - pa_assert(str); - pa_assert(devices); - -- if (in_output) -- return parse_list(string_conversion_table_output_device, str, devices) > 0; -- else -- return parse_list(string_conversion_table_input_device, str, devices) > 0; -+ count = parse_list(in_output ? string_conversion_table_output_device -+ : string_conversion_table_input_device, -+ str, devices, &unknown); -+ -+ return check_and_log(fn, ln, in_output ? "output devices" : "input devices", -+ count, str, unknown, must_have_all); -+} -+ -+static bool parse_output_flags(const char *fn, const unsigned ln, -+ const char *str, audio_output_flags_t *flags) { -+ int count; -+ char *unknown = NULL; -+ -+ pa_assert(fn); -+ pa_assert(str); -+ pa_assert(flags); -+ -+ count = parse_list(string_conversion_table_output_flag, str, flags, &unknown); -+ -+ return check_and_log(fn, ln, "flags", count, str, unknown, false); - } - --static bool parse_flags(const char *str, audio_output_flags_t *flags) { -+#if DROID_HAL >= 3 -+static bool parse_input_flags(const char *fn, const unsigned ln, -+ const char *str, audio_input_flags_t *flags) { -+ int count; -+ char *unknown = NULL; -+ -+ pa_assert(fn); - pa_assert(str); - pa_assert(flags); - -- return parse_list(string_conversion_table_flag, str, flags) > 0; -+ count = parse_list(string_conversion_table_input_flag, str, flags, &unknown); -+ -+ return check_and_log(fn, ln, "flags", count, str, unknown, false); - } -+#endif -+ -+#define MAX_LINE_LENGTH (1024) - - bool pa_parse_droid_audio_config(const char *filename, pa_droid_config_audio *config) { - FILE *f; -- int n = 0; -+ unsigned n = 0; - bool ret = true; -+ char *full_line = NULL; - - enum config_loc { -- IN_ROOT = 0, -- IN_GLOBAL = 1, -- IN_HW_MODULES = 1, -- IN_MODULE = 2, -- IN_OUTPUT_INPUT = 3, -- IN_CONFIG = 4 -+ IN_ROOT = 0, -+ IN_GLOBAL = 1, -+ IN_HW_MODULES = 2, -+ IN_MODULE = 3, -+ IN_OUTPUT_INPUT = 4, -+ IN_CONFIG = 5, -+ IN_GLOBAL_EXT = 6, - } loc = IN_ROOT; - - -@@ -362,29 +444,39 @@ bool pa_parse_droid_audio_config(const c - - pa_lock_fd(fileno(f), 1); - -+ full_line = pa_xmalloc0(sizeof(char) * MAX_LINE_LENGTH); -+ - while (!feof(f)) { -- char ln[512]; -- char *d, *v, *val; -+ char *ln, *d, *v, *val; - -- if (!fgets(ln, sizeof(ln), f)) -+ if (!fgets(full_line, MAX_LINE_LENGTH, f)) - break; - - n++; - -- pa_strip_nl(ln); -+ pa_strip_nl(full_line); - -- if (ln[0] == '#' || !*ln ) -+ if (!*full_line) - continue; - -+ ln = full_line + strspn(full_line, WHITESPACE); -+ -+ if (ln[0] == '#') -+ continue; -+ -+ v = ln; -+ d = v + strcspn(v, WHITESPACE); -+ -+ val = d + strspn(d, WHITESPACE); -+ d[0] = '\0'; -+ d = val + strcspn(val, WHITESPACE); -+ d[0] = '\0'; -+ - /* Enter section */ -- if (ln[strlen(ln)-1] == '{') { -- d = ln+strspn(ln, WHITESPACE); -- v = d; -- d = v+strcspn(v, WHITESPACE); -- d[0] = '\0'; -+ if (pa_streq(val, "{")) { - - if (!*v) { -- pa_log(__FILE__ ": [%s:%u] failed to parse line - too few words", filename, n); -+ pa_log("[%s:%u] failed to parse line - too few words", filename, n); - goto finish; - } - -@@ -397,7 +489,17 @@ bool pa_parse_droid_audio_config(const c - else if (pa_streq(v, AUDIO_HW_MODULE_TAG)) - loc = IN_HW_MODULES; - else { -- pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown field (%s)", filename, n, v); -+ pa_log("[%s:%u] failed to parse line - unknown field (%s)", filename, n, v); -+ ret = false; -+ goto finish; -+ } -+ break; -+ -+ case IN_GLOBAL: -+ if (pa_streq(v, GLOBAL_CONFIG_EXT_TAG)) -+ loc = IN_GLOBAL_EXT; -+ else { -+ pa_log("[%s:%u] failed to parse line - unknown section (%s)", filename, n, v); - ret = false; - goto finish; - } -@@ -420,7 +522,7 @@ bool pa_parse_droid_audio_config(const c - loc = IN_OUTPUT_INPUT; - in_output = false; - } else { -- pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown field (%s)", filename, n, v); -+ pa_log("[%s:%u] failed to parse line - unknown field (%s)", filename, n, v); - ret = false; - goto finish; - } -@@ -447,7 +549,12 @@ bool pa_parse_droid_audio_config(const c - break; - - case IN_CONFIG: -- pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown field in config (%s)", filename, n, v); -+ pa_log("[%s:%u] failed to parse line - unknown field in config (%s)", filename, n, v); -+ ret = false; -+ goto finish; -+ -+ default: -+ pa_log("[%s:%u] failed to parse line - unknown section (%s)", filename, n, v); - ret = false; - goto finish; - } -@@ -456,108 +563,125 @@ bool pa_parse_droid_audio_config(const c - } - - /* Exit section */ -- if (ln[strlen(ln)-1] == '}') { -- if (loc == IN_ROOT) { -- pa_log(__FILE__ ": [%s:%u] failed to parse line - extra closing bracket", filename, n); -- ret = false; -- goto finish; -- } -+ if (pa_streq(v, "}")) { -+ switch (loc) { -+ case IN_ROOT: -+ pa_log("[%s:%u] failed to parse line - extra closing bracket", filename, n); -+ ret = false; -+ goto finish; - -- loc--; -- if (loc == IN_MODULE) { -- if (in_output) -- output = NULL; -- else -- input = NULL; -- } -- if (loc == IN_ROOT) -- module = NULL; -+ case IN_HW_MODULES: -+ module = NULL; -+ /* fall through */ -+ case IN_GLOBAL: -+ loc = IN_ROOT; -+ break; - -+ case IN_OUTPUT_INPUT: -+ if (in_output) -+ output = NULL; -+ else -+ input = NULL; -+ /* fall through */ -+ case IN_MODULE: -+ /* fall through */ -+ case IN_CONFIG: -+ /* fall through */ -+ case IN_GLOBAL_EXT: -+ loc--; -+ break; -+ } - in_global = false; - - continue; - } - -- /* Parse global configuration */ -- if (in_global) { -+ if (loc == IN_GLOBAL || -+ loc == IN_GLOBAL_EXT || -+ loc == IN_CONFIG) { -+ - bool success = false; - -- d = ln+strspn(ln, WHITESPACE); -- v = d; -- d = v+strcspn(v, WHITESPACE); -- -- val = d+strspn(d, WHITESPACE); -- d[0] = '\0'; -- d = val+strcspn(val, WHITESPACE); -- d[0] = '\0'; -- -- if (pa_streq(v, ATTACHED_OUTPUT_DEVICES_TAG)) -- success = parse_devices(val, true, &config->global_config.attached_output_devices); -- else if (pa_streq(v, DEFAULT_OUTPUT_DEVICE_TAG)) -- success = parse_devices(val, true, &config->global_config.default_output_device); -- else if (pa_streq(v, ATTACHED_INPUT_DEVICES_TAG)) -- success = parse_devices(val, false, &config->global_config.attached_input_devices); -- else if (pa_streq(v, SPEAKER_DRC_ENABLED_TAG)) { -- pa_log(__FILE__ ": speaker drc is not yet supported, skipping", filename); -- success = true; -- } else { -- pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown config entry %s", filename, n, v); -- success = false; -- } -+ if (loc == IN_GLOBAL) { - -- if (!success) { -- ret = false; -- goto finish; -- } -- } -+ /* Parse global configuration */ - -- /* Parse per-output or per-input configuration */ -- if (loc == IN_CONFIG) { -- bool success = false; -+ if (pa_streq(v, ATTACHED_OUTPUT_DEVICES_TAG)) { -+ success = parse_devices(filename, n, val, true, -+ &config->global_config.attached_output_devices, false); -+ } -+ else if (pa_streq(v, DEFAULT_OUTPUT_DEVICE_TAG)) { -+ success = parse_devices(filename, n, val, true, -+ &config->global_config.default_output_device, true); -+ } -+ else if (pa_streq(v, ATTACHED_INPUT_DEVICES_TAG)) { -+ success = parse_devices(filename, n, val, false, -+ &config->global_config.attached_input_devices, false); -+ } -+#ifdef DROID_HAVE_DRC -+ // SPEAKER_DRC_ENABLED_TAG is only from Android v4.4 -+ else if (pa_streq(v, SPEAKER_DRC_ENABLED_TAG)) -+ /* TODO - Add support for dynamic range control */ -+ success = true; /* Do not fail while parsing speaker_drc_enabled entry */ -+#endif -+ else { -+ pa_log("[%s:%u] failed to parse line - unknown config entry %s", filename, n, v); -+ success = false; -+ } - -- pa_assert(module); -+ } else if (loc == IN_GLOBAL_EXT) { - -- d = ln+strspn(ln, WHITESPACE); -- v = d; -- d = v+strcspn(v, WHITESPACE); -+ /* Parse custom global configuration -+ * For now just log all custom variables, don't do -+ * anything with the values. -+ * TODO: Store custom values somehow */ - -- val = d+strspn(d, WHITESPACE); -- d[0] = '\0'; -- d = val+strcspn(val, WHITESPACE); -- d[0] = '\0'; -+ pa_log_debug("[%s:%u] TODO custom variable: %s = %s", filename, n, v, val); -+ success = true; - -+ } else if (loc == IN_CONFIG) { - -- if ((in_output && !output) || (!in_output && !input)) { -- pa_log(__FILE__ ": [%s:%u] failed to parse line", filename, n); -- ret = false; -- goto finish; -- } -+ /* Parse per-output or per-input configuration */ - -- if (pa_streq(v, SAMPLING_RATES_TAG)) -- success = parse_sampling_rates(val, in_output ? output->sampling_rates : input->sampling_rates); -- else if (pa_streq(v, FORMATS_TAG)) -- success = parse_formats(val, in_output ? &output->formats : &input->formats); -- else if (pa_streq(v, CHANNELS_TAG)) { -- if (in_output) -- success = (parse_channels(val, true, &output->channel_masks) > 0); -- else -- success = (parse_channels(val, false, &input->channel_masks) > 0); -- } else if (pa_streq(v, DEVICES_TAG)) { -- if (in_output) -- success = parse_devices(val, true, &output->devices); -- else -- success = parse_devices(val, false, &input->devices); -- } else if (pa_streq(v, FLAGS_TAG)) { -- if (in_output) -- success = parse_flags(val, &output->flags); -- else { -- pa_log(__FILE__ ": [%s:%u] failed to parse line - output flags inside input definition", filename, n); -+ if ((in_output && !output) || (!in_output && !input)) { -+ pa_log("[%s:%u] failed to parse line", filename, n); -+ ret = false; -+ goto finish; -+ } -+ -+ if (pa_streq(v, SAMPLING_RATES_TAG)) -+ success = parse_sampling_rates(filename, n, val, -+ in_output ? output->sampling_rates : input->sampling_rates); -+ else if (pa_streq(v, FORMATS_TAG)) -+ success = parse_formats(filename, n, val, in_output ? &output->formats : &input->formats); -+ else if (pa_streq(v, CHANNELS_TAG)) { -+ if (in_output) -+ success = parse_channels(filename, n, val, true, &output->channel_masks); -+ else -+ success = parse_channels(filename, n, val, false, &input->channel_masks); -+ } else if (pa_streq(v, DEVICES_TAG)) { -+ if (in_output) -+ success = parse_devices(filename, n, val, true, &output->devices, false); -+ else -+ success = parse_devices(filename, n, val, false, &input->devices, false); -+ } else if (pa_streq(v, FLAGS_TAG)) { -+ if (in_output) -+ success = parse_output_flags(filename, n, val, &output->flags); -+ else { -+#if DROID_HAL >= 3 -+ success = parse_input_flags(filename, n, val, &input->flags); -+#else -+ pa_log("[%s:%u] failed to parse line - output flags inside input definition", filename, n); -+ success = false; -+#endif -+ } -+ } else { -+ pa_log("[%s:%u] failed to parse line - unknown config entry %s", filename, n, v); - success = false; - } -- } else { -- pa_log(__FILE__ ": [%s:%u] failed to parse line - unknown config entry %s", filename, n, v); -- success = false; -- } -+ -+ } else -+ pa_assert_not_reached(); - - if (!success) { - ret = false; -@@ -574,6 +698,8 @@ finish: - fclose(f); - } - -+ pa_xfree(full_line); -+ - return ret; - } - -@@ -614,21 +740,50 @@ const pa_droid_config_hw_module *pa_droi - return NULL; - } - --pa_droid_profile *pa_droid_profile_new(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input) { -+static pa_droid_profile *profile_new(pa_droid_profile_set *ps, -+ const pa_droid_config_hw_module *module, -+ const char *name, -+ const char *description) { - pa_droid_profile *p; - - pa_assert(ps); -- pa_assert(output); -+ pa_assert(module); -+ pa_assert(name); -+ pa_assert(description); - - p = pa_xnew0(pa_droid_profile, 1); - p->profile_set = ps; -- p->module = output->module; -- p->name = pa_sprintf_malloc("%s%s%s", output->name, input ? "-" : "", input ? input->name : ""); -- p->description = pa_sprintf_malloc("%s output%s%s%s", output->name, -- input ? " and " : "", -- input ? input->name : "", -- input ? " input." : ""); -+ p->module = module; -+ p->name = pa_xstrdup(name); -+ p->description = pa_xstrdup(description); - p->priority = DEFAULT_PRIORITY; -+ -+ p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); -+ p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); -+ -+ pa_hashmap_put(ps->profiles, p->name, p); -+ -+ return p; -+} -+ -+pa_droid_profile *pa_droid_profile_new(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input) { -+ pa_droid_profile *p; -+ char *name; -+ char *description; -+ -+ pa_assert(ps); -+ pa_assert(output); -+ -+ name = pa_sprintf_malloc("%s%s%s", output->name, input ? "-" : "", input ? input->name : ""); -+ description = pa_sprintf_malloc("%s output%s%s%s", output->name, -+ input ? " and " : "", -+ input ? input->name : "", -+ input ? " input." : ""); -+ -+ p = profile_new(ps, output->module, name, description); -+ pa_xfree(name); -+ pa_xfree(description); -+ - if (pa_streq(output->name, "primary")) { - p->priority += DEFAULT_PRIORITY; - -@@ -637,16 +792,24 @@ pa_droid_profile *pa_droid_profile_new(p - } - - if (output) -- p->output = pa_droid_mapping_get(ps, PA_DIRECTION_OUTPUT, output); -+ pa_idxset_put(p->output_mappings, pa_droid_mapping_get(ps, PA_DIRECTION_OUTPUT, output), NULL); - if (input) -- p->input = pa_droid_mapping_get(ps, PA_DIRECTION_INPUT, input); -- -- pa_hashmap_put(ps->profiles, p->name, p); -+ pa_idxset_put(p->input_mappings, pa_droid_mapping_get(ps, PA_DIRECTION_INPUT, input), NULL); - - return p; - } - --static void add_profile(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input) { -+void pa_droid_profile_add_mapping(pa_droid_profile *p, pa_droid_mapping *am) { -+ pa_assert(p); -+ pa_assert(am); -+ -+ if (am->direction == PA_DIRECTION_OUTPUT) -+ pa_idxset_put(p->output_mappings, am, NULL); -+ else -+ pa_idxset_put(p->input_mappings, am, NULL); -+} -+ -+static pa_droid_profile *add_profile(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input) { - pa_droid_profile *ap; - - pa_log_debug("New profile: %s-%s", output->name, input ? input->name : "no input"); -@@ -654,19 +817,121 @@ static void add_profile(pa_droid_profile - ap = pa_droid_profile_new(ps, output, input); - - pa_hashmap_put(ps->profiles, ap->name, ap); -+ -+ return ap; - } - --pa_droid_profile_set *pa_droid_profile_set_new(const pa_droid_config_hw_module *module) { -+static bool str_in_strlist(const char *str, pa_strlist *list) { -+ pa_strlist *iter; -+ -+ pa_assert(str); -+ pa_assert(list); -+ -+ for (iter = list; iter; iter = pa_strlist_next(iter)) { -+ if (pa_streq(str, pa_strlist_data(iter))) -+ return true; -+ } -+ -+ return false; -+} -+ -+/* outputs or inputs string lists can be NULL, which means include all outputs and inputs -+ * from module. */ -+static pa_droid_profile *add_combined_profile(pa_droid_profile_set *ps, -+ const pa_droid_config_hw_module *module, -+ pa_strlist *outputs, -+ pa_strlist *inputs) { -+ pa_droid_profile *p; -+ char *description; -+ char *o_str; -+ char *i_str; -+ pa_strlist *to_outputs = NULL; -+ pa_strlist *to_inputs = NULL; -+ pa_droid_mapping *am; -+ -+ pa_assert(ps); -+ pa_assert(module); -+ -+ for (unsigned i = 0; i < module->outputs_size; i++) { -+ if (outputs && !str_in_strlist(module->outputs[i].name, outputs)) -+ continue; -+ -+ to_outputs = pa_strlist_prepend(to_outputs, module->outputs[i].name); -+ } -+ to_outputs = pa_strlist_reverse(to_outputs); -+ -+ for (unsigned i = 0; i < module->inputs_size; i++) { -+ if (inputs && !str_in_strlist(module->inputs[i].name, inputs)) -+ continue; -+ -+ to_inputs = pa_strlist_prepend(to_inputs, module->inputs[i].name); -+ } -+ to_inputs = pa_strlist_reverse(to_inputs); -+ -+ o_str = pa_strlist_to_string(to_outputs); -+ i_str = pa_strlist_to_string(to_inputs); -+ -+ pa_log_debug("New combined profile: %s (outputs: %s, inputs: %s)", module->name, o_str, i_str); -+ -+ description = pa_sprintf_malloc("Combined outputs (%s) and inputs (%s) of %s.", o_str, -+ i_str, -+ module->name); -+ p = profile_new(ps, module, module->name, description); -+ pa_xfree(description); -+ pa_xfree(o_str); -+ pa_xfree(i_str); -+ -+ for (unsigned i = 0; i < module->outputs_size; i++) { -+ if (!str_in_strlist(module->outputs[i].name, to_outputs)) -+ continue; -+ -+ am = pa_droid_mapping_get(ps, PA_DIRECTION_OUTPUT, &module->outputs[i]); -+ pa_droid_profile_add_mapping(p, am); -+ -+ if (pa_streq(module->outputs[i].name, "primary")) -+ p->priority += DEFAULT_PRIORITY; -+ } -+ -+ for (unsigned i = 0; i < module->inputs_size; i++) { -+ if (!str_in_strlist(module->inputs[i].name, to_inputs)) -+ continue; -+ -+ am = pa_droid_mapping_get(ps, PA_DIRECTION_INPUT, &module->inputs[i]); -+ pa_droid_profile_add_mapping(p, am); -+ -+ if (pa_streq(module->inputs[i].name, "primary")) -+ p->priority += DEFAULT_PRIORITY; -+ } -+ -+ pa_strlist_free(to_outputs); -+ pa_strlist_free(to_inputs); -+ -+ return p; -+} -+ -+static pa_droid_profile_set *profile_set_new(const pa_droid_config_hw_module *module) { - pa_droid_profile_set *ps; - - pa_assert(module); - - ps = pa_xnew0(pa_droid_profile_set, 1); - ps->config = module->config; -- ps->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_droid_profile_free); -- ps->output_mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_droid_mapping_free); -- ps->input_mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_droid_mapping_free); -- ps->all_ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) droid_port_free); -+ ps->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, -+ NULL, (pa_free_cb_t) pa_droid_profile_free); -+ ps->output_mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, -+ NULL, (pa_free_cb_t) pa_droid_mapping_free); -+ ps->input_mappings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, -+ NULL, (pa_free_cb_t) pa_droid_mapping_free); -+ ps->all_ports = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, -+ NULL, (pa_free_cb_t) droid_port_free); -+ -+ return ps; -+} -+ -+pa_droid_profile_set *pa_droid_profile_set_new(const pa_droid_config_hw_module *module) { -+ pa_droid_profile_set *ps; -+ -+ ps = profile_set_new(module); - - /* Each distinct hw module output matches one profile. If there are multiple inputs - * combinations are made so that all possible outputs and inputs can be selected. -@@ -686,6 +951,15 @@ pa_droid_profile_set *pa_droid_profile_s - return ps; - } - -+pa_droid_profile_set *pa_droid_profile_set_combined_new(const pa_droid_config_hw_module *module, pa_strlist *inputs, pa_strlist *outputs) { -+ pa_droid_profile_set *ps; -+ -+ ps = profile_set_new(module); -+ add_combined_profile(ps, module, inputs, outputs); -+ -+ return ps; -+} -+ - void pa_droid_mapping_free(pa_droid_mapping *am) { - pa_assert(am); - -@@ -700,9 +974,21 @@ void pa_droid_profile_free(pa_droid_prof - - pa_xfree(ap->name); - pa_xfree(ap->description); -+ if (ap->output_mappings) -+ pa_idxset_free(ap->output_mappings, NULL); -+ if (ap->input_mappings) -+ pa_idxset_free(ap->input_mappings, NULL); - pa_xfree(ap); - } - -+static void droid_port_free(pa_droid_port *p) { -+ pa_assert(p); -+ -+ pa_xfree(p->name); -+ pa_xfree(p->description); -+ pa_xfree(p); -+} -+ - void pa_droid_profile_set_free(pa_droid_profile_set *ps) { - pa_assert(ps); - -@@ -749,9 +1035,6 @@ static pa_droid_port *create_o_port(pa_d - if (am->profile_set->config->global_config.default_output_device & device) - p->priority += DEFAULT_PRIORITY; - -- if (check_port_availability(p->name)) -- p->priority += (DEFAULT_PRIORITY * 3); -- - return p; - } - -@@ -766,7 +1049,7 @@ static void add_o_ports(pa_droid_mapping - - devices = am->output->devices; - -- devices &= ~AUDIO_DEVICE_OUT_DEFAULT; -+ devices &= ~AUDIO_DEVICE_BIT_DEFAULT; - - /* IHF combo devices, these devices are combined with IHF */ - combo_devices = AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADPHONE; -@@ -819,6 +1102,35 @@ static void add_o_ports(pa_droid_mapping - pa_idxset_put(am->ports, p, NULL); - } - -+static void add_i_port(pa_droid_mapping *am, uint32_t device, const char *name) { -+ pa_droid_port *p; -+ char *desc; -+ -+ pa_assert(am); -+ pa_assert(name); -+ -+ if (!(p = pa_hashmap_get(am->profile_set->all_ports, name))) { -+ pa_log_debug(" New input port %s", name); -+ p = pa_xnew0(pa_droid_port, 1); -+ -+ p->mapping = am; -+ p->name = pa_xstrdup(name); -+ desc = pa_replace(name, "input-", "Input from "); -+ p->description = pa_replace(desc, "_", " "); -+ pa_xfree(desc); -+ p->priority = DEFAULT_PRIORITY; -+ p->device = device; -+ -+ if (am->profile_set->config->global_config.attached_input_devices & device) -+ p->priority += DEFAULT_PRIORITY; -+ -+ pa_hashmap_put(am->profile_set->all_ports, p->name, p); -+ } else -+ pa_log_debug(" Input port %s from cache", name); -+ -+ pa_idxset_put(am->ports, p, NULL); -+} -+ - static void add_i_ports(pa_droid_mapping *am) { - pa_droid_port *p; - const char *name; -@@ -828,9 +1140,9 @@ static void add_i_ports(pa_droid_mapping - - pa_assert(am); - -- devices = am->input->devices; --#ifdef HAL_V2 -- devices &= ~AUDIO_DEVICE_IN_DEFAULT; -+ devices = am->input->devices | AUDIO_DEVICE_IN_DEFAULT; -+#if DROID_HAL >= 2 -+ devices &= ~AUDIO_DEVICE_BIT_IN; - #endif - - while (devices) { -@@ -838,44 +1150,25 @@ static void add_i_ports(pa_droid_mapping - - if (devices & cur_device) { - --#ifdef HAL_V2 -+#if DROID_HAL >= 2 -+#ifndef DROID_DEVICE_MAKO - cur_device |= AUDIO_DEVICE_BIT_IN; - #endif -+#endif - - pa_assert_se(pa_droid_input_port_name(cur_device, &name)); -- -- if (!(p = pa_hashmap_get(am->profile_set->all_ports, name))) { -- pa_log_debug(" New input port %s", name); -- p = pa_xnew0(pa_droid_port, 1); -- -- p->mapping = am; -- p->name = pa_xstrdup(name); -- desc = pa_replace(name, "input-", "Input from "); -- p->description = pa_replace(desc, "_", " "); -- pa_xfree(desc); -- p->priority = DEFAULT_PRIORITY; -- p->device = cur_device; -- -- if (am->profile_set->config->global_config.attached_input_devices & cur_device & ~AUDIO_DEVICE_BIT_IN) -- p->priority += DEFAULT_PRIORITY; -- -- /* Make builtin mic the default input device */ -- if (cur_device == AUDIO_DEVICE_IN_BUILTIN_MIC) -- p->priority += DEFAULT_PRIORITY; -- -- if (check_port_availability(p->name)) -- p->priority += (DEFAULT_PRIORITY * 3); -- -- pa_hashmap_put(am->profile_set->all_ports, p->name, p); -- } else -- pa_log_debug(" Input port %s from cache", name); -- -- pa_idxset_put(am->ports, p, NULL); -+ add_i_port(am, cur_device, name); - - devices &= ~cur_device; - } - } - -+#if DROID_HAL == 1 -+ /* HAL v1 has default input device defined as another input device, -+ * so we need to add it by hand here. */ -+ add_i_port(am, AUDIO_DEVICE_IN_DEFAULT, "input-default"); -+#endif -+ - if (!(p = pa_hashmap_get(am->profile_set->all_ports, PA_DROID_INPUT_PARKING))) { - pa_log_debug(" New input port %s", PA_DROID_INPUT_PARKING); - /* Create parking port for input mapping to be used when audio_mode_t changes. */ -@@ -935,6 +1228,32 @@ pa_droid_mapping *pa_droid_mapping_get(p - return am; - } - -+bool pa_droid_mapping_is_primary(pa_droid_mapping *am) { -+ pa_assert(am); -+ -+ if (am->direction == PA_DIRECTION_OUTPUT) { -+ pa_assert(am->output); -+ return pa_streq(am->output->name, PA_DROID_PRIMARY_DEVICE); -+ } else { -+ pa_assert(am->input); -+ return pa_streq(am->input->name, PA_DROID_PRIMARY_DEVICE); -+ } -+} -+ -+pa_droid_mapping *pa_droid_idxset_get_primary(pa_idxset *i) { -+ pa_droid_mapping *am; -+ uint32_t idx; -+ -+ pa_assert(i); -+ -+ PA_IDXSET_FOREACH(am, i, idx) { -+ if (pa_droid_mapping_is_primary(am)) -+ return am; -+ } -+ -+ return NULL; -+} -+ - bool pa_droid_output_port_name(audio_devices_t value, const char **to_str) { - return string_convert_num_to_str(string_conversion_table_output_device_fancy, (uint32_t) value, to_str); - } -@@ -943,8 +1262,13 @@ bool pa_droid_input_port_name(audio_devi - return string_convert_num_to_str(string_conversion_table_input_device_fancy, (uint32_t) value, to_str); - } - -+bool pa_droid_audio_source_name(audio_source_t value, const char **to_str) { -+ return string_convert_num_to_str(string_conversion_table_audio_source_fancy, (uint32_t) value, to_str); -+} -+ - static int add_ports(pa_core *core, pa_card_profile *cp, pa_hashmap *ports, pa_droid_mapping *am, pa_hashmap *extra) { - pa_droid_port *p; -+ pa_device_port_new_data dp_data; - pa_device_port *dp; - pa_droid_port_data *data; - uint32_t idx; -@@ -955,35 +1279,37 @@ static int add_ports(pa_core *core, pa_c - PA_IDXSET_FOREACH(p, am->ports, idx) { - if (!(dp = pa_hashmap_get(ports, p->name))) { - pa_log_debug(" New port %s", p->name); -+ pa_device_port_new_data_init(&dp_data); -+ pa_device_port_new_data_set_name(&dp_data, p->name); -+ pa_device_port_new_data_set_description(&dp_data, p->description); -+ pa_device_port_new_data_set_direction(&dp_data, p->mapping->direction); -+ pa_device_port_new_data_set_available(&dp_data, PA_AVAILABLE_YES); - -- pa_device_port_new_data port_data; -- pa_device_port_new_data_init(&port_data); -- pa_device_port_new_data_set_name(&port_data, p->name); -- pa_device_port_new_data_set_description(&port_data, p->description); -- pa_device_port_new_data_set_direction(&port_data, p->mapping->direction); -- dp = pa_device_port_new(core, &port_data, sizeof(pa_droid_port_data)); -- pa_device_port_new_data_done(&port_data); -+ dp = pa_device_port_new(core, &dp_data, sizeof(pa_droid_port_data)); - dp->priority = p->priority; - -+ pa_device_port_new_data_done(&dp_data); -+ - pa_hashmap_put(ports, dp->name, dp); -- dp->profiles = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) pa_droid_profile_free); -+ dp->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - - data = PA_DEVICE_PORT_DATA(dp); - data->device = p->device; - } else - pa_log_debug(" Port %s from cache", p->name); - -- /* If port/jack detection is available, start as not available by default */ -- dp->available = check_port_availability(p->name) ? PA_AVAILABLE_NO : PA_AVAILABLE_UNKNOWN; -- -- if (cp) -- pa_hashmap_put(dp->profiles, cp->name, cp); -+ if (cp) { -+ if (!pa_hashmap_get(dp->profiles, cp->name)) -+ pa_hashmap_put(dp->profiles, cp->name, cp); -+ } - - count++; - - if (extra) { -- pa_hashmap_put(extra, dp->name, dp); -- pa_device_port_ref(dp); -+ if (!pa_hashmap_get(extra, dp->name)) { -+ pa_hashmap_put(extra, dp->name, dp); -+ pa_device_port_ref(dp); -+ } - } - } - -@@ -1032,15 +1358,14 @@ static pa_droid_hw_module *droid_hw_modu - - hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, module->name, (const hw_module_t**) &hwmod); - if (!hwmod) { -- pa_log("Failed to get hw module id: %s name: %s, trying alternative.", AUDIO_HARDWARE_MODULE_ID, module->name); -- hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID2, module->name, (const hw_module_t**) &hwmod); -- if (!hwmod) { -- pa_log("Failed to get hw module id: %s name: %s.", AUDIO_HARDWARE_MODULE_ID2, module->name); -- goto fail; -- } -+ pa_log("Failed to get hw module %s.", module->name); -+ goto fail; - } - -- pa_log_info("Loaded hw module %s", module->name); -+ pa_log_info("Loaded hw module %s (HAL %d.%d.%d)", module->name, -+ ANDROID_VERSION_MAJOR, -+ ANDROID_VERSION_MINOR, -+ ANDROID_VERSION_PATCH); - - ret = audio_hw_device_open(hwmod, &device); - if (!device) { -@@ -1058,11 +1383,15 @@ static pa_droid_hw_module *droid_hw_modu - hw->core = core; - hw->hwmod = hwmod; - hw->hw_mutex = pa_mutex_new(true, false); -+ hw->output_mutex = pa_mutex_new(true, false); -+ hw->input_mutex = pa_mutex_new(true, false); - hw->device = device; - hw->config = config; /* We take ownership of config struct. */ - hw->enabled_module = pa_droid_config_find_module(hw->config, module_id); - hw->module_id = hw->enabled_module->name; - hw->shared_name = shared_name_get(hw->module_id); -+ hw->outputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); -+ hw->inputs = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - - pa_assert_se(pa_shared_set(core, hw->shared_name, hw) >= 0); - -@@ -1117,9 +1446,25 @@ static void droid_hw_module_close(pa_dro - if (hw->hw_mutex) - pa_mutex_free(hw->hw_mutex); - -+ if (hw->output_mutex) -+ pa_mutex_free(hw->output_mutex); -+ -+ if (hw->input_mutex) -+ pa_mutex_free(hw->input_mutex); -+ - if (hw->shared_name) - pa_xfree(hw->shared_name); - -+ if (hw->outputs) { -+ pa_assert(pa_idxset_size(hw->outputs) == 0); -+ pa_idxset_free(hw->outputs, NULL); -+ } -+ -+ if (hw->inputs) { -+ pa_assert(pa_idxset_size(hw->inputs) == 0); -+ pa_idxset_free(hw->inputs, NULL); -+ } -+ - pa_xfree(hw); - } - -@@ -1187,3 +1532,404 @@ void pa_droid_hw_module_unlock(pa_droid_ - - pa_mutex_unlock(hw->hw_mutex); - } -+ -+static pa_droid_stream *droid_stream_new(pa_droid_hw_module *module) { -+ pa_droid_stream *s; -+ -+ s = pa_xnew0(pa_droid_stream, 1); -+ PA_REFCNT_INIT(s); -+ -+ s->module = module; -+ -+ return s; -+} -+ -+pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module, -+ const pa_sample_spec *spec, -+ const pa_channel_map *map, -+ audio_output_flags_t flags, -+ audio_devices_t devices) { -+ pa_droid_stream *s = NULL; -+ int ret; -+ struct audio_stream_out *stream; -+ audio_format_t hal_audio_format = 0; -+ audio_channel_mask_t hal_channel_mask = 0; -+ struct audio_config config_out; -+ size_t buffer_size; -+ -+ pa_assert(module); -+ pa_assert(spec); -+ pa_assert(map); -+ -+ if (!pa_convert_format(spec->format, CONV_FROM_PA, &hal_audio_format)) { -+ pa_log("Sample spec format %u not supported.", spec->format); -+ goto fail; -+ } -+ -+ for (int i = 0; i < map->channels; i++) { -+ audio_channel_mask_t c; -+ if (!pa_convert_output_channel(map->map[i], CONV_FROM_PA, &c)) { -+ pa_log("Failed to convert channel map."); -+ goto fail; -+ } -+ hal_channel_mask |= c; -+ } -+ -+ config_out.sample_rate = spec->rate; -+ config_out.channel_mask = hal_channel_mask; -+ config_out.format = hal_audio_format; -+ -+ pa_droid_hw_module_lock(module); -+ ret = module->device->open_output_stream(module->device, -+ module->stream_out_id++, -+ devices, -+ flags, -+ &config_out, -+ &stream -+#if DROID_HAL >= 3 -+ /* Go with empty address, should work -+ * with most devices for now. */ -+ , NULL -+#endif -+ ); -+ pa_droid_hw_module_unlock(module); -+ -+ if (ret < 0 || !stream) { -+ pa_log("Failed to open output stream: %d", ret); -+ goto fail; -+ } -+ -+ s = droid_stream_new(module); -+ s->out = stream; -+ s->sample_spec = *spec; -+ s->channel_map = *map; -+ s->flags = flags; -+ -+ if ((s->sample_spec.rate = s->out->common.get_sample_rate(&s->out->common)) != spec->rate) -+ pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, s->sample_spec.rate); -+ -+ pa_idxset_put(module->outputs, s, NULL); -+ -+ buffer_size = s->out->common.get_buffer_size(&s->out->common); -+ -+ pa_log_info("Opened droid output stream %p with device: %u flags: %u sample rate: %u channels: %u (%u) format: %u (%u) buffer size: %u (%llu usec)", -+ (void *) s, -+ devices, -+ s->flags, -+ s->sample_spec.rate, -+ s->sample_spec.channels, hal_channel_mask, -+ s->sample_spec.format, hal_audio_format, -+ buffer_size, -+ pa_bytes_to_usec(buffer_size, &s->sample_spec)); -+ -+ return s; -+ -+fail: -+ pa_xfree(s); -+ -+ return NULL; -+} -+ -+pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *module, -+ const pa_sample_spec *spec, -+ const pa_channel_map *map, -+ audio_devices_t devices) { -+ -+ pa_droid_stream *s = NULL; -+ int ret; -+ audio_stream_in_t *stream; -+ audio_format_t hal_audio_format = 0; -+ audio_channel_mask_t hal_channel_mask = 0; -+ pa_channel_map channel_map; -+ pa_sample_spec sample_spec; -+ bool voicecall_record = false; -+ struct audio_config config_in; -+ size_t buffer_size; -+ -+#if DROID_HAL >= 2 -+ if ((devices & ~AUDIO_DEVICE_BIT_IN) & AUDIO_DEVICE_IN_VOICE_CALL) -+#else -+ if (devices & AUDIO_DEVICE_IN_VOICE_CALL) -+#endif -+ voicecall_record = true; -+ -+ channel_map = *map; -+ sample_spec = *spec; -+ -+ if (!pa_convert_format(spec->format, CONV_FROM_PA, &hal_audio_format)) { -+ pa_log("Sample spec format %u not supported.", spec->format); -+ goto fail; -+ } -+ -+ for (int i = 0; i < map->channels; i++) { -+ audio_channel_mask_t c; -+ if (!pa_convert_input_channel(map->map[i], CONV_FROM_PA, &c)) { -+ pa_log("Failed to convert channel map."); -+ goto fail; -+ } -+ hal_channel_mask |= c; -+ } -+ -+ if (voicecall_record) { -+ pa_channel_map_init_mono(&channel_map); -+ sample_spec.channels = 1; -+ /* Only allow recording both downlink and uplink. */ -+#ifdef QCOM_HARDWARE -+ hal_channel_mask = AUDIO_CHANNEL_IN_VOICE_CALL_MONO; -+#else -+ hal_channel_mask = AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK; -+#endif -+ } -+ -+ config_in.sample_rate = sample_spec.rate; -+ config_in.channel_mask = hal_channel_mask; -+ config_in.format = hal_audio_format; -+ -+ pa_droid_hw_module_lock(module); -+ ret = module->device->open_input_stream(module->device, -+ module->stream_in_id++, -+ devices, -+ &config_in, -+ &stream -+#if DROID_HAL >= 3 -+ , AUDIO_INPUT_FLAG_NONE /* Default to no input flags */ -+ , NULL /* Don't define address */ -+ , AUDIO_SOURCE_DEFAULT /* Default audio source */ -+#endif -+ ); -+ pa_droid_hw_module_unlock(module); -+ -+ if (ret < 0 || !stream) { -+ pa_log("Failed to open input stream: %d", ret); -+ goto fail; -+ } -+ -+ s = droid_stream_new(module); -+ s->in = stream; -+ s->sample_spec = sample_spec; -+ s->channel_map = channel_map; -+ s->flags = 0; -+ -+ if ((s->sample_spec.rate = s->in->common.get_sample_rate(&s->in->common)) != spec->rate) -+ pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, s->sample_spec.rate); -+ -+ pa_idxset_put(module->inputs, s, NULL); -+ -+ buffer_size = s->in->common.get_buffer_size(&s->in->common); -+ -+ pa_log_info("Opened droid input stream %p with device: %u flags: %u sample rate: %u channels: %u (%u) format: %u (%u) buffer size: %u (%llu usec)", -+ (void *) s, -+ devices, -+ s->flags, -+ s->sample_spec.rate, -+ s->sample_spec.channels, hal_channel_mask, -+ s->sample_spec.format, hal_audio_format, -+ buffer_size, -+ pa_bytes_to_usec(buffer_size, &s->sample_spec)); -+ -+ return s; -+ -+fail: -+ pa_xfree(s); -+ -+ return NULL; -+} -+ -+pa_droid_stream *pa_droid_stream_ref(pa_droid_stream *s) { -+ pa_assert(s); -+ pa_assert(s->out || s->in); -+ pa_assert(PA_REFCNT_VALUE(s) >= 1); -+ -+ PA_REFCNT_INC(s); -+ return s; -+} -+ -+void pa_droid_stream_unref(pa_droid_stream *s) { -+ pa_assert(s); -+ pa_assert(s->out || s->in); -+ pa_assert(PA_REFCNT_VALUE(s) >= 1); -+ -+ if (PA_REFCNT_DEC(s) > 0) -+ return; -+ -+ if (s->out) { -+ pa_mutex_lock(s->module->output_mutex); -+ pa_idxset_remove_by_data(s->module->outputs, s, NULL); -+ s->module->device->close_output_stream(s->module->device, s->out); -+ pa_mutex_unlock(s->module->output_mutex); -+ } else { -+ pa_mutex_lock(s->module->input_mutex); -+ pa_idxset_remove_by_data(s->module->inputs, s, NULL); -+ s->module->device->close_input_stream(s->module->device, s->in); -+ pa_mutex_unlock(s->module->input_mutex); -+ } -+ -+ pa_xfree(s); -+} -+ -+static pa_droid_stream *get_primary_output(pa_droid_hw_module *hw) { -+ pa_droid_stream *s; -+ uint32_t idx; -+ -+ pa_assert(hw); -+ pa_assert(hw->outputs); -+ -+ PA_IDXSET_FOREACH(s, hw->outputs, idx) { -+ if (s->flags & AUDIO_OUTPUT_FLAG_PRIMARY) -+ return s; -+ } -+ -+ return NULL; -+} -+ -+int pa_droid_stream_set_output_route(pa_droid_stream *s, audio_devices_t device) { -+ pa_droid_stream *slave; -+ uint32_t idx; -+ char *parameters; -+ int ret; -+ -+ pa_assert(s); -+ pa_assert(s->out); -+ pa_assert(s->module); -+ pa_assert(s->module->output_mutex); -+ -+ pa_mutex_lock(s->module->output_mutex); -+ -+ parameters = pa_sprintf_malloc("%s=%u;", AUDIO_PARAMETER_STREAM_ROUTING, device); -+ -+ if (s->flags & AUDIO_OUTPUT_FLAG_PRIMARY || get_primary_output(s->module) == NULL) { -+ pa_log_debug("output stream %p set_parameters(%s) %#010x", (void *) s, parameters, device); -+ ret = s->out->common.set_parameters(&s->out->common, parameters); -+ -+ if (ret < 0) { -+ if (ret == -ENOSYS) -+ pa_log_warn("output set_parameters(%s) not allowed while stream is active", parameters); -+ else -+ pa_log_warn("output set_parameters(%s) failed", parameters); -+ } -+ } -+ -+ if (s->flags & AUDIO_OUTPUT_FLAG_PRIMARY && pa_idxset_size(s->module->outputs) > 1) { -+ -+ PA_IDXSET_FOREACH(slave, s->module->outputs, idx) { -+ if (slave == s) -+ continue; -+ -+ pa_log_debug("slave output stream %p set_parameters(%s)", (void *) slave, parameters); -+ ret = slave->out->common.set_parameters(&slave->out->common, parameters); -+ -+ if (ret < 0) { -+ if (ret == -ENOSYS) -+ pa_log_warn("output set_parameters(%s) not allowed while stream is active", parameters); -+ else -+ pa_log_warn("output set_parameters(%s) failed", parameters); -+ } -+ } -+ } -+ -+ pa_xfree(parameters); -+ -+ pa_mutex_unlock(s->module->output_mutex); -+ -+ return ret; -+} -+ -+int pa_droid_stream_set_input_route(pa_droid_stream *s, audio_devices_t device, audio_source_t *new_source) { -+ audio_source_t source = (uint32_t) -1; -+ char *parameters; -+ int ret; -+ -+ pa_assert(s); -+ pa_assert(s->in); -+ -+#ifdef DROID_DEVICE_I9305 -+ device &= ~AUDIO_DEVICE_BIT_IN; -+#endif -+ -+ if (pa_input_device_default_audio_source(device, &source)) -+#ifdef DROID_AUDIO_HAL_ATOI_FIX -+ parameters = pa_sprintf_malloc("%s=%d;%s=%u", AUDIO_PARAMETER_STREAM_ROUTING, (int32_t) device, -+ AUDIO_PARAMETER_STREAM_INPUT_SOURCE, source); -+#else -+ parameters = pa_sprintf_malloc("%s=%u;%s=%u", AUDIO_PARAMETER_STREAM_ROUTING, device, -+ AUDIO_PARAMETER_STREAM_INPUT_SOURCE, source); -+#endif -+ else -+ parameters = pa_sprintf_malloc("%s=%u", AUDIO_PARAMETER_STREAM_ROUTING, device); -+ -+ pa_log_debug("input stream %p set_parameters(%s) %#010x ; %#010x", -+ (void *) s, parameters, device, source); -+ -+ -+#if defined(DROID_DEVICE_MAKO) || defined(DROID_DEVICE_ANZU) ||\ -+ defined(DROID_DEVICE_COCONUT) || defined(DROID_DEVICE_HAIDA) ||\ -+ defined(DROID_DEVICE_HALLON) || defined(DROID_DEVICE_IYOKAN) ||\ -+ defined(DROID_DEVICE_MANGO) || defined(DROID_DEVICE_SATSUMA) ||\ -+ defined(DROID_DEVICE_SMULTRON) || defined(DROID_DEVICE_URUSHI) -+#warning Using mako set_parameters hack. -+ pa_mutex_lock(s->module->hw_mutex); -+ ret = s->module->device->set_parameters(s->module->device, parameters); -+ pa_mutex_unlock(s->module->hw_mutex); -+#else -+ pa_mutex_lock(s->module->input_mutex); -+ ret = s->in->common.set_parameters(&s->in->common, parameters); -+ pa_mutex_unlock(s->module->input_mutex); -+#endif -+ -+ if (ret < 0) { -+ if (ret == -ENOSYS) -+ pa_log_warn("input set_parameters(%s) not allowed while stream is active", parameters); -+ else -+ pa_log_warn("input set_parameters(%s) failed", parameters); -+ } -+ -+ if (new_source) -+ *new_source = source; -+ -+ pa_xfree(parameters); -+ -+ return ret; -+} -+ -+int pa_droid_stream_set_parameters(pa_droid_stream *s, const char *parameters) { -+ int ret; -+ -+ pa_assert(s); -+ pa_assert(s->out || s->in); -+ pa_assert(parameters); -+ -+ if (s->out) { -+ pa_log_debug("output stream %p set_parameters(%s)", (void *) s, parameters); -+ pa_mutex_lock(s->module->output_mutex); -+ ret = s->out->common.set_parameters(&s->out->common, parameters); -+ pa_mutex_unlock(s->module->output_mutex); -+ } else { -+ pa_log_debug("input stream %p set_parameters(%s)", (void *) s, parameters); -+ pa_mutex_lock(s->module->input_mutex); -+ ret = s->in->common.set_parameters(&s->in->common, parameters); -+ pa_mutex_unlock(s->module->input_mutex); -+ } -+ -+ if (ret < 0) -+ pa_log("%s stream %p set_parameters(%s) failed: %d", -+ s->out ? "output" : "input", (void *) s, parameters, ret); -+ -+ return ret; -+} -+ -+int pa_droid_set_parameters(pa_droid_hw_module *hw, const char *parameters) { -+ int ret; -+ -+ pa_assert(hw); -+ pa_assert(parameters); -+ -+ pa_log_debug("hw %p set_parameters(%s)", (void *) hw, parameters); -+ pa_mutex_lock(hw->hw_mutex); -+ ret = hw->device->set_parameters(hw->device, parameters); -+ pa_mutex_unlock(hw->hw_mutex); -+ -+ if (ret < 0) -+ pa_log("hw module %p set_parameters(%s) failed: %d", (void *) hw, parameters, ret); -+ -+ return ret; -+} -Index: pulseaudio/src/modules/droid/droid-util.h -=================================================================== ---- pulseaudio.orig/src/modules/droid/droid-util.h -+++ pulseaudio/src/modules/droid/droid-util.h -@@ -28,24 +28,36 @@ - #include - #include - #include --#include -+#include - --#include --#include -+#include -+ -+#if !defined(ANDROID_VERSION_MAJOR) || !defined(ANDROID_VERSION_MINOR) || !defined(ANDROID_VERSION_PATCH) -+#error "ANDROID_VERSION_* not defined. Did you get your headers via extract-headers.sh?" -+#endif -+ -+#if ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 1 -+#include "droid-util-41qc.h" -+#elif ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 2 -+#include "droid-util-42.h" -+#elif ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 4 -+#include "droid-util-44.h" -+#elif ANDROID_VERSION_MAJOR == 5 && ANDROID_VERSION_MINOR == 1 -+#include "droid-util-51.h" -+#else -+#error "No valid ANDROID_VERSION found." -+#endif - - #define PROP_DROID_DEVICES "droid.devices" - #define PROP_DROID_FLAGS "droid.flags" - #define PROP_DROID_HW_MODULE "droid.hw_module" - --/* Alternative module ID */ --#define AUDIO_HARDWARE_MODULE_ID2 "libaudio" -- --/* From module-device-restore */ --#define MODULE_DEVICE_RESTORE_SKIP_PROPERTY "module-device-restore.skip" -+#define PA_DROID_PRIMARY_DEVICE "primary" - - typedef struct pa_droid_hw_module pa_droid_hw_module; -+typedef struct pa_droid_stream pa_droid_stream; - typedef struct pa_droid_card_data pa_droid_card_data; --typedef void (*common_set_parameters_cb_t)(pa_droid_card_data *card_data, const char *str); -+typedef int (*common_set_parameters_cb_t)(pa_droid_card_data *card_data, const char *str); - - typedef struct pa_droid_config_audio pa_droid_config_audio; - typedef struct pa_droid_config_hw_module pa_droid_config_hw_module; -@@ -59,6 +71,8 @@ struct pa_droid_hw_module { - pa_droid_config_audio *config; - const pa_droid_config_hw_module *enabled_module; - pa_mutex *hw_mutex; -+ pa_mutex *output_mutex; -+ pa_mutex *input_mutex; - - struct hw_module_t *hwmod; - audio_hw_device_t *device; -@@ -68,6 +82,21 @@ struct pa_droid_hw_module { - uint32_t stream_out_id; - uint32_t stream_in_id; - -+ pa_idxset *outputs; -+ pa_idxset *inputs; -+}; -+ -+struct pa_droid_stream { -+ PA_REFCNT_DECLARE; -+ -+ pa_droid_hw_module *module; -+ -+ pa_sample_spec sample_spec; -+ pa_channel_map channel_map; -+ uint32_t flags; -+ -+ struct audio_stream_out *out; -+ struct audio_stream_in *in; - }; - - struct pa_droid_card_data { -@@ -92,9 +121,9 @@ typedef struct pa_droid_config_output { - const pa_droid_config_hw_module *module; - - char name[AUDIO_HARDWARE_MODULE_ID_MAX_LEN]; -- uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; -+ uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; /* (uint32_t) -1 -> dynamic */ - audio_channel_mask_t channel_masks; /* 0 -> dynamic */ -- audio_format_t formats; -+ audio_format_t formats; /* 0 -> dynamic */ - audio_devices_t devices; - audio_output_flags_t flags; - } pa_droid_config_output; -@@ -103,10 +132,13 @@ typedef struct pa_droid_config_input { - const pa_droid_config_hw_module *module; - - char name[AUDIO_HARDWARE_MODULE_ID_MAX_LEN]; -- uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; -+ uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; /* (uint32_t) -1 -> dynamic */ - audio_channel_mask_t channel_masks; /* 0 -> dynamic */ -- audio_format_t formats; -+ audio_format_t formats; /* 0 -> dynamic */ - audio_devices_t devices; -+#if DROID_HAL >= 3 -+ audio_input_flags_t flags; -+#endif - } pa_droid_config_input; - - struct pa_droid_config_hw_module { -@@ -173,9 +205,10 @@ typedef struct pa_droid_profile { - char *description; - unsigned priority; - -- /* Profile doesn't own the mappings */ -- pa_droid_mapping *output; -- pa_droid_mapping *input; -+ /* Idxsets contain pa_droid_mapping objects. -+ * Profile doesn't own the mappings. */ -+ pa_idxset *output_mappings; -+ pa_idxset *input_mappings; - - } pa_droid_profile; - -@@ -225,6 +258,10 @@ char *pa_list_string_output_device(audio - char *pa_list_string_input_device(audio_devices_t devices); - char *pa_list_string_flags(audio_output_flags_t flags); - -+/* Get default audio source associated with input device. -+ * Return true if default source was found, false if not. */ -+bool pa_input_device_default_audio_source(audio_devices_t input_device, audio_source_t *default_source); -+ - /* Config parser */ - bool pa_parse_droid_audio_config(const char *filename, pa_droid_config_audio *config); - pa_droid_config_audio *pa_droid_config_load(pa_modargs *ma); -@@ -236,21 +273,65 @@ const pa_droid_config_hw_module *pa_droi - - /* Profiles */ - pa_droid_profile_set *pa_droid_profile_set_new(const pa_droid_config_hw_module *module); -+pa_droid_profile_set *pa_droid_profile_set_combined_new(const pa_droid_config_hw_module *module, -+ pa_strlist *inputs, -+ pa_strlist *outputs); - void pa_droid_profile_set_free(pa_droid_profile_set *ps); - - pa_droid_profile *pa_droid_profile_new(pa_droid_profile_set *ps, const pa_droid_config_output *output, const pa_droid_config_input *input); -+void pa_droid_profile_add_mapping(pa_droid_profile *p, pa_droid_mapping *am); - void pa_droid_profile_free(pa_droid_profile *p); - - pa_droid_mapping *pa_droid_mapping_get(pa_droid_profile_set *ps, pa_direction_t direction, const void *data); -+bool pa_droid_mapping_is_primary(pa_droid_mapping *am); -+/* Go through idxset containing pa_droid_mapping objects and if primary output or input -+ * mapping is found, return pointer to that mapping. */ -+pa_droid_mapping *pa_droid_idxset_get_primary(pa_idxset *i); - void pa_droid_mapping_free(pa_droid_mapping *am); - --/* Add ports from sinks/sources */ -+/* Add ports from sinks/sources. -+ * May be called multiple times for one sink/source. */ - void pa_droid_add_ports(pa_hashmap *ports, pa_droid_mapping *am, pa_card *card); --/* Add ports from card */ -+/* Add ports from card. -+ * May be called multiple times for one card profile. */ - void pa_droid_add_card_ports(pa_card_profile *cp, pa_hashmap *ports, pa_droid_mapping *am, pa_core *core); - - /* Pretty port names */ - bool pa_droid_output_port_name(audio_devices_t value, const char **to_str); - bool pa_droid_input_port_name(audio_devices_t value, const char **to_str); - -+/* Pretty audio source names */ -+bool pa_droid_audio_source_name(audio_source_t value, const char **to_str); -+ -+/* Module operations */ -+int pa_droid_set_parameters(pa_droid_hw_module *hw, const char *parameters); -+ -+/* Stream operations */ -+pa_droid_stream *pa_droid_stream_ref(pa_droid_stream *s); -+void pa_droid_stream_unref(pa_droid_stream *s); -+ -+int pa_droid_stream_set_parameters(pa_droid_stream *s, const char *parameters); -+ -+/* Output stream operations */ -+pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module, -+ const pa_sample_spec *spec, -+ const pa_channel_map *map, -+ audio_output_flags_t flags, -+ audio_devices_t devices); -+ -+/* Set routing to the output stream, with following side-effects: -+ * - if routing is set to primary output stream, set routing to all other -+ * open streams as well -+ * - if routing is set to non-primary stream and primary stream exists, do nothing -+ * - if routing is set to non-primary stream and primary stream doesn't exist, set routing -+ */ -+int pa_droid_stream_set_output_route(pa_droid_stream *s, audio_devices_t device); -+ -+/* Input stream operations */ -+pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *module, -+ const pa_sample_spec *spec, -+ const pa_channel_map *map, -+ audio_devices_t devices); -+int pa_droid_stream_set_input_route(pa_droid_stream *s, audio_devices_t device, audio_source_t *new_source); -+ - #endif -Index: pulseaudio/src/modules/droid/module-droid-card.c -=================================================================== ---- pulseaudio.orig/src/modules/droid/module-droid-card.c -+++ pulseaudio/src/modules/droid/module-droid-card.c -@@ -54,9 +54,7 @@ - #include - #include - #include -- --#include --#include -+#include - - //#include - //#include -@@ -68,7 +66,11 @@ - #include "droid-extcon.h" - #endif - --#include "module-droid-card-symdef.h" -+#if ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 2 -+#include "module-droid-card-19-symdef.h" -+#elif ANDROID_VERSION_MAJOR == 5 && ANDROID_VERSION_MINOR == 1 -+#include "module-droid-card-22-symdef.h" -+#endif - - PA_MODULE_AUTHOR("Juho Hämäläinen"); - PA_MODULE_DESCRIPTION("Droid card"); -@@ -84,10 +86,9 @@ PA_MODULE_USAGE( - "voice_source_routing= " - "deferred_volume= " - "config= " -- "voice_volume_call_mode= " - "voice_property_key= " - "voice_property_value= " -- "voice_virtual_stream= create virtual stream for voice call volume control (default false)" -+ "combine=" - ); - - static const char* const valid_modargs[] = { -@@ -97,6 +98,14 @@ static const char* const valid_modargs[] - "namereg_fail", - "format", - "rate", -+ "channels", -+ "channel_map", -+ "sink_rate", -+ "sink_format", -+ "sink_channel_map", -+ "source_rate", -+ "source_format", -+ "source_channel_map", - "output_flags", - "module_id", - "voice_source_routing", -@@ -110,21 +119,28 @@ static const char* const valid_modargs[] - "voice_property_key", - "voice_property_value", - "voice_virtual_stream", -+ "combine", - NULL, - }; - - #define DEFAULT_MODULE_ID "primary" --#define DEFAULT_AUDIO_POLICY_CONF "/system/etc/audio_policy.conf" - #define VOICE_CALL_PROFILE_NAME "voicecall" - #define VOICE_CALL_PROFILE_DESC "Call mode" -+#define VOICE_RECORD_PROFILE_NAME "voicecall-record" -+#define VOICE_RECORD_PROFILE_DESC "Call mode record" - #define RINGTONE_PROFILE_NAME "ringtone" - #define RINGTONE_PROFILE_DESC "Ringtone mode" - #define COMMUNICATION_PROFILE_NAME "communication" - #define COMMUNICATION_PROFILE_DESC "Communication mode" - -+struct userdata; -+ -+typedef bool (*virtual_profile_event_cb)(struct userdata *u, pa_droid_profile *p, bool enabling); -+ - struct virtual_profile { -- pa_droid_profile *profile; -- audio_mode_t mode; -+ pa_card_profile *parent; -+ pa_card_profile *extension; -+ virtual_profile_event_cb event_cb; - }; - - struct userdata { -@@ -140,11 +156,8 @@ struct userdata { - pa_droid_hw_module *hw_module; - pa_droid_card_data card_data; - -- struct virtual_profile call_profile; -- struct virtual_profile comm_profile; -- struct virtual_profile ring_profile; - pa_droid_profile *old_profile; -- -+ pa_source *voicecall_source; - #ifdef HAVE_UDEV - pa_droid_extcon *extcon; - #endif -@@ -157,8 +170,50 @@ struct userdata { - - struct profile_data { - pa_droid_profile *profile; -+ audio_mode_t mode; -+ bool virtual_profile; -+ /* Variables for virtual profiles: */ -+ struct virtual_profile virtual; - }; - -+#ifdef DROID_AUDIO_HAL_USE_VSID -+ -+/* From hal/voice_extn/voice_extn.c */ -+#define AUDIO_PARAMETER_KEY_VSID "vsid" -+#define AUDIO_PARAMETER_KEY_CALL_STATE "call_state" -+ -+/* From hal/voice_extn/voice_extn.c */ -+#define VOICE2_VSID 0x10DC1000 -+#define VOLTE_VSID 0x10C02000 -+#define QCHAT_VSID 0x10803000 -+#define VOWLAN_VSID 0x10002000 -+ -+/* From hal/voice.h */ -+#define BASE_CALL_STATE 1 -+#define CALL_INACTIVE (BASE_CALL_STATE) -+#define CALL_ACTIVE (BASE_CALL_STATE + 1) -+#define VOICE_VSID 0x10C01000 -+ -+/* For virtual profiles */ -+#define VOICE_SESSION_VOICE1_PROFILE_NAME "voicecall-voice1" -+#define VOICE_SESSION_VOICE1_PROFILE_DESC "Call mode, default to voice 1 vsid" -+#define VOICE_SESSION_VOICE2_PROFILE_NAME "voicecall-voice2" -+#define VOICE_SESSION_VOICE2_PROFILE_DESC "Call mode, default to voice 2 vsid" -+#define VOICE_SESSION_VOLTE_PROFILE_NAME "voicecall-volte" -+#define VOICE_SESSION_VOLTE_PROFILE_DESC "Call mode, default to volte vsid" -+#define VOICE_SESSION_QCHAT_PROFILE_NAME "voicecall-qchat" -+#define VOICE_SESSION_QCHAT_PROFILE_DESC "Call mode, default to qchat vsid" -+#define VOICE_SESSION_VOWLAN_PROFILE_NAME "voicecall-vowlan" -+#define VOICE_SESSION_VOWLAN_PROFILE_DESC "Call mode, default to vowlan vsid" -+ -+static bool voicecall_voice1_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling); -+static bool voicecall_voice2_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling); -+static bool voicecall_volte_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling); -+static bool voicecall_qchat_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling); -+static bool voicecall_vowlan_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling); -+ -+#endif /* DROID_AUDIO_HAL_USE_VSID */ -+ - static void add_disabled_profile(pa_hashmap *profiles) { - pa_card_profile *cp; - struct profile_data *d; -@@ -172,10 +227,13 @@ static void add_disabled_profile(pa_hash - } - - /* Special profile for calls */ --static pa_droid_profile* add_virtual_profile(struct userdata *u, const char *name, const char *description, pa_hashmap *profiles) { -+static pa_card_profile* add_virtual_profile(struct userdata *u, const char *name, const char *description, -+ audio_mode_t audio_mode, virtual_profile_event_cb event_cb, -+ pa_available_t available, pa_card_profile *extension_to, -+ pa_hashmap *profiles) { - pa_droid_profile *ap; - pa_card_profile *cp; -- struct profile_data *d; -+ struct profile_data *d, *ext; - - pa_assert(u); - pa_assert(u->profile_set); -@@ -191,28 +249,34 @@ static pa_droid_profile* add_virtual_pro - pa_hashmap_put(u->profile_set->profiles, ap->name, ap); - - cp = pa_card_profile_new(ap->name, ap->description, sizeof(struct profile_data)); -+ cp->available = available; - d = PA_CARD_PROFILE_DATA(cp); - d->profile = ap; -+ d->virtual_profile = true; -+ d->mode = audio_mode; -+ d->virtual.event_cb = event_cb; -+ d->virtual.extension = NULL; -+ if (extension_to) { -+ ext = PA_CARD_PROFILE_DATA(extension_to); -+ ext->virtual.extension = cp; -+ d->virtual.parent = extension_to; -+ } else -+ d->virtual.parent = NULL; - - pa_hashmap_put(profiles, cp->name, cp); - -- return ap; -+ return cp; - } - --static void set_parameters_cb(pa_droid_card_data *card_data, const char *str) { -+static int set_parameters_cb(pa_droid_card_data *card_data, const char *str) { - struct userdata *u; -+ int ret; - - pa_assert(card_data); -+ pa_assert_se((u = card_data->userdata)); - pa_assert(str); - -- u = card_data->userdata; -- -- if (u) { -- pa_log_debug("Setting parameters: %s", str); -- pa_droid_hw_module_lock(u->hw_module); -- u->hw_module->device->set_parameters(u->hw_module->device, str); -- pa_droid_hw_module_unlock(u->hw_module); -- } -+ return pa_droid_set_parameters(u->hw_module, str); - } - - static void set_card_name(pa_modargs *ma, pa_card_new_data *data, const char *module_id) { -@@ -238,6 +302,9 @@ static void set_card_name(pa_modargs *ma - static void add_profile(struct userdata *u, pa_hashmap *h, pa_hashmap *ports, pa_droid_profile *ap) { - pa_card_profile *cp; - struct profile_data *d; -+ pa_droid_mapping *am; -+ int max_channels; -+ uint32_t idx; - - pa_assert(u); - pa_assert(h); -@@ -247,19 +314,31 @@ static void add_profile(struct userdata - pa_log_debug("Card profile %s", ap->name); - - cp = pa_card_profile_new(ap->name, ap->description, sizeof(struct profile_data)); -+ cp->available = PA_AVAILABLE_YES; - cp->priority = ap->priority; - -- cp->n_sinks = 1; -- pa_droid_add_card_ports(cp, ports, ap->output, u->core); -- cp->max_sink_channels = popcount(ap->output->output->channel_masks); -- if (ap->input) { -- pa_droid_add_card_ports(cp, ports, ap->input, u->core); -- cp->n_sources = 1; -- cp->max_source_channels = popcount(ap->input->input->channel_masks); -+ max_channels = 0; -+ PA_IDXSET_FOREACH(am, ap->output_mappings, idx) { -+ cp->n_sinks++; -+ pa_droid_add_card_ports(cp, ports, am, u->core); -+ max_channels = popcount(am->output->channel_masks) > max_channels -+ ? popcount(am->output->channel_masks) : max_channels; -+ } -+ cp->max_sink_channels = max_channels; -+ -+ max_channels = 0; -+ PA_IDXSET_FOREACH(am, ap->input_mappings, idx) { -+ cp->n_sources++; -+ pa_droid_add_card_ports(cp, ports, am, u->core); -+ max_channels = popcount(am->input->channel_masks) > max_channels -+ ? popcount(am->input->channel_masks) : max_channels; - } -+ cp->max_source_channels = max_channels; - - d = PA_CARD_PROFILE_DATA(cp); - d->profile = ap; -+ d->virtual_profile = false; -+ d->mode = AUDIO_MODE_NORMAL; - - pa_hashmap_put(h, cp->name, cp); - } -@@ -280,6 +359,7 @@ static void add_profiles(struct userdata - static void init_profile(struct userdata *u) { - pa_droid_mapping *am; - struct profile_data *d; -+ uint32_t idx; - - pa_assert(u); - -@@ -287,14 +367,16 @@ static void init_profile(struct userdata - - d = PA_CARD_PROFILE_DATA(u->card->active_profile); - -- if (d->profile && d->profile->output) { -- am = d->profile->output; -- am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card); -+ if (d->profile && pa_idxset_size(d->profile->output_mappings) > 0) { -+ PA_IDXSET_FOREACH(am, d->profile->output_mappings, idx) { -+ am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card); -+ } - } - -- if (d->profile && d->profile->input) { -- am = d->profile->input; -- am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, &u->card_data, am, u->card); -+ if (d->profile && pa_idxset_size(d->profile->input_mappings) > 0) { -+ PA_IDXSET_FOREACH(am, d->profile->input_mappings, idx) { -+ am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card); -+ } - } - } - -@@ -332,77 +414,300 @@ static int set_mode(struct userdata *u, - } - - static void park_profile(pa_droid_profile *dp) { -+ struct profile_data *data; -+ pa_droid_mapping *am; -+ uint32_t idx; -+ - pa_assert(dp); - -- if (dp->output && dp->output->sink) -- pa_sink_set_port(dp->output->sink, PA_DROID_OUTPUT_PARKING, false); -- if (dp->input && dp->input->source) -- pa_source_set_port(dp->input->source, PA_DROID_INPUT_PARKING, false); -+ /* Virtual profiles don't have output mappings. */ -+ if (dp->output_mappings) { -+ PA_IDXSET_FOREACH(am, dp->output_mappings, idx) { -+ if (pa_droid_mapping_is_primary(am)) -+ pa_sink_set_port(am->sink, PA_DROID_OUTPUT_PARKING, false); -+ } -+ }; -+ -+ /* Virtual profiles don't have input mappings. */ -+ if (dp->input_mappings) { -+ PA_IDXSET_FOREACH(am, dp->input_mappings, idx) { -+ if (pa_droid_mapping_is_primary(am)) -+ pa_source_set_port(am->source, PA_DROID_INPUT_PARKING, false); -+ } -+ }; -+} -+ -+static bool voicecall_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) { -+ pa_card_profile *cp; -+ pa_droid_mapping *am_output, *am_input; -+ -+ pa_assert(u); -+ pa_assert(p); -+ pa_assert(u->old_profile); -+ -+ if (!(am_output = pa_droid_idxset_get_primary(u->old_profile->output_mappings))) { -+ pa_log("Active profile doesn't have primary output device."); -+ return false; -+ } -+ -+ if (!(am_input = pa_droid_idxset_get_primary(u->old_profile->input_mappings))) -+ pa_log_warn("Active profile doesn't have primary input device."); -+ -+ /* call mode specialities */ -+ if (enabling) { -+ pa_droid_sink_set_voice_control(am_output->sink, true); -+ if (am_input && !u->voice_source_routing) -+ pa_droid_source_set_routing(am_input->source, false); -+ if (am_input && am_input->input->devices & AUDIO_DEVICE_IN_VOICE_CALL && -+ (cp = pa_hashmap_get(u->card->profiles, VOICE_RECORD_PROFILE_NAME))) { -+ if (cp->available == PA_AVAILABLE_NO) { -+ pa_log_debug("Enable %s profile.", VOICE_RECORD_PROFILE_NAME); -+ pa_card_profile_set_available(cp, PA_AVAILABLE_YES); -+ } -+ } -+ } else { -+ pa_droid_sink_set_voice_control(am_output->sink, false); -+ if (am_input && !u->voice_source_routing) -+ pa_droid_source_set_routing(am_input->source, true); -+ if (am_input && am_input->input->devices & AUDIO_DEVICE_IN_VOICE_CALL && -+ (cp = pa_hashmap_get(u->card->profiles, VOICE_RECORD_PROFILE_NAME))) { -+ if (cp->available == PA_AVAILABLE_YES) { -+ pa_log_debug("Disable %s profile.", VOICE_RECORD_PROFILE_NAME); -+ pa_card_profile_set_available(cp, PA_AVAILABLE_NO); -+ } -+ } -+ } -+ -+ return true; -+} -+ -+#if DROID_HAL == 1 -+static bool voicecall_record_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) { -+ pa_queue *source_outputs = NULL; -+ pa_droid_mapping *am; -+ -+ pa_assert_ctl_context(); -+ pa_assert(u); -+ pa_assert(p); -+ pa_assert(u->old_profile); -+ -+ if (enabling) { -+ /* don't do anything if voicecall source has already been created. */ -+ if (u->voicecall_source) -+ return true; -+ -+ pa_log_info("Enabling voice call record."); -+ -+ am = pa_droid_idxset_get_primary(u->old_profile->input_mappings); -+ -+ if (am && am->source) { -+ source_outputs = pa_source_move_all_start(am->source, source_outputs); -+ pa_droid_source_free(am->source); -+ am->source = NULL; -+ } -+ -+ u->voicecall_source = pa_droid_source_new(u->module, u->modargs, __FILE__, AUDIO_DEVICE_IN_VOICE_CALL, &u->card_data, am, u->card); -+ if (!u->voicecall_source) -+ pa_log("Failed to enable voice call recording."); -+ -+ if (u->voicecall_source && source_outputs) { -+ pa_source_move_all_finish(u->voicecall_source, source_outputs, false); -+ source_outputs = NULL; -+ } -+ -+ } else { -+ /* don't do anything if voicecall source has already been destroyed. */ -+ if (!u->voicecall_source) -+ return true; -+ -+ pa_log_info("Disabling voice call record."); -+ -+ source_outputs = pa_source_move_all_start(u->voicecall_source, source_outputs); -+ pa_droid_source_free(u->voicecall_source); -+ u->voicecall_source = NULL; -+ -+ am = pa_droid_idxset_get_primary(u->old_profile->input_mappings); -+ -+ if (am && !am->source) { -+ am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card); -+ -+ if (source_outputs && am->source) { -+ pa_source_move_all_finish(am->source, source_outputs, false); -+ source_outputs = NULL; -+ } -+ } -+ } -+ -+ if (source_outputs) -+ pa_source_move_all_fail(source_outputs); -+ -+ return true; -+} -+ -+#else -+ -+static bool voicecall_record_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) { -+ pa_droid_mapping *am; -+ pa_device_port *port; -+ const char *port_name; -+ -+ pa_assert_ctl_context(); -+ pa_assert(u); -+ pa_assert(p); -+ pa_assert(u->old_profile); -+ -+ if (!(am = pa_droid_idxset_get_primary(u->old_profile->input_mappings))) { -+ pa_log("Active profile doesn't have primary input device. Cannot enable record profile."); -+ return false; -+ } -+ -+ if (!am->source) { -+ pa_log("No active source, refusing to switch source port."); -+ return false; -+ } -+ -+ pa_source_assert_ref(am->source); -+ -+ pa_assert_se(pa_droid_input_port_name(enabling ? AUDIO_DEVICE_IN_VOICE_CALL : AUDIO_DEVICE_IN_DEFAULT, -+ &port_name)); -+ pa_assert_se((port = pa_hashmap_get(am->source->ports, port_name))); -+ -+ if (pa_droid_source_set_port(am->source, port) != 0) -+ return false; -+ -+ pa_hook_fire(&u->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED], am->source); -+ -+ return true; -+} -+#endif -+ -+#ifdef DROID_AUDIO_HAL_USE_VSID -+static bool voicecall_vsid(struct userdata *u, pa_droid_profile *p, uint32_t vsid, bool enabling) -+{ -+ char *setparam; -+ -+ voicecall_profile_event_cb(u, p, enabling); -+ -+ setparam = pa_sprintf_malloc("%s=%u;%s=%d", AUDIO_PARAMETER_KEY_VSID, vsid, -+ AUDIO_PARAMETER_KEY_CALL_STATE, -+ enabling ? CALL_ACTIVE : CALL_INACTIVE); -+ -+ pa_droid_set_parameters(u->hw_module, setparam); -+ pa_xfree(setparam); -+ -+ return true; -+} -+ -+static bool voicecall_voice1_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) -+{ -+ return voicecall_vsid(u, p, VOICE_VSID, enabling); -+} -+ -+static bool voicecall_voice2_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) -+{ -+ return voicecall_vsid(u, p, VOICE2_VSID, enabling); -+} -+ -+static bool voicecall_volte_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) -+{ -+ return voicecall_vsid(u, p, VOLTE_VSID, enabling); -+} -+ -+static bool voicecall_qchat_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) -+{ -+ return voicecall_vsid(u, p, QCHAT_VSID, enabling); -+} -+ -+static bool voicecall_vowlan_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) -+{ -+ return voicecall_vsid(u, p, VOWLAN_VSID, enabling); -+} -+#endif /* DROID_AUDIO_HAL_USE_VSID */ -+ -+static void leave_virtual_profile(struct userdata *u, pa_card *c, pa_card_profile *cp, pa_card_profile *new_profile) { -+ struct profile_data *pd, *parent; -+ -+ pa_assert(u); -+ pa_assert(c); -+ pa_assert(cp); -+ pa_assert_se((pd = PA_CARD_PROFILE_DATA(cp))); -+ -+ if (pd->virtual.parent) -+ pa_log_debug("Leaving extension %s.", cp->name); -+ -+ /* First run event for old virtual profile, unless new profile is extension -+ * to the old profile. */ -+ if (pd->virtual.extension) { -+ if (pd->virtual.extension != new_profile && pd->virtual.event_cb) -+ pd->virtual.event_cb(u, pd->profile, false); -+ } else { -+ if (pd->virtual.event_cb) -+ pd->virtual.event_cb(u, pd->profile, false); -+ } -+ -+ /* If old virtual profile was extension, and new profile is not extensions parent, run event -+ * for extension's parent as well. */ -+ if (pd->virtual.parent != new_profile && pd->virtual.parent) { -+ parent = PA_CARD_PROFILE_DATA(pd->virtual.parent); -+ -+ if (parent->virtual.event_cb) -+ parent->virtual.event_cb(u, parent->profile, false); -+ } - } - - static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { - struct userdata *u; - pa_droid_mapping *am; -- struct virtual_profile *new_vp = NULL; -- struct virtual_profile *old_vp = NULL; - struct profile_data *nd, *od; - pa_queue *sink_inputs = NULL, *source_outputs = NULL; -+ uint32_t idx; - - pa_assert(c); - pa_assert(new_profile); - pa_assert_se(u = c->userdata); - -+ if (new_profile->available != PA_AVAILABLE_YES) { -+ pa_log("Profile %s is not available.", new_profile->name); -+ return -1; -+ } -+ - nd = PA_CARD_PROFILE_DATA(new_profile); - od = PA_CARD_PROFILE_DATA(c->active_profile); - -- if (nd->profile == u->call_profile.profile) -- new_vp = &u->call_profile; -- if (nd->profile == u->ring_profile.profile) -- new_vp = &u->ring_profile; -- if (nd->profile == u->comm_profile.profile) -- new_vp = &u->comm_profile; -- -- if (new_vp) { -- pa_log_debug("Setting new virtual profile."); -- if (u->old_profile == NULL) -+ if (nd->virtual_profile) { -+ /* old_profile should always be real profile. */ -+ if (u->old_profile == NULL) { -+ pa_assert(!od->virtual_profile); - u->old_profile = od->profile; -+ } -+ -+ if (od->virtual_profile) -+ leave_virtual_profile(u, c, c->active_profile, new_profile); - - park_profile(od->profile); - -- set_mode(u, new_vp->mode); -+ if (nd->mode != od->mode) -+ set_mode(u, nd->mode); -+ -+ if (od->virtual.parent != new_profile && nd->virtual.event_cb) -+ nd->virtual.event_cb(u, nd->profile, true); - -- /* call mode specialities */ -- if (new_vp->profile == u->call_profile.profile) { -- pa_droid_sink_set_voice_control(u->old_profile->output->sink, true); -- if (!u->voice_source_routing) -- pa_droid_source_set_routing(u->old_profile->input->source, false); -- } - return 0; - } - -- if (od->profile == u->call_profile.profile) -- old_vp = &u->call_profile; -- if (od->profile == u->ring_profile.profile) -- old_vp = &u->ring_profile; -- if (od->profile == u->comm_profile.profile) -- old_vp = &u->comm_profile; -- -- if (old_vp) { -+ if (od->virtual_profile) { - pa_assert(u->old_profile); - -- park_profile(nd->profile); -+ if (od->virtual_profile) -+ leave_virtual_profile(u, c, c->active_profile, NULL); - -- set_mode(u, AUDIO_MODE_NORMAL); -+ park_profile(nd->profile); - -- /* call mode specialities */ -- if (old_vp->profile == u->call_profile.profile) { -- pa_droid_sink_set_voice_control(u->old_profile->output->sink, false); -- if (!u->voice_source_routing) -- pa_droid_source_set_routing(u->old_profile->input->source, true); -- } -+ if (nd->mode != od->mode) -+ set_mode(u, nd->mode); - - /* If new profile is the same as from which we switched to -- * call profile, transfer ownership back to that profile. -+ * virtual profile, transfer ownership back to that profile. - * Otherwise destroy sinks & sources and switch to new profile. */ - if (nd->profile == u->old_profile) { - u->old_profile = NULL; -@@ -411,66 +716,64 @@ static int card_set_profile(pa_card *c, - od->profile = u->old_profile; - u->old_profile = NULL; - -- /* Continue to sink-input transfer below */ -+ /* Continue to sink-input/source-output transfer below. */ - } - } - - /* If there are connected sink inputs/source outputs in old profile's sinks/sources move - * them all to new sinks/sources. */ - -- if (od->profile && od->profile->output) { -- do { -- am = od->profile->output; -- -+ if (od->profile && pa_idxset_size(od->profile->output_mappings) > 0) { -+ PA_IDXSET_FOREACH(am, od->profile->output_mappings, idx) { - if (!am->sink) - continue; - -- if (nd->profile && nd->profile->output && am == nd->profile->output) -+ if (nd->profile && -+ pa_idxset_get_by_data(nd->profile->output_mappings, am, NULL)) - continue; - - sink_inputs = pa_sink_move_all_start(am->sink, sink_inputs); - pa_droid_sink_free(am->sink); - am->sink = NULL; -- } while(0); -+ } - } - -- if (od->profile && od->profile->input) { -- do { -- am = od->profile->input; -- -+ if (od->profile && pa_idxset_size(od->profile->input_mappings) > 0) { -+ PA_IDXSET_FOREACH(am, od->profile->input_mappings, idx) { - if (!am->source) - continue; - -- if (nd->profile && nd->profile->input && am == nd->profile->input) -+ if (nd->profile && -+ pa_idxset_get_by_data(nd->profile->input_mappings, am, NULL)) - continue; - - source_outputs = pa_source_move_all_start(am->source, source_outputs); - pa_droid_source_free(am->source); - am->source = NULL; -- } while(0); -+ } - } - -- if (nd->profile && nd->profile->output) { -- am = nd->profile->output; -- -- if (!am->sink) -- am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card); -+ if (nd->profile && pa_idxset_size(nd->profile->output_mappings) > 0) { -+ PA_IDXSET_FOREACH(am, nd->profile->output_mappings, idx) { -+ if (!am->sink) -+ am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card); - -- if (sink_inputs && am->sink) { -- pa_sink_move_all_finish(am->sink, sink_inputs, false); -- sink_inputs = NULL; -+ if (sink_inputs && am->sink) { -+ pa_sink_move_all_finish(am->sink, sink_inputs, false); -+ sink_inputs = NULL; -+ } - } - } - -- if (nd->profile && nd->profile->input) { -- am = nd->profile->input; -- -- if (!am->source) -- am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, &u->card_data, am, u->card); -+ if (nd->profile && pa_idxset_size(nd->profile->input_mappings) > 0) { -+ PA_IDXSET_FOREACH(am, nd->profile->input_mappings, idx) { -+ if (!am->source) -+ am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, (audio_devices_t) 0, &u->card_data, am, u->card); - -- if (source_outputs && am->source) { -- pa_source_move_all_finish(am->source, source_outputs, false); -- source_outputs = NULL; -+ if (source_outputs && am->source) { -+ pa_source_move_all_finish(am->source, source_outputs, false); -+ source_outputs = NULL; -+ } - } - } - -@@ -491,14 +794,18 @@ int pa__init(pa_module *m) { - const char *module_id; - bool namereg_fail = false; - bool voice_source_routing = false; -+ pa_card_profile *virtual; -+ const char *combine; - - pa_assert(m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -- pa_log("Failed to parse module argumets."); -+ pa_log("Failed to parse module arguments."); - goto fail; - } - -+ combine = pa_modargs_get_value(ma, "combine", NULL); -+ - struct userdata *u = pa_xnew0(struct userdata, 1); - u->core = m->core; - -@@ -521,7 +828,19 @@ int pa__init(pa_module *m) { - u->card_data.module_id = pa_xstrdup(module_id); - u->card_data.userdata = u; - -- u->profile_set = pa_droid_profile_set_new(u->hw_module->enabled_module); -+ if (combine) { -+ char *tmp; -+ pa_strlist *o = NULL; -+ -+ tmp = pa_replace(combine, ",", " "); -+ o = pa_strlist_parse(tmp); -+ -+ u->profile_set = pa_droid_profile_set_combined_new(u->hw_module->enabled_module, -+ o, NULL); -+ pa_strlist_free(o); -+ pa_xfree(tmp); -+ } else -+ u->profile_set = pa_droid_profile_set_new(u->hw_module->enabled_module); - - pa_card_new_data_init(&data); - data.driver = __FILE__; -@@ -549,15 +868,37 @@ int pa__init(pa_module *m) { - goto fail; - } - -- u->call_profile.profile = add_virtual_profile(u, VOICE_CALL_PROFILE_NAME, -- VOICE_CALL_PROFILE_DESC, data.profiles); -- u->call_profile.mode = AUDIO_MODE_IN_CALL; -- u->comm_profile.profile = add_virtual_profile(u, COMMUNICATION_PROFILE_NAME, -- COMMUNICATION_PROFILE_DESC, data.profiles); -- u->comm_profile.mode = AUDIO_MODE_IN_COMMUNICATION; -- u->ring_profile.profile = add_virtual_profile(u, RINGTONE_PROFILE_NAME, -- RINGTONE_PROFILE_DESC, data.profiles); -- u->ring_profile.mode = AUDIO_MODE_RINGTONE; -+ virtual = -+ add_virtual_profile(u, VOICE_CALL_PROFILE_NAME, VOICE_CALL_PROFILE_DESC, -+ AUDIO_MODE_IN_CALL, voicecall_profile_event_cb, -+ PA_AVAILABLE_YES, NULL, data.profiles); -+ add_virtual_profile(u, VOICE_RECORD_PROFILE_NAME, VOICE_RECORD_PROFILE_DESC, -+ AUDIO_MODE_IN_CALL, voicecall_record_profile_event_cb, -+ PA_AVAILABLE_NO, virtual, data.profiles); -+ add_virtual_profile(u, COMMUNICATION_PROFILE_NAME, COMMUNICATION_PROFILE_DESC, -+ AUDIO_MODE_IN_COMMUNICATION, NULL, -+ PA_AVAILABLE_YES, NULL, data.profiles); -+ add_virtual_profile(u, RINGTONE_PROFILE_NAME, RINGTONE_PROFILE_DESC, -+ AUDIO_MODE_RINGTONE, NULL, -+ PA_AVAILABLE_YES, NULL, data.profiles); -+#ifdef DROID_AUDIO_HAL_USE_VSID -+ add_virtual_profile(u, VOICE_SESSION_VOICE1_PROFILE_NAME, VOICE_SESSION_VOICE1_PROFILE_DESC, -+ AUDIO_MODE_IN_CALL, voicecall_voice1_vsid_profile_event_cb, -+ PA_AVAILABLE_YES, NULL, data.profiles); -+ add_virtual_profile(u, VOICE_SESSION_VOICE2_PROFILE_NAME, VOICE_SESSION_VOICE2_PROFILE_DESC, -+ AUDIO_MODE_IN_CALL, voicecall_voice2_vsid_profile_event_cb, -+ PA_AVAILABLE_YES, NULL, data.profiles); -+ /* TODO: Probably enabled state needs to be determined dynamically for VOLTE and friends. */ -+ add_virtual_profile(u, VOICE_SESSION_VOLTE_PROFILE_NAME, VOICE_SESSION_VOLTE_PROFILE_DESC, -+ AUDIO_MODE_IN_CALL, voicecall_volte_vsid_profile_event_cb, -+ PA_AVAILABLE_YES, NULL, data.profiles); -+ add_virtual_profile(u, VOICE_SESSION_QCHAT_PROFILE_NAME, VOICE_SESSION_QCHAT_PROFILE_DESC, -+ AUDIO_MODE_IN_CALL, voicecall_qchat_vsid_profile_event_cb, -+ PA_AVAILABLE_YES, NULL, data.profiles); -+ add_virtual_profile(u, VOICE_SESSION_VOWLAN_PROFILE_NAME, VOICE_SESSION_VOWLAN_PROFILE_DESC, -+ AUDIO_MODE_IN_CALL, voicecall_vowlan_vsid_profile_event_cb, -+ PA_AVAILABLE_YES, NULL, data.profiles); -+#endif /* DROID_AUDIO_HAL_USE_VSID */ - - add_disabled_profile(data.profiles); - -Index: pulseaudio/src/modules/droid/module-droid-discover.c -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/droid/module-droid-discover.c -@@ -0,0 +1,95 @@ -+/*** -+ This file is part of PulseAudio. -+ -+ Copyright 2016 Simon Fels -+ -+ PulseAudio is free software; you can redistribute it and/or modify -+ it under the terms of the GNU Lesser General Public License as -+ published by the Free Software Foundation; either version 2.1 of the -+ License, or (at your option) any later version. -+ -+ PulseAudio is distributed in the hope that it will be useful, but -+ WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public -+ License along with PulseAudio; if not, see . -+***/ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include -+#include -+#include -+ -+#include -+ -+#include "module-droid-discover-symdef.h" -+ -+PA_MODULE_AUTHOR("Simon Fels"); -+PA_MODULE_DESCRIPTION("Detect on which Android version we're running and load correct implementation for it"); -+PA_MODULE_VERSION(PACKAGE_VERSION); -+PA_MODULE_LOAD_ONCE(true); -+ -+#define ANDROID_PROPERTY_VERSION_RELEASE "ro.build.version.release" -+ -+#define MODULE_DROID_CARD_PREFIX "module-droid-card-" -+ -+struct userdata { -+ uint32_t module_idx; -+}; -+ -+int pa__init(pa_module* m) { -+ struct userdata *u; -+ pa_module *mm; -+ char version[50]; -+ char *module_name = NULL; -+ -+ pa_assert(m); -+ -+ if (property_get(ANDROID_PROPERTY_VERSION_RELEASE, version, "") < 0 || !version) { -+ pa_log("Failed to determine Android platform version we're running on"); -+ return -1; -+ } -+ -+ m->userdata = u = pa_xnew0(struct userdata, 1); -+ -+ if (!strncmp(version, "5.1", 3) && pa_module_exists(MODULE_DROID_CARD_PREFIX "22")) -+ module_name = MODULE_DROID_CARD_PREFIX "22"; -+ else if (!strncmp(version, "4.4", 3) && pa_module_exists(MODULE_DROID_CARD_PREFIX "19")) -+ module_name = MODULE_DROID_CARD_PREFIX "19"; -+ else { -+ pa_log("Unsupported Android version %s", version); -+ pa_xfree(u); -+ return -1; -+ } -+ -+ mm = pa_module_load(m->core, module_name, m->argument); -+ if (mm) -+ u->module_idx = mm->index; -+ -+ if (u->module_idx == PA_INVALID_INDEX) { -+ pa_xfree(u); -+ pa_log("Failed to load droid card module for Android version %s", version); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+void pa__done(pa_module* m) { -+ struct userdata *u; -+ -+ pa_assert(m); -+ -+ if (!(u = m->userdata)) -+ return; -+ -+ if (u->module_idx != PA_INVALID_INDEX) -+ pa_module_unload_by_index(m->core, u->module_idx, true); -+ -+ pa_xfree(u); -+} -Index: pulseaudio/src/modules/droid/module-droid-sink.c -=================================================================== ---- pulseaudio.orig/src/modules/droid/module-droid-sink.c -+++ pulseaudio/src/modules/droid/module-droid-sink.c -@@ -40,30 +40,36 @@ - #include "droid-util.h" - #include "droid-sink.h" - --#include "module-droid-sink-symdef.h" -+#if ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 2 -+#include "module-droid-sink-19-symdef.h" -+#elif ANDROID_VERSION_MAJOR == 5 && ANDROID_VERSION_MINOR == 1 -+#include "module-droid-sink-22-symdef.h" -+#endif - - PA_MODULE_AUTHOR("Juho Hämäläinen"); - PA_MODULE_DESCRIPTION("Droid sink"); - PA_MODULE_USAGE("master_sink= " -- "sink_name= " -- "sco_fake_sink="); -+ "sink_name="); - PA_MODULE_VERSION(PACKAGE_VERSION); - - static const char* const valid_modargs[] = { - "rate", -+ "format", -+ "channels", -+ "channel_map", -+ "sink_rate", -+ "sink_format", -+ "sink_channel_map", - "flags", -- "devices", -+ "output_devices", - "sink_name", - "module_id", - "mute_routing_before", - "mute_routing_after", - "sink_buffer", - "deferred_volume", -- "voice_volume_call_mode", - "voice_property_key", - "voice_property_value", -- "voice_virtual_stream", -- "sco_fake_sink", - NULL, - }; - -@@ -78,15 +84,24 @@ void pa__done(pa_module *m) { - - int pa__init(pa_module *m) { - pa_modargs *ma = NULL; -+ const char *flags_str; -+ audio_output_flags_t flags = 0; - - pa_assert(m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -- pa_log("Failed to parse module argumets."); -+ pa_log("Failed to parse module arguments."); - goto fail; - } - -- if (!(m->userdata = pa_droid_sink_new(m, ma, __FILE__, NULL, 0, NULL, NULL))) -+ if ((flags_str = pa_modargs_get_value(ma, "flags", NULL))) { -+ if (!pa_string_convert_flag_str_to_num(flags_str, &flags)) { -+ pa_log("Failed to parse flags"); -+ goto fail; -+ } -+ } -+ -+ if (!(m->userdata = pa_droid_sink_new(m, ma, __FILE__, NULL, flags, NULL, NULL))) - goto fail; - - pa_modargs_free(ma); -Index: pulseaudio/src/modules/droid/module-droid-source.c -=================================================================== ---- pulseaudio.orig/src/modules/droid/module-droid-source.c -+++ pulseaudio/src/modules/droid/module-droid-source.c -@@ -40,7 +40,11 @@ - #include "droid-util.h" - #include "droid-source.h" - --#include "module-droid-source-symdef.h" -+#if ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 2 -+#include "module-droid-source-19-symdef.h" -+#elif ANDROID_VERSION_MAJOR == 5 && ANDROID_VERSION_MINOR == 1 -+#include "module-droid-source-22-symdef.h" -+#endif - - PA_MODULE_AUTHOR("Juho Hämäläinen"); - PA_MODULE_DESCRIPTION("Droid source"); -@@ -50,8 +54,14 @@ PA_MODULE_VERSION(PACKAGE_VERSION); - - static const char* const valid_modargs[] = { - "rate", -+ "format", -+ "channels", -+ "channel_map", -+ "source_rate", -+ "source_format", -+ "source_channel_map", - "flags", -- "devices", -+ "input_devices", - "source_name", - "module_id", - "source_buffer", -@@ -74,11 +84,11 @@ int pa__init(pa_module *m) { - pa_assert(m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { -- pa_log("Failed to parse module argumets."); -+ pa_log("Failed to parse module arguments."); - goto fail; - } - -- if (!(m->userdata = pa_droid_source_new(m, ma, __FILE__, NULL, NULL, NULL))) -+ if (!(m->userdata = pa_droid_source_new(m, ma, __FILE__, (audio_devices_t) 0, NULL, NULL, NULL))) - goto fail; - - pa_modargs_free(ma); -Index: pulseaudio/src/modules/module-device-restore.c -=================================================================== ---- pulseaudio.orig/src/modules/module-device-restore.c -+++ pulseaudio/src/modules/module-device-restore.c -@@ -815,6 +815,7 @@ static pa_hook_result_t sink_port_hook_c - pa_assert(u); - pa_assert(u->restore_volume || u->restore_muted); - -+#define MODULE_DEVICE_RESTORE_SKIP_PROPERTY "module-device-restore.skip" - if (pa_proplist_gets(sink->proplist, MODULE_DEVICE_RESTORE_SKIP_PROPERTY)) - return PA_HOOK_OK; - diff -Nru pulseaudio-10.0/debian/patches/0601-droid-alternative-hw-module-id.patch pulseaudio-10.0/debian/patches/0601-droid-alternative-hw-module-id.patch --- pulseaudio-10.0/debian/patches/0601-droid-alternative-hw-module-id.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0601-droid-alternative-hw-module-id.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,38 +0,0 @@ -Subject: Add workaround for MTK devices -Author: Rex Tsai -Forwarded: no - -Index: pulseaudio/src/modules/droid/droid-util.c -=================================================================== ---- pulseaudio.orig/src/modules/droid/droid-util.c -+++ pulseaudio/src/modules/droid/droid-util.c -@@ -1358,8 +1358,13 @@ static pa_droid_hw_module *droid_hw_modu - - hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, module->name, (const hw_module_t**) &hwmod); - if (!hwmod) { -- pa_log("Failed to get hw module %s.", module->name); -- goto fail; -+ /* alternative hardware module id for MTK. */ -+ pa_log("Failed to get hw module id: %s name: %s, trying alternative.", AUDIO_HARDWARE_MODULE_ID, module->name); -+ hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID2, module->name, (const hw_module_t**) &hwmod); -+ if (!hwmod) { -+ pa_log("Failed to get hw module id: %s name: %s.", AUDIO_HARDWARE_MODULE_ID2, module->name); -+ goto fail; -+ } - } - - pa_log_info("Loaded hw module %s (HAL %d.%d.%d)", module->name, -Index: pulseaudio/src/modules/droid/droid-util.h -=================================================================== ---- pulseaudio.orig/src/modules/droid/droid-util.h -+++ pulseaudio/src/modules/droid/droid-util.h -@@ -54,6 +54,9 @@ - - #define PA_DROID_PRIMARY_DEVICE "primary" - -+/* Workaround for MTK */ -+#define AUDIO_HARDWARE_MODULE_ID2 "libaudio" -+ - typedef struct pa_droid_hw_module pa_droid_hw_module; - typedef struct pa_droid_stream pa_droid_stream; - typedef struct pa_droid_card_data pa_droid_card_data; diff -Nru pulseaudio-10.0/debian/patches/0602-droid-inputstream-config-parameters.pach pulseaudio-10.0/debian/patches/0602-droid-inputstream-config-parameters.pach --- pulseaudio-10.0/debian/patches/0602-droid-inputstream-config-parameters.pach 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0602-droid-inputstream-config-parameters.pach 1970-01-01 08:00:00.000000000 +0800 @@ -1,79 +0,0 @@ -Subject: Extend droid module to acept further config parameters -Author: Rex Tsai -Forwarded: no - -Index: pulseaudio/src/modules/droid/droid-util.c -=================================================================== ---- pulseaudio.orig/src/modules/droid/droid-util.c -+++ pulseaudio/src/modules/droid/droid-util.c -@@ -1134,7 +1158,6 @@ static void add_i_port(pa_droid_mapping - static void add_i_ports(pa_droid_mapping *am) { - pa_droid_port *p; - const char *name; -- char *desc; - uint32_t devices; - uint32_t i = 0; - -@@ -1697,11 +1723,27 @@ pa_droid_stream *pa_droid_open_input_str - &config_in, - &stream - #if DROID_HAL >= 3 -- , AUDIO_INPUT_FLAG_NONE /* Default to no input flags */ -- , NULL /* Don't define address */ -- , AUDIO_SOURCE_DEFAULT /* Default audio source */ -+ , AUDIO_INPUT_FLAG_NONE /* Default to no input flags */ -+ , NULL /* Don't define address */ -+ , AUDIO_SOURCE_DEFAULT /* Default audio source */ - #endif - ); -+ /* On some devices the first call will fail if the config parameters are -+ * not supported, but it'll automatically set the right ones, expecting -+ * the caller to call it again, so let's try at least one more time */ -+ if (!stream) -+ ret = module->device->open_input_stream(module->device, -+ module->stream_in_id++, -+ devices, -+ &config_in, -+ &stream -+#if DROID_HAL >= 3 -+ , AUDIO_INPUT_FLAG_NONE /* Default to no input flags */ -+ , NULL /* Don't define address */ -+ , AUDIO_SOURCE_DEFAULT /* Default audio source */ -+#endif -+ ); -+ - pa_droid_hw_module_unlock(module); - - if (ret < 0 || !stream) { -@@ -1715,6 +1757,22 @@ pa_droid_stream *pa_droid_open_input_str - s->channel_map = channel_map; - s->flags = 0; - -+ if (s->in->common.get_channels(&s->in->common) != hal_channel_mask) { -+ pa_log_warn("Requested channel mask %u but got %u instead.", hal_channel_mask, config_in.channel_mask); -+ /* convert the channel mask back to pa_channel_map -+ * some device support only mono channel. -+ * */ -+ pa_channel_position_t c; -+ if (pa_convert_input_channel(config_in.channel_mask, CONV_FROM_HAL, &c) && (c == PA_CHANNEL_POSITION_MONO)) { -+ s->channel_map.channels = 1; -+ s->channel_map.map[0] = PA_CHANNEL_POSITION_MONO; -+ s->sample_spec.channels = 1 ; -+ } else { -+ pa_log("Failed to convert channel map."); -+ goto fail; -+ } -+ } -+ - if ((s->sample_spec.rate = s->in->common.get_sample_rate(&s->in->common)) != spec->rate) - pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, s->sample_spec.rate); - -@@ -1727,7 +1785,7 @@ pa_droid_stream *pa_droid_open_input_str - devices, - s->flags, - s->sample_spec.rate, -- s->sample_spec.channels, hal_channel_mask, -+ s->sample_spec.channels, config_in.channel_mask, - s->sample_spec.format, hal_audio_format, - buffer_size, - pa_bytes_to_usec(buffer_size, &s->sample_spec)); diff -Nru pulseaudio-10.0/debian/patches/0603-droid-port-priority-and-availability.patch pulseaudio-10.0/debian/patches/0603-droid-port-priority-and-availability.patch --- pulseaudio-10.0/debian/patches/0603-droid-port-priority-and-availability.patch 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/patches/0603-droid-port-priority-and-availability.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,107 +0,0 @@ -Subject: Bring back proper jack detection -Author: Rex Tsai -Forwarded: no - -Index: pulseaudio/src/modules/droid/droid-util.c -=================================================================== ---- pulseaudio.orig/src/modules/droid/droid-util.c -+++ pulseaudio/src/modules/droid/droid-util.c -@@ -110,6 +110,20 @@ static bool string_convert_str_to_num(co - return false; - } - -+static bool check_port_availability(const char *port) { -+ pa_assert(port); -+ -+ pa_log_debug("Checking availability for port '%s'", port); -+ -+ for (unsigned int i = 0; port_availability[i]; i++) { -+ if (pa_streq(port_availability[i], port)) { -+ return true; -+ } -+ } -+ -+ return false; -+} -+ - static char *list_string(struct string_conversion *list, uint32_t flags) { - char *str = NULL; - char *tmp; -@@ -1035,6 +1049,9 @@ static pa_droid_port *create_o_port(pa_d - if (am->profile_set->config->global_config.default_output_device & device) - p->priority += DEFAULT_PRIORITY; - -+ if (check_port_availability(p->name)) -+ p->priority += (DEFAULT_PRIORITY * 3); -+ - return p; - } - -@@ -1124,6 +1141,13 @@ static void add_i_port(pa_droid_mapping - if (am->profile_set->config->global_config.attached_input_devices & device) - p->priority += DEFAULT_PRIORITY; - -+ /* Make builtin mic the default input device */ -+ if (device == AUDIO_DEVICE_IN_BUILTIN_MIC) -+ p->priority += DEFAULT_PRIORITY; -+ -+ if (check_port_availability(p->name)) -+ p->priority += (DEFAULT_PRIORITY * 3); -+ - pa_hashmap_put(am->profile_set->all_ports, p->name, p); - } else - pa_log_debug(" Input port %s from cache", name); -@@ -1297,6 +1321,9 @@ static int add_ports(pa_core *core, pa_c - } else - pa_log_debug(" Port %s from cache", p->name); - -+ /* If port/jack detection is available, start as not available by default */ -+ dp->available = check_port_availability(p->name) ? PA_AVAILABLE_NO : PA_AVAILABLE_UNKNOWN; -+ - if (cp) { - if (!pa_hashmap_get(dp->profiles, cp->name)) - pa_hashmap_put(dp->profiles, cp->name, cp); -@@ -1749,6 +1776,9 @@ pa_droid_stream *pa_droid_open_input_str - if ((s->sample_spec.rate = s->in->common.get_sample_rate(&s->in->common)) != spec->rate) - pa_log_warn("Requested sample rate %u but got %u instead.", spec->rate, s->sample_spec.rate); - -+ if (s->sample_spec.channels != spec->channels) -+ pa_log_warn("Requested chennel %u but got %u instead.", spec->channels, s->sample_spec.channels); -+ - pa_idxset_put(module->inputs, s, NULL); - - buffer_size = s->in->common.get_buffer_size(&s->in->common); -Index: pulseaudio/src/modules/droid/droid-util-44.h -=================================================================== ---- pulseaudio.orig/src/modules/droid/droid-util-44.h -+++ pulseaudio/src/modules/droid/droid-util-44.h -@@ -346,4 +346,12 @@ struct string_conversion string_conversi - }; - #undef STRING_ENTRY - -+/* Ports with availability option (for port/jack detection) */ -+static const char* port_availability[] = { -+ "output-wired_headset", -+ "output-wired_headphone", -+ "input-wired_headset", -+ NULL -+}; -+ - #endif -Index: pulseaudio/src/modules/droid/droid-util-51.h -=================================================================== ---- pulseaudio.orig/src/modules/droid/droid-util-51.h -+++ pulseaudio/src/modules/droid/droid-util-51.h -@@ -402,4 +402,12 @@ struct string_conversion string_conversi - }; - #undef STRING_ENTRY - -+/* Ports with availability option (for port/jack detection) */ -+static const char* port_availability[] = { -+ "output-wired_headset", -+ "output-wired_headphone", -+ "input-wired_headset", -+ NULL -+}; -+ - #endif diff -Nru pulseaudio-10.0/debian/patches/series pulseaudio-10.0/debian/patches/series --- pulseaudio-10.0/debian/patches/series 2017-07-11 16:47:59.000000000 +0800 +++ pulseaudio-10.0/debian/patches/series 2017-08-07 17:16:14.000000000 +0800 @@ -5,16 +5,6 @@ 0022-inotify-wrapper-Quit-daemon-if-pid-file-is-removed.patch 0030-load-module-switch-on-connect.patch -# Ubuntu touch stuff -0202-dont-probe-ucm.patch -0203-card-Add-hook-before-profile-changes.patch -0206-module-bluetooth-discover-adding-module-option-profi.patch -0207-Enable-pulseaudio-droid.patch -0208-module-bluetooth-device-Allow-leaving-transport-runn.patch -0209-module-switch-on-connect-adding-parameter-to-allow-s.patch -0210-module-device-restore-adding-property-to-skip.patch -0211-corking-a-sink-input-stream-when-stalled.patch - # Ubuntu touch: support for trust-store 0406-tagstruct-add-copy-method.patch 0407-access-Add-access-control-hooks.patch @@ -23,24 +13,6 @@ 0410-Add-thread-to-activate-trust-store-interface.patch 0417-increase-timeout-check-apparmor.patch -# Ubuntu touch: enable bluez5 HFP over ofono support -0501-bluetooth-bluez5-ofono-add-support-for-HFP-gateway-r.patch -0502-bluetooth-bluez5-bring-back-SCO-over-PCM-support.patch -0503-bluetooth-bluez5-ofono-add-support-for-spekaer-micro.patch -0504-bluetooth-bluez5-add-support-for-both-mode.patch -0505-bluetooth-bluez5-let-user-specify-a-default-profile-.patch -0506-bluetooth-bluez5-prevent-SCO-sink-source-to-be-suspe.patch -0507-bluetooth-bluez5-drop-save-restore-of-SCO-sink-sourc.patch -0508-bluetooth-bluez5-add-guards-to-prevent-HFP-and-HSP-c.patch -0509-bluetooth-bluez5-don-t-reactivate-default-profile-wh.patch -0510-Further-fixes-for-HFP-A2DP-with-BlueZ-5.x.patch - -# Ubuntu touch: add support for Android 5.x -0600-droid-sync-with-upstream-for-Android-5-support-and-b.patch -0601-droid-alternative-hw-module-id.patch -0602-droid-inputstream-config-parameters.pach -0603-droid-port-priority-and-availability.patch - # Ubuntu Snappy 0700-modules-add-snappy-policy-module.patch diff -Nru pulseaudio-10.0/debian/pulseaudio-module-droid.install pulseaudio-10.0/debian/pulseaudio-module-droid.install --- pulseaudio-10.0/debian/pulseaudio-module-droid.install 2017-07-11 16:17:05.000000000 +0800 +++ pulseaudio-10.0/debian/pulseaudio-module-droid.install 1970-01-01 08:00:00.000000000 +0800 @@ -1,7 +0,0 @@ -usr/lib/pulse-*/modules/libdroid-util-*.so -usr/lib/pulse-*/modules/libdroid-sink-*.so -usr/lib/pulse-*/modules/libdroid-source-*.so -usr/lib/pulse-*/modules/module-droid-sink-*.so -usr/lib/pulse-*/modules/module-droid-source-*.so -usr/lib/pulse-*/modules/module-droid-card-*.so -usr/lib/pulse-*/modules/module-droid-discover.so