├── src ├── meson.build ├── .gitignore ├── common │ ├── libdroid-util.pc.in │ ├── config-parser-xml.h │ ├── include │ │ └── droid │ │ │ ├── utils.h │ │ │ ├── version.h │ │ │ ├── sllist.h │ │ │ ├── droid-config.h │ │ │ ├── conversion.h │ │ │ └── droid-util.h │ ├── meson.build │ ├── utils.c │ ├── sllist.c │ ├── droid-config.c │ ├── conversion.c │ └── droid-util-audio.h └── droid │ ├── droid-source.h │ ├── droid-sink.h │ ├── meson.build │ ├── module-droid-source.c │ ├── module-droid-sink.c │ ├── droid-source.c │ └── module-droid-card.c ├── .gitignore ├── git-version-gen ├── meson_options.txt ├── rpm └── pulseaudio-modules-droid.spec ├── meson.build ├── README.md └── COPYING /src/meson.build: -------------------------------------------------------------------------------- 1 | subdir('common') 2 | subdir('droid') 3 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | .libs 3 | *.la 4 | *.lo 5 | *.o 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | Makefile.in 3 | aclocal.m4 4 | autom4te.cache 5 | compile 6 | config.guess 7 | config.h.in 8 | config.log 9 | config.sub 10 | configure 11 | depcomp 12 | install-sh 13 | ltmain.sh 14 | missing 15 | Makefile 16 | config.h 17 | *~ 18 | config.status 19 | libtool 20 | *.pc 21 | stamp-* 22 | -------------------------------------------------------------------------------- /src/common/libdroid-util.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=${prefix} 3 | libdir=@libdir@ 4 | includedir=${prefix}/include 5 | libexecdir=@libexecdir@ 6 | 7 | Name: libdroid-util 8 | Description: Common droid module building interface. 9 | Version: @PA_MODULE_VERSION@ 10 | Libs: -L${libdir}/pulse-@PA_MAJORMINOR@/modules -ldroid-util 11 | Cflags: -D_REENTRANT -I${includedir}/pulsecore/modules 12 | -------------------------------------------------------------------------------- /git-version-gen: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if test $# -lt 1 4 | then 5 | echo 1>&2 "Usage: $0 \$srcdir/.tarball-version" 6 | exit 1 7 | fi 8 | 9 | VF=$1 10 | 11 | # First see if there is a version file, 12 | # then try git-describe, otherwise fail. 13 | if test -f $VF 14 | then 15 | VN=$(cat $VF) 16 | elif test -d ${GIT_DIR:-.git} -o -f .git && 17 | V=$(git describe --match "[0-9]*" --abbrev=7 --tags 2>/dev/null) 18 | then 19 | VN=$V 20 | else 21 | echo 1>&2 "$0: Failed to determine revision" 22 | exit 1 23 | fi 24 | 25 | # Omit the trailing newline, so that m4_esyscmd can use the result directly. 26 | echo "$VN" | tr -d '\012' 27 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('atomic-arm-linux-helpers', 2 | type : 'boolean', 3 | value : true, 4 | description : 'Use inline asm or libatomic_ops instead') 5 | option('atomic-arm-memory-barrier', 6 | type : 'boolean', 7 | value : false, 8 | description : 'Enable memory barriers (only really needed in SMP arm systems)') 9 | option('droid-device', 10 | type : 'string', 11 | value : 'generic', 12 | description : 'Droid device type for possible specific quirks (defaults to generic).') 13 | option('modlibexecdir', 14 | type : 'string', 15 | description : 'Specify location where modules will be installed') 16 | -------------------------------------------------------------------------------- /src/common/config-parser-xml.h: -------------------------------------------------------------------------------- 1 | #ifndef foodroidconfigparserxmlfoo 2 | #define foodroidconfigparserxmlfoo 3 | 4 | /* 5 | * Copyright (C) 2022 Jolla Ltd. 6 | * 7 | * Contact: Juho Hämäläinen 8 | * 9 | * These PulseAudio Modules are free software; you can redistribute 10 | * it and/or modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation 12 | * version 2.1 of the License. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 22 | * USA. 23 | */ 24 | 25 | #ifdef HAVE_CONFIG_H 26 | #include 27 | #endif 28 | 29 | #include 30 | 31 | dm_config_device *pa_parse_droid_audio_config_xml(const char *filename); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/common/include/droid/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef foodroidcommonutilsfoo 2 | #define foodroidcommonutilsfoo 3 | 4 | /* 5 | * Copyright (C) 2022 Jolla Ltd. 6 | * 7 | * Contact: Juho Hämäläinen 8 | * 9 | * These PulseAudio Modules are free software; you can redistribute 10 | * it and/or modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation 12 | * version 2.1 of the License. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 22 | * USA. 23 | */ 24 | 25 | #ifdef HAVE_CONFIG_H 26 | #include 27 | #endif 28 | 29 | void dm_replace_in_place(char **string, const char *a, const char *b); 30 | bool dm_strcasestr(const char *haystack, const char *needle); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/droid/droid-source.h: -------------------------------------------------------------------------------- 1 | #ifndef foodroidsourcefoo 2 | #define foodroidsourcefoo 3 | 4 | /* 5 | * Copyright (C) 2013-2022 Jolla Ltd. 6 | * 7 | * Contact: Juho Hämäläinen 8 | * 9 | * These PulseAudio Modules are free software; you can redistribute 10 | * it and/or modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation 12 | * version 2.1 of the License. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 22 | * USA. 23 | */ 24 | 25 | #ifdef HAVE_CONFIG_H 26 | #include 27 | #endif 28 | 29 | #ifdef HAVE_VALGRIND_MEMCHECK_H 30 | #include 31 | #endif 32 | 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | 46 | pa_source *pa_droid_source_new(pa_module *m, 47 | pa_modargs *ma, 48 | const char *driver, 49 | pa_droid_card_data *card_data, 50 | pa_droid_mapping *am, 51 | pa_card *card); 52 | void pa_droid_source_free(pa_source *s); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/common/meson.build: -------------------------------------------------------------------------------- 1 | libdroid_util_sources = [ 2 | 'config-parser-xml.c', 3 | 'config-parser-xml.h', 4 | 'conversion.c', 5 | 'droid-util.c', 6 | 'droid-util-audio.h', 7 | 'droid-config.c', 8 | 'sllist.c', 9 | 'utils.c', 10 | ] 11 | 12 | libdroid_util_headers = [ 13 | 'include/droid/conversion.h', 14 | 'include/droid/droid-config.h', 15 | 'include/droid/droid-util.h', 16 | 'include/droid/sllist.h', 17 | 'include/droid/utils.h', 18 | 'include/droid/version.h', 19 | ] 20 | 21 | libdroid_util_deps = [ 22 | droid_headers_dep, 23 | expat_dep, 24 | hybris_dep, 25 | hybris_common_dep, 26 | ltdl_dep, 27 | pulsecore_dep, 28 | ] 29 | 30 | install_headers(libdroid_util_headers, subdir : 'pulsecore/modules/droid') 31 | 32 | libdroid_util = library('droid-util', 33 | libdroid_util_sources, 34 | c_args : [pa_c_args, '-DPULSEAUDIO_VERSION=@0@'.format(pa_version_major)], 35 | dependencies : libdroid_util_deps, 36 | pic : true, 37 | include_directories : [configinc, include_directories('include')], 38 | install : true, 39 | install_dir : modlibexecdir, 40 | install_rpath : rpath_dirs, 41 | ) 42 | 43 | libdroid_util_dep = declare_dependency( 44 | link_with : libdroid_util, 45 | compile_args : pa_c_args + ['-DPULSEAUDIO_VERSION=@0@'.format(pa_version_major)], 46 | dependencies : libdroid_util_deps, 47 | include_directories : [configinc, include_directories('include')], 48 | ) 49 | 50 | # pkgconfig 51 | pc_cdata = configuration_data() 52 | 53 | pc_cdata.set('prefix', get_option('prefix')) 54 | pc_cdata.set('libdir', libdir) 55 | pc_cdata.set('libexecdir', get_option('libexecdir')) 56 | pc_cdata.set('PA_MAJORMINOR', pa_version_major_minor) 57 | pc_cdata.set('PA_MODULE_VERSION', pa_version_module) 58 | 59 | configure_file( 60 | input : 'libdroid-util.pc.in', 61 | output : 'libdroid-util.pc', 62 | configuration : pc_cdata, 63 | install_dir : join_paths(libdir, 'pkgconfig') 64 | ) 65 | -------------------------------------------------------------------------------- /src/droid/droid-sink.h: -------------------------------------------------------------------------------- 1 | #ifndef foodroidsinkfoo 2 | #define foodroidsinkfoo 3 | 4 | /* 5 | * Copyright (C) 2013-2018 Jolla Ltd. 6 | * 7 | * Contact: Juho Hämäläinen 8 | * 9 | * These PulseAudio Modules are free software; you can redistribute 10 | * it and/or modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation 12 | * version 2.1 of the License. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 22 | * USA. 23 | */ 24 | 25 | #ifdef HAVE_CONFIG_H 26 | #include 27 | #endif 28 | 29 | #ifdef HAVE_VALGRIND_MEMCHECK_H 30 | #include 31 | #endif 32 | 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | 46 | pa_sink *pa_droid_sink_new(pa_module *m, 47 | pa_modargs *ma, 48 | const char *driver, 49 | pa_droid_card_data *card_data, 50 | audio_output_flags_t flags, 51 | pa_droid_mapping *am, 52 | pa_card *card); 53 | void pa_droid_sink_free(pa_sink *s); 54 | 55 | void pa_droid_sink_set_voice_control(pa_sink* sink, bool enable); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/common/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Jolla Ltd. 3 | * 4 | * Contact: Juho Hämäläinen 5 | * 6 | * These PulseAudio Modules are free software; you can redistribute 7 | * it and/or modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation 9 | * version 2.1 of the License. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 | * USA. 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include "droid/utils.h" 32 | 33 | void dm_replace_in_place(char **string, const char *a, const char *b) { 34 | char *tmp; 35 | 36 | pa_assert(*string); 37 | pa_assert(a); 38 | pa_assert(b); 39 | 40 | tmp = pa_replace(*string, a, b); 41 | pa_xfree(*string); 42 | *string = tmp; 43 | } 44 | 45 | /* Simple strcasestr replacement. */ 46 | bool dm_strcasestr(const char *haystack, const char *needle) { 47 | size_t len_haystack, len_needle; 48 | 49 | len_haystack = strlen(haystack); 50 | len_needle = strlen(needle); 51 | 52 | if (len_needle > len_haystack) 53 | return false; 54 | 55 | for (size_t i = 0; i < len_haystack; i++) { 56 | if (len_needle > len_haystack - i) 57 | return false; 58 | 59 | if (strncasecmp(haystack + i, needle, len_needle) == 0) 60 | return true; 61 | } 62 | 63 | return false; 64 | } 65 | -------------------------------------------------------------------------------- /src/droid/meson.build: -------------------------------------------------------------------------------- 1 | droid_sink = library('droid-sink', 2 | ['droid-sink.c', 'droid-sink.h'], 3 | dependencies : libdroid_util_dep, 4 | pic : true, 5 | include_directories : configinc, 6 | install : true, 7 | install_dir : modlibexecdir, 8 | install_rpath : rpath_dirs, 9 | ) 10 | 11 | droid_sink_dep = declare_dependency( 12 | link_with : droid_sink, 13 | compile_args : pa_c_args + ['-DPULSEAUDIO_VERSION=@0@'.format(pa_version_major)], 14 | dependencies : libdroid_util_dep, 15 | include_directories : [configinc], 16 | ) 17 | 18 | droid_source = library('droid-source', 19 | ['droid-source.c', 'droid-source.h'], 20 | dependencies : libdroid_util_dep, 21 | pic : true, 22 | install : true, 23 | install_dir : modlibexecdir, 24 | install_rpath : rpath_dirs, 25 | ) 26 | 27 | droid_source_dep = declare_dependency( 28 | link_with : droid_source, 29 | compile_args : pa_c_args + ['-DPULSEAUDIO_VERSION=@0@'.format(pa_version_major)], 30 | dependencies : libdroid_util_dep, 31 | include_directories : [configinc], 32 | ) 33 | 34 | module_sink = shared_module('module-droid-sink', 35 | 'module-droid-sink.c', 36 | name_prefix : '', 37 | c_args : '-DPA_MODULE_NAME=module_droid_sink', 38 | dependencies : [droid_sink_dep, libdroid_util_dep], 39 | install : true, 40 | install_dir : modlibexecdir, 41 | install_rpath : rpath_dirs, 42 | ) 43 | 44 | module_sink = shared_module('module-droid-source', 45 | 'module-droid-source.c', 46 | name_prefix : '', 47 | c_args : '-DPA_MODULE_NAME=module_droid_source', 48 | dependencies : [droid_source_dep, libdroid_util_dep], 49 | install : true, 50 | install_dir : modlibexecdir, 51 | install_rpath : rpath_dirs, 52 | ) 53 | 54 | module_sink = shared_module('module-droid-card', 55 | 'module-droid-card.c', 56 | name_prefix : '', 57 | c_args : '-DPA_MODULE_NAME=module_droid_card', 58 | dependencies : [droid_sink_dep, droid_source_dep, libdroid_util_dep], 59 | install : true, 60 | install_dir : modlibexecdir, 61 | install_rpath : rpath_dirs, 62 | ) 63 | -------------------------------------------------------------------------------- /src/common/include/droid/version.h: -------------------------------------------------------------------------------- 1 | #ifndef foodroidversionfoo 2 | #define foodroidversionfoo 3 | 4 | /* 5 | * Copyright (C) 2018-2022 Jolla Ltd. 6 | * 7 | * Contact: Juho Hämäläinen 8 | * 9 | * These PulseAudio Modules are free software; you can redistribute 10 | * it and/or modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation 12 | * version 2.1 of the License. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 22 | * USA. 23 | */ 24 | 25 | 26 | #include 27 | #if defined(QCOM_BSP) 28 | #define QCOM_HARDWARE 29 | #endif 30 | 31 | #include 32 | 33 | #if !defined(ANDROID_VERSION_MAJOR) || !defined(ANDROID_VERSION_MINOR) || !defined(ANDROID_VERSION_PATCH) 34 | #error "ANDROID_VERSION_* not defined. Did you get your headers via extract-headers.sh?" 35 | #endif 36 | 37 | /* We currently support API version up to 3.1 */ 38 | #define DROID_API_VERSION_SUPPORT HARDWARE_DEVICE_API_VERSION(3, 1) 39 | 40 | #if AUDIO_DEVICE_API_VERSION_CURRENT > DROID_API_VERSION_SUPPORT 41 | #warning Compiling against higher audio device API version than currently supported! 42 | #warning Compile likely fails or module may malfunction. 43 | #endif 44 | 45 | #define AUDIO_API_VERSION_MAJ ((AUDIO_DEVICE_API_VERSION_CURRENT >> 8) & 0xff) 46 | #define AUDIO_API_VERSION_MIN (AUDIO_DEVICE_API_VERSION_CURRENT & 0xff) 47 | 48 | #define AUDIO_API_VERSION_GET_MAJ(x) ((x >> 8) & 0xff) 49 | #define AUDIO_API_VERSION_GET_MIN(x) (x & 0xff) 50 | 51 | #if AUDIO_API_VERSION_MAJ < 3 52 | #error This module only supports audio API version 3 and upwards. 53 | #endif 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /rpm/pulseaudio-modules-droid.spec: -------------------------------------------------------------------------------- 1 | %define pulseversion %{expand:%(rpm -q --qf '[%%{version}]' pulseaudio)} 2 | %define pulsemajorminor %{expand:%(echo '%{pulseversion}' | cut -d+ -f1)} 3 | %define moduleversion %{pulsemajorminor}.%{expand:%(echo '%{version}' | cut -d. -f3)} 4 | 5 | Name: pulseaudio-modules-droid 6 | 7 | Summary: PulseAudio Droid HAL modules 8 | Version: %{pulsemajorminor}.104 9 | Release: 1 10 | License: LGPLv2+ 11 | URL: https://github.com/mer-hybris/pulseaudio-modules-droid 12 | Source0: %{name}-%{version}.tar.bz2 13 | Requires: pulseaudio >= %{pulseversion} 14 | Requires: %{name}-common = %{version}-%{release} 15 | Requires: pulseaudio-module-keepalive >= 1.0.0 16 | BuildRequires: libtool-ltdl-devel 17 | BuildRequires: meson 18 | BuildRequires: ccache 19 | BuildRequires: pkgconfig(pulsecore) >= %{pulsemajorminor} 20 | BuildRequires: pkgconfig(android-headers) 21 | BuildRequires: pkgconfig(libhardware) 22 | BuildRequires: pkgconfig(expat) 23 | 24 | %description 25 | PulseAudio Droid HAL modules. 26 | 27 | %package common 28 | Summary: Common libs for the PulseAudio droid modules 29 | Requires: pulseaudio >= %{pulseversion} 30 | 31 | %description common 32 | This contains common libs for the PulseAudio droid modules. 33 | 34 | %package devel 35 | Summary: Development files for PulseAudio droid modules 36 | Requires: %{name}-common = %{version}-%{release} 37 | Requires: pulseaudio >= %{pulseversion} 38 | 39 | %description devel 40 | This contains development files for PulseAudio droid modules. 41 | 42 | %prep 43 | %autosetup -n %{name}-%{version} 44 | 45 | %build 46 | echo "%{moduleversion}" > .tarball-version 47 | # Obtain the DEVICE from the same source as used in /etc/os-release 48 | if [ -e "%{_includedir}/droid-devel/hw-release.vars" ]; then 49 | . %{_includedir}/droid-devel/hw-release.vars 50 | else 51 | . %{_libdir}/droid-devel/hw-release.vars 52 | fi 53 | 54 | %meson -Ddroid-device=$MER_HA_DEVICE 55 | %meson_build 56 | 57 | %install 58 | %meson_install 59 | 60 | %files 61 | %{_libdir}/pulse-*/modules/libdroid-sink.so 62 | %{_libdir}/pulse-*/modules/libdroid-source.so 63 | %{_libdir}/pulse-*/modules/module-droid-sink.so 64 | %{_libdir}/pulse-*/modules/module-droid-source.so 65 | %{_libdir}/pulse-*/modules/module-droid-card.so 66 | %license COPYING 67 | 68 | %files common 69 | %{_libdir}/pulse-*/modules/libdroid-util.so 70 | 71 | %files devel 72 | %dir %{_includedir}/pulsecore/modules/droid 73 | %{_includedir}/pulsecore/modules/droid/conversion.h 74 | %{_includedir}/pulsecore/modules/droid/droid-config.h 75 | %{_includedir}/pulsecore/modules/droid/droid-util.h 76 | %{_includedir}/pulsecore/modules/droid/sllist.h 77 | %{_includedir}/pulsecore/modules/droid/utils.h 78 | %{_includedir}/pulsecore/modules/droid/version.h 79 | %{_libdir}/pkgconfig/*.pc 80 | -------------------------------------------------------------------------------- /src/droid/module-droid-source.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2022 Jolla Ltd. 3 | * 4 | * Contact: Juho Hämäläinen 5 | * 6 | * These PulseAudio Modules are free software; you can redistribute 7 | * it and/or modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation 9 | * version 2.1 of the License. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 | * USA. 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | 26 | #ifdef HAVE_VALGRIND_MEMCHECK_H 27 | #include 28 | #endif 29 | 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include "droid-source.h" 42 | 43 | PA_MODULE_AUTHOR("Juho Hämäläinen"); 44 | PA_MODULE_DESCRIPTION("Droid source"); 45 | PA_MODULE_USAGE("master_source= " 46 | "source_name="); 47 | PA_MODULE_VERSION(PACKAGE_VERSION); 48 | 49 | static const char* const valid_modargs[] = { 50 | "config", 51 | "rate", 52 | "format", 53 | "channels", 54 | "channel_map", 55 | "source_rate", 56 | "source_format", 57 | "source_channel_map", 58 | "flags", 59 | "input_devices", 60 | "source_name", 61 | "module_id", 62 | "source_buffer", 63 | "deferred_volume", 64 | NULL, 65 | }; 66 | 67 | void pa__done(pa_module *m) { 68 | pa_source *source; 69 | 70 | pa_assert(m); 71 | 72 | if ((source = m->userdata)) 73 | pa_droid_source_free(source); 74 | } 75 | 76 | int pa__init(pa_module *m) { 77 | pa_modargs *ma = NULL; 78 | 79 | pa_assert(m); 80 | 81 | if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 82 | pa_log("Failed to parse module arguments."); 83 | goto fail; 84 | } 85 | 86 | if (!(m->userdata = pa_droid_source_new(m, ma, __FILE__, NULL, NULL, NULL))) 87 | goto fail; 88 | 89 | pa_modargs_free(ma); 90 | 91 | return 0; 92 | 93 | fail: 94 | if (ma) 95 | pa_modargs_free(ma); 96 | 97 | pa__done(m); 98 | 99 | return -1; 100 | } 101 | -------------------------------------------------------------------------------- /src/droid/module-droid-sink.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2018 Jolla Ltd. 3 | * 4 | * Contact: Juho Hämäläinen 5 | * 6 | * These PulseAudio Modules are free software; you can redistribute 7 | * it and/or modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation 9 | * version 2.1 of the License. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 | * USA. 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | 26 | #ifdef HAVE_VALGRIND_MEMCHECK_H 27 | #include 28 | #endif 29 | 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | #include "droid-sink.h" 43 | 44 | PA_MODULE_AUTHOR("Juho Hämäläinen"); 45 | PA_MODULE_DESCRIPTION("Droid sink"); 46 | PA_MODULE_USAGE("master_sink= " 47 | "sink_name="); 48 | PA_MODULE_VERSION(PACKAGE_VERSION); 49 | 50 | static const char* const valid_modargs[] = { 51 | "config", 52 | "rate", 53 | "format", 54 | "channels", 55 | "channel_map", 56 | "sink_rate", 57 | "sink_format", 58 | "sink_channel_map", 59 | "sink_mix_route", 60 | "flags", 61 | "output", 62 | "output_devices", 63 | "sink_name", 64 | "module_id", 65 | "mute_routing_before", 66 | "mute_routing_after", 67 | "prewrite_on_resume", 68 | "sink_buffer", 69 | "deferred_volume", 70 | "voice_property_key", 71 | "voice_property_value", 72 | NULL, 73 | }; 74 | 75 | void pa__done(pa_module *m) { 76 | pa_sink *sink; 77 | 78 | pa_assert(m); 79 | 80 | if ((sink = m->userdata)) 81 | pa_droid_sink_free(sink); 82 | } 83 | 84 | int pa__init(pa_module *m) { 85 | pa_modargs *ma = NULL; 86 | const char *flags_str; 87 | audio_output_flags_t flags = 0; 88 | 89 | pa_assert(m); 90 | 91 | if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 92 | pa_log("Failed to parse module arguments."); 93 | goto fail; 94 | } 95 | 96 | if ((flags_str = pa_modargs_get_value(ma, "flags", NULL))) { 97 | if (!pa_string_convert_flag_str_to_num(flags_str, &flags)) { 98 | pa_log("Failed to parse flags"); 99 | goto fail; 100 | } 101 | } 102 | 103 | if (!(m->userdata = pa_droid_sink_new(m, ma, __FILE__, NULL, flags, NULL, NULL))) 104 | goto fail; 105 | 106 | pa_modargs_free(ma); 107 | 108 | return 0; 109 | 110 | fail: 111 | if (ma) 112 | pa_modargs_free(ma); 113 | 114 | pa__done(m); 115 | 116 | return -1; 117 | } 118 | -------------------------------------------------------------------------------- /src/common/include/droid/sllist.h: -------------------------------------------------------------------------------- 1 | #ifndef foosllistfoo 2 | #define foosllistfoo 3 | 4 | /* 5 | * Copyright (C) 2018-2022 Jolla Ltd. 6 | * 7 | * Contact: Juho Hämäläinen 8 | * 9 | * These PulseAudio Modules are free software; you can redistribute 10 | * it and/or modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation 12 | * version 2.1 of the License. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 22 | * USA. 23 | */ 24 | 25 | #ifdef HAVE_CONFIG_H 26 | #include 27 | #endif 28 | #include 29 | #include 30 | 31 | #define SLLIST_APPEND(t, head, item) \ 32 | do { \ 33 | item->next = NULL; \ 34 | if (!head) { \ 35 | head = item; \ 36 | } else { \ 37 | t *_list; \ 38 | for (_list = head; _list->next; _list = _list->next); \ 39 | _list->next = item; \ 40 | } \ 41 | } while (0) 42 | 43 | #define SLLIST_FOREACH(i, head) \ 44 | for (i = (head); i; i = i->next) 45 | 46 | #define SLLIST_STEAL_FIRST(i, head) \ 47 | do { \ 48 | if (head) { \ 49 | i = head; \ 50 | head = head->next; \ 51 | } else \ 52 | i = NULL; \ 53 | } while (0) 54 | 55 | typedef struct dm_list_entry dm_list_entry; 56 | typedef struct dm_list dm_list; 57 | 58 | struct dm_list_entry { 59 | struct dm_list_entry *next; 60 | struct dm_list_entry *prev; 61 | void *data; 62 | }; 63 | 64 | struct dm_list { 65 | struct dm_list_entry *head; 66 | struct dm_list_entry *tail; 67 | ssize_t size; 68 | }; 69 | 70 | dm_list *dm_list_new(void); 71 | void dm_list_free(dm_list *list, pa_free_cb_t free_cb); 72 | bool dm_list_remove(dm_list *list, dm_list_entry *entry); 73 | void dm_list_prepend(dm_list *list, void *data); 74 | void dm_list_push_back(dm_list *list, void *data); 75 | dm_list_entry *dm_list_last(dm_list *list); 76 | void *dm_list_steal_first(dm_list *list); 77 | ssize_t dm_list_size(dm_list *list); 78 | void *dm_list_first_data(dm_list *list, void **state); 79 | void *dm_list_next_data(dm_list *list, void **state); 80 | /* For example 81 | * dm_list *list; 82 | * void *state; 83 | * my_data *data; 84 | * DM_LIST_FOREACH_DATA(data, list, state) { 85 | * do_something_with_my(data); 86 | * } 87 | */ 88 | #define DM_LIST_FOREACH_DATA(i, list, state) \ 89 | for (i = dm_list_first_data(list, &(state)); state; i = dm_list_next_data(list, &(state))) 90 | /* Access i->data */ 91 | #define DM_LIST_FOREACH(i, list) \ 92 | for (i = list->head; i; i = i->next) 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/common/sllist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Jolla Ltd. 3 | * 4 | * Contact: Juho Hämäläinen 5 | * 6 | * These PulseAudio Modules are free software; you can redistribute 7 | * it and/or modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation 9 | * version 2.1 of the License. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 | * USA. 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | #include 26 | #include 27 | 28 | #include "droid/sllist.h" 29 | 30 | dm_list *dm_list_new(void) { 31 | return pa_xnew0(dm_list, 1); 32 | } 33 | 34 | void dm_list_free(dm_list *list, pa_free_cb_t free_cb) { 35 | pa_assert(list); 36 | 37 | while (list->head) { 38 | void *data = dm_list_steal_first(list); 39 | 40 | if (free_cb) 41 | free_cb(data); 42 | } 43 | 44 | pa_xfree(list); 45 | } 46 | 47 | bool dm_list_remove(dm_list *list, dm_list_entry *entry) { 48 | dm_list_entry *i; 49 | bool removed = false; 50 | 51 | for (i = list->head; i; i = i->next) { 52 | if (i == entry) { 53 | removed = true; 54 | if (list->head == entry) 55 | list->head = entry->next; 56 | if (list->tail == entry) 57 | list->tail = entry->prev; 58 | if (entry->next) 59 | entry->next->prev = entry->prev; 60 | if (entry->prev) 61 | entry->prev->next = entry->next; 62 | pa_xfree(entry); 63 | break; 64 | } 65 | } 66 | 67 | return removed; 68 | } 69 | 70 | void dm_list_prepend(dm_list *list, void *data) { 71 | dm_list_entry *entry; 72 | 73 | pa_assert(list); 74 | 75 | entry = pa_xnew0(dm_list_entry, 1); 76 | entry->data = data; 77 | 78 | if (!list->tail) 79 | list->tail = entry; 80 | 81 | if (list->head) { 82 | entry->next = list->head; 83 | list->head->prev = entry; 84 | } 85 | 86 | list->head = entry; 87 | list->size++; 88 | } 89 | 90 | void dm_list_push_back(dm_list *list, void *data) { 91 | dm_list_entry *entry; 92 | 93 | pa_assert(list); 94 | 95 | entry = pa_xnew0(dm_list_entry, 1); 96 | entry->data = data; 97 | 98 | if (!list->head) 99 | list->head = entry; 100 | 101 | if (list->tail) { 102 | list->tail->next = entry; 103 | entry->prev = list->tail; 104 | } 105 | 106 | list->tail = entry; 107 | list->size++; 108 | } 109 | 110 | dm_list_entry *dm_list_last(dm_list *list) { 111 | pa_assert(list); 112 | 113 | return list->tail; 114 | } 115 | 116 | void *dm_list_steal_first(dm_list *list) { 117 | dm_list_entry *entry; 118 | void *data = NULL; 119 | 120 | pa_assert(list); 121 | 122 | if (list->head) { 123 | data = list->head->data; 124 | entry = list->head; 125 | if (list->head == list->tail) { 126 | list->head = NULL; 127 | list->tail = NULL; 128 | } else { 129 | list->head->next->prev = NULL; 130 | list->head = list->head->next; 131 | } 132 | pa_xfree(entry); 133 | list->size--; 134 | } 135 | 136 | return data; 137 | } 138 | 139 | ssize_t dm_list_size(dm_list *list) { 140 | pa_assert(list); 141 | 142 | return list->size; 143 | } 144 | 145 | /* For iteration */ 146 | 147 | void *dm_list_first_data(dm_list *list, void **state) { 148 | pa_assert(list); 149 | pa_assert(state); 150 | 151 | *state = list->head; 152 | 153 | if (list->head) 154 | return list->head->data; 155 | else 156 | return NULL; 157 | } 158 | 159 | void *dm_list_next_data(dm_list *list, void **state) { 160 | dm_list_entry *entry; 161 | 162 | pa_assert(list); 163 | pa_assert(state); 164 | 165 | entry = *state; 166 | *state = entry->next; 167 | 168 | if (entry->next) 169 | return entry->next->data; 170 | else 171 | return NULL; 172 | } 173 | -------------------------------------------------------------------------------- /src/common/include/droid/droid-config.h: -------------------------------------------------------------------------------- 1 | #ifndef foodroidconfigfoo 2 | #define foodroidconfigfoo 3 | 4 | /* 5 | * Copyright (C) 2018-2022 Jolla Ltd. 6 | * 7 | * Contact: Juho Hämäläinen 8 | * 9 | * These PulseAudio Modules are free software; you can redistribute 10 | * it and/or modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation 12 | * version 2.1 of the License. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 22 | * USA. 23 | */ 24 | 25 | #ifdef HAVE_CONFIG_H 26 | #include 27 | #endif 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | 36 | #define AUDIO_MAX_SAMPLING_RATES (32) 37 | #define AUDIO_MAX_CHANNEL_MASKS (32) 38 | 39 | typedef struct dm_config_global dm_config_global; 40 | typedef struct dm_config_port dm_config_port; 41 | typedef struct dm_config_route dm_config_route; 42 | typedef struct dm_config_module dm_config_module; 43 | typedef struct dm_config_device dm_config_device; 44 | typedef struct dm_config_profile dm_config_profile; 45 | 46 | struct dm_config_global { 47 | char *key; 48 | char *value; 49 | }; 50 | 51 | struct dm_config_profile { 52 | char *name; 53 | audio_format_t format; /* 0 -> dynamic TODO check that this is still true */ 54 | uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]; /* sampling_rates[0] == 0 -> dynamic, otherwise 0 terminates list */ 55 | audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]; /* channel_masks[0] == 0 -> dynamic */ 56 | }; 57 | 58 | typedef enum dm_config_role { 59 | DM_CONFIG_ROLE_SINK, 60 | DM_CONFIG_ROLE_SOURCE, 61 | } dm_config_role_t; 62 | 63 | typedef enum dm_config_type { 64 | DM_CONFIG_TYPE_MIX, 65 | DM_CONFIG_TYPE_DEVICE_PORT, 66 | DM_CONFIG_TYPE_MIX_PORT, 67 | } dm_config_type_t; 68 | 69 | struct dm_config_port { 70 | dm_config_module *module; 71 | 72 | /* common values */ 73 | 74 | dm_config_type_t port_type; /* either mixPort or devicePort */ 75 | char *name; 76 | dm_config_role_t role; 77 | dm_list *profiles; /* dm_config_profile* */ 78 | 79 | /* devicePort specific values */ 80 | 81 | audio_devices_t type; 82 | char *address; 83 | 84 | /* mixPort specific values */ 85 | 86 | uint32_t flags; /* audio_output_flag_t or audio_input_flag_t */ 87 | int max_open_count; /* 0 == not defined */ 88 | int max_active_count; /* 0 == not defined */ 89 | }; 90 | 91 | struct dm_config_route { 92 | dm_config_type_t type; 93 | dm_config_port *sink; 94 | dm_list *sources; /* dm_config_port* */ 95 | }; 96 | 97 | struct dm_config_module { 98 | dm_config_device *config; 99 | 100 | char *name; 101 | int version_major; 102 | int version_minor; 103 | 104 | dm_list *attached_devices; /* dm_config_port* owned by device_ports list below */ 105 | dm_config_port *default_output_device; /* owned by device_ports list below */ 106 | dm_list *ports; /* dm_config_port* - for convenience port types are filtered to two lists below: */ 107 | dm_list *mix_ports; /* dm_config_port* */ 108 | dm_list *device_ports; /* dm_config_port* */ 109 | dm_list *routes; /* dm_config_route* */ 110 | }; 111 | 112 | struct dm_config_device { 113 | dm_list *global_config; /* dm_config_global* */ 114 | dm_list *modules; /* dm_config_module* */ 115 | }; 116 | 117 | 118 | /* Config parser */ 119 | dm_config_device *dm_config_load(pa_modargs *ma); 120 | dm_config_device *dm_config_dup(const dm_config_device *config); 121 | void dm_config_free(dm_config_device *config); 122 | /* autodetect config type from filename and parse */ 123 | dm_config_device *pa_parse_droid_audio_config(const char *filename); 124 | 125 | dm_config_module *dm_config_find_module(dm_config_device *config, const char* module_id); 126 | dm_config_port *dm_config_find_port(dm_config_module *module, const char* name); 127 | dm_config_port *dm_config_default_output_device(dm_config_module *module); 128 | dm_config_port *dm_config_find_device_port(dm_config_port *port, audio_devices_t device); 129 | char *dm_config_escape_string(const char *string); 130 | 131 | bool dm_config_port_equal(const dm_config_port *a, const dm_config_port *b); 132 | 133 | dm_config_port *dm_config_find_mix_port(dm_config_module *module, const char *name); 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /src/common/include/droid/conversion.h: -------------------------------------------------------------------------------- 1 | #ifndef foodroidconversionfoo 2 | #define foodroidconversionfoo 3 | 4 | /* 5 | * Copyright (C) 2018-2022 Jolla Ltd. 6 | * 7 | * Contact: Juho Hämäläinen 8 | * 9 | * These PulseAudio Modules are free software; you can redistribute 10 | * it and/or modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation 12 | * version 2.1 of the License. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 22 | * USA. 23 | */ 24 | 25 | #ifdef HAVE_CONFIG_H 26 | #include 27 | #endif 28 | #include 29 | 30 | #include 31 | 32 | /* From recent audio_policy_conf.h */ 33 | #ifndef AUDIO_HAL_VERSION_TAG 34 | #define AUDIO_HAL_VERSION_TAG "audio_hal_version" 35 | #endif 36 | #ifndef GAINS_TAG 37 | #define GAINS_TAG "gains" 38 | #endif 39 | 40 | #include 41 | #include 42 | 43 | typedef enum { 44 | CONV_FROM_PA, 45 | CONV_FROM_HAL 46 | } pa_conversion_field_t; 47 | 48 | typedef enum { 49 | CONV_STRING_FORMAT, 50 | CONV_STRING_OUTPUT_CHANNELS, 51 | CONV_STRING_INPUT_CHANNELS, 52 | CONV_STRING_OUTPUT_DEVICE, 53 | CONV_STRING_INPUT_DEVICE, 54 | CONV_STRING_OUTPUT_FLAG, 55 | CONV_STRING_INPUT_FLAG, 56 | CONV_STRING_AUDIO_SOURCE_FANCY, 57 | } pa_conversion_string_t; 58 | 59 | bool pa_string_convert_num_to_str(pa_conversion_string_t type, uint32_t value, const char **to_str); 60 | bool pa_string_convert_str_to_num(pa_conversion_string_t type, const char *str, uint32_t *to_value); 61 | 62 | bool pa_convert_output_channel(uint32_t value, pa_conversion_field_t from, uint32_t *to_value); 63 | bool pa_convert_input_channel(uint32_t value, pa_conversion_field_t from, uint32_t *to_value); 64 | bool pa_convert_format(uint32_t value, pa_conversion_field_t from, uint32_t *to_value); 65 | 66 | bool pa_string_convert_output_device_num_to_str(audio_devices_t value, const char **to_str); 67 | bool pa_string_convert_output_device_str_to_num(const char *str, audio_devices_t *to_value); 68 | bool pa_string_convert_input_device_num_to_str(audio_devices_t value, const char **to_str); 69 | bool pa_string_convert_input_device_str_to_num(const char *str, audio_devices_t *to_value); 70 | 71 | bool pa_string_convert_flag_num_to_str(audio_output_flags_t value, const char **to_str); 72 | bool pa_string_convert_flag_str_to_num(const char *str, audio_output_flags_t *to_value); 73 | 74 | char *pa_list_string_flags(audio_output_flags_t flags); 75 | 76 | /* Get default audio source associated with input device. 77 | * Return true if default source was found. */ 78 | bool pa_input_device_default_audio_source(audio_devices_t input_device, audio_source_t *default_source); 79 | 80 | /* Pretty port names */ 81 | bool pa_droid_output_port_name(audio_devices_t value, const char **to_str); 82 | bool pa_droid_input_port_name(audio_devices_t value, const char **to_str); 83 | 84 | int pa_conversion_parse_list(pa_conversion_string_t type, const char *separator, 85 | const char *str, uint32_t *dst, char **unknown_entries); 86 | 87 | bool pa_conversion_parse_sampling_rates(const char *fn, const unsigned ln, 88 | const char *str, 89 | uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]); 90 | bool pa_conversion_parse_formats(const char *fn, const unsigned ln, 91 | const char *str, 92 | audio_format_t *formats); 93 | int pa_conversion_parse_output_channels(const char *fn, const unsigned ln, 94 | const char *str, 95 | audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]); 96 | int pa_conversion_parse_input_channels(const char *fn, const unsigned ln, 97 | const char *str, 98 | audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]); 99 | bool pa_conversion_parse_output_devices(const char *fn, const unsigned ln, 100 | char *str, bool must_recognize_all, 101 | audio_devices_t *devices); 102 | bool pa_conversion_parse_input_devices(const char *fn, const unsigned ln, 103 | char *str, bool must_recognize_all, 104 | audio_devices_t *devices); 105 | bool pa_conversion_parse_output_flags(const char *fn, const unsigned ln, 106 | const char *str, audio_output_flags_t *flags); 107 | bool pa_conversion_parse_input_flags(const char *fn, const unsigned ln, 108 | const char *str, uint32_t *flags); 109 | bool pa_conversion_parse_version(const char *fn, const unsigned ln, const char *str, uint32_t *version); 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('pulseaudio-modules-droid', 'c', 2 | version : run_command(find_program('git-version-gen'), join_paths(meson.current_source_dir(), '.tarball-version'), check: true).stdout().strip(), 3 | meson_version : '>= 0.50.0', 4 | default_options : [ 'c_std=gnu11' ] 5 | ) 6 | 7 | cc = meson.get_compiler('c') 8 | 9 | configinc = include_directories('.') 10 | libdir = join_paths(get_option('prefix'), get_option('libdir')) 11 | 12 | pa_c_args = ['-DHAVE_CONFIG_H'] 13 | 14 | droiddevice = get_option('droid-device') 15 | pa_c_args += ['-DDROID_DEVICE_@0@=1'.format(droiddevice.underscorify().to_upper())] 16 | pa_c_args += ['-DDROID_DEVICE_STRING="@0@"'.format(droiddevice)] 17 | 18 | # dependencies 19 | droid_headers_dep = dependency('android-headers', required : true) 20 | expat_dep = dependency('expat', version : '>= 2.1', required : true) 21 | hybris_dep = dependency('libhardware', version : '>= 0.1.0', required : true) 22 | hybris_common_dep = cc.find_library('hybris-common', required : true) 23 | ltdl_dep = cc.find_library('ltdl', required : true) 24 | pulsecore_dep = dependency('pulsecore', version : '>= 14.2', required : true) 25 | 26 | pa_version_str = pulsecore_dep.version() 27 | # For tarballs, the first split will do nothing, but for builds in git, we 28 | # split out suffixes when there are commits since the last tag 29 | # (e.g.: v11.99.1-3-gad14bdb24 -> v11.99.1) 30 | version_split = pa_version_str.split('-')[0].split('.') 31 | pa_version_major = version_split[0].split('v')[0] 32 | pa_version_minor = version_split[1] 33 | pa_version_major_minor = pa_version_major + '.' + pa_version_minor 34 | project_version_str = meson.project_version() 35 | project_version_split = project_version_str.split('-')[0].split('.') 36 | pa_version_module = project_version_split[2].split('+')[0] 37 | 38 | modlibexecdir = get_option('modlibexecdir') 39 | if modlibexecdir == '' 40 | modlibexecdir = join_paths(libdir, 'pulse-' + pa_version_major_minor, 'modules') 41 | endif 42 | 43 | privlibdir = join_paths(libdir, 'pulseaudio') 44 | rpath_dirs = join_paths(privlibdir) + ':' + join_paths(modlibexecdir) 45 | 46 | cdata = configuration_data() 47 | cdata.set_quoted('PACKAGE', meson.project_name()) 48 | cdata.set_quoted('PACKAGE_NAME', meson.project_name()) 49 | cdata.set_quoted('PACKAGE_VERSION', pa_version_str) 50 | cdata.set_quoted('VERSION', pa_version_str) 51 | 52 | # Atomic operations 53 | 54 | if get_option('atomic-arm-memory-barrier') 55 | cdata.set('ATOMIC_ARM_MEMORY_BARRIER_ENABLED', 1) 56 | endif 57 | 58 | need_libatomic_ops = false 59 | 60 | atomictest = '''void func() { 61 | volatile int atomic = 2; 62 | __sync_bool_compare_and_swap (&atomic, 2, 3); 63 | } 64 | ''' 65 | 66 | if cc.compiles(atomictest) 67 | cdata.set('HAVE_ATOMIC_BUILTINS', 1) 68 | 69 | newatomictest = '''void func() { 70 | int c = 0; 71 | __atomic_store_n(&c, 4, __ATOMIC_SEQ_CST); 72 | } 73 | ''' 74 | 75 | if(cc.compiles(newatomictest)) 76 | cdata.set('HAVE_ATOMIC_BUILTINS_MEMORY_MODEL', 1) 77 | endif 78 | 79 | elif host_machine.cpu_family() == 'arm' 80 | if host_machine.system() == 'linux' and get_option('atomic-arm-linux-helpers') 81 | cdata.set('ATOMIC_ARM_LINUX_HELPERS', 1) 82 | else 83 | armatomictest = '''int func() { 84 | volatile int a=0; 85 | int o=0, n=1, r; 86 | asm volatile ( 87 | "ldrex %0, [%1]\n" 88 | "subs %0, %0, %2\n" 89 | "strexeq %0, %3, [%1]\n" 90 | : "=&r" (r) 91 | : "r" (&a), "Ir" (o), "r" (n) 92 | : "cc"); 93 | return (a==1 ? 0 : -1); 94 | } 95 | ''' 96 | 97 | if cc.compiles(armatomictest) 98 | cdata.set('ATOMIC_ARM_INLINE_ASM', 1) 99 | else 100 | need_libatomic_ops = true 101 | endif 102 | endif # arm && !linux 103 | 104 | elif not ['freebsd', 'netbsd'].contains(host_machine.system()) 105 | need_libatomic_ops = true 106 | endif # !atomic helpers && !arm 107 | 108 | if need_libatomic_ops 109 | assert(cc.has_header('atomic_ops.h'), 'Need libatomic_ops') 110 | 111 | cdata.set('AO_REQUIRE_CAS', 1) 112 | 113 | if host_machine.system() != 'windows' 114 | libatomic_ops_dep = cc.find_library('atomic_ops', required : true) 115 | else 116 | libatomic_ops_dep = dependency('', required: false) 117 | endif 118 | else 119 | libatomic_ops_dep = dependency('', required: false) 120 | endif 121 | 122 | check_enums = [ 123 | # Input devices 124 | 'AUDIO_DEVICE_IN_FM_RX', 125 | 'AUDIO_DEVICE_IN_FM_RX_A2DP', 126 | # Audio sources 127 | 'AUDIO_SOURCE_ECHO_REFERENCE', 128 | 'AUDIO_SOURCE_FM_TUNER', 129 | 'AUDIO_SOURCE_FM_RX', 130 | 'AUDIO_SOURCE_FM_RX_A2DP', 131 | # Output flags 132 | 'AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH', 133 | # Channels 134 | 'AUDIO_CHANNEL_IN_VOICE_CALL_MONO', 135 | 'AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO', 136 | 'AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO', 137 | # Formats 138 | 'AUDIO_FORMAT_PCM_OFFLOAD', 139 | 'AUDIO_FORMAT_FLAC', 140 | 'AUDIO_FORMAT_OPUS', 141 | ] 142 | 143 | foreach e : check_enums 144 | if cc.has_header_symbol('system/audio.h', e, dependencies : droid_headers_dep, prefix : '#include \n#ifdef QCOM_BSP\n#define QCOM_HARDWARE\n#endif') 145 | define = 'HAVE_ENUM_' + e 146 | cdata.set(define, 1) 147 | define = 'STRING_ENTRY_IF_' + e 148 | cdata.set(define, 'STRING_ENTRY(' + e + '),') 149 | define = 'FANCY_ENTRY_IF_' + e + '(n)' 150 | cdata.set(define, '{' + e + ', n},') 151 | else 152 | define = 'STRING_ENTRY_IF_' + e 153 | cdata.set(define, '') 154 | define = 'FANCY_ENTRY_IF_' + e + '(n)' 155 | cdata.set(define, '') 156 | endif 157 | endforeach 158 | 159 | # Headers 160 | check_headers = [ 161 | 'valgrind/memcheck.h', 162 | ] 163 | 164 | foreach h : check_headers 165 | if cc.has_header(h) 166 | define = 'HAVE_' + h.underscorify().to_upper() 167 | cdata.set(define, 1) 168 | endif 169 | endforeach 170 | 171 | # Now generate config.h from everything above 172 | configure_file(output : 'config.h', configuration : cdata) 173 | 174 | subdir('src') 175 | -------------------------------------------------------------------------------- /src/common/droid-config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2022 Jolla Ltd. 3 | * 4 | * Contact: Juho Hämäläinen 5 | * 6 | * These PulseAudio Modules are free software; you can redistribute 7 | * it and/or modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation 9 | * version 2.1 of the License. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 | * USA. 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | 26 | #include "droid/version.h" 27 | #include "droid/droid-config.h" 28 | #include "droid/sllist.h" 29 | #include "config-parser-xml.h" 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #ifdef HAVE_VALGRIND_MEMCHECK_H 37 | #include 38 | #endif 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | 48 | #define ODM_AUDIO_POLICY_CONFIG_XML_FILE "/odm/etc/audio_policy_configuration.xml" 49 | #define VENDOR_AUDIO_AUDIO_POLICY_CONFIG_XML_FILE "/vendor/etc/audio/audio_policy_configuration.xml" 50 | #define VENDOR_AUDIO_POLICY_CONFIG_XML_FILE "/vendor/etc/audio_policy_configuration.xml" 51 | #define SYSTEM_AUDIO_POLICY_CONFIG_XML_FILE "/system/etc/audio_policy_configuration.xml" 52 | 53 | 54 | dm_config_device *dm_config_load(pa_modargs *ma) { 55 | dm_config_device *config = NULL; 56 | const char *manual_config; 57 | const char *config_location[] = { 58 | ODM_AUDIO_POLICY_CONFIG_XML_FILE, 59 | VENDOR_AUDIO_AUDIO_POLICY_CONFIG_XML_FILE, 60 | VENDOR_AUDIO_POLICY_CONFIG_XML_FILE, 61 | SYSTEM_AUDIO_POLICY_CONFIG_XML_FILE, 62 | NULL}; 63 | 64 | pa_assert(ma); 65 | 66 | if ((manual_config = pa_modargs_get_value(ma, "config", NULL))) { 67 | if (!(config = pa_parse_droid_audio_config(manual_config))) 68 | pa_log("Failed to parse configuration from %s", manual_config); 69 | } else { 70 | int i; 71 | for (i = 0; config_location[i]; i++) { 72 | if ((config = pa_parse_droid_audio_config(config_location[i]))) 73 | break; 74 | else 75 | pa_log_debug("Failed to parse configuration from %s", config_location[i]); 76 | } 77 | 78 | } 79 | 80 | if (!config) 81 | pa_log("Failed to parse any configuration."); 82 | 83 | return config; 84 | } 85 | 86 | static dm_config_profile *config_profile_dup(const dm_config_profile *profile) { 87 | dm_config_profile *copy = pa_xnew0(dm_config_profile, 1); 88 | 89 | copy->name = pa_xstrdup(profile->name); 90 | copy->format = profile->format; 91 | memcpy(copy->sampling_rates, 92 | profile->sampling_rates, 93 | sizeof(profile->sampling_rates)); 94 | memcpy(copy->channel_masks, 95 | profile->channel_masks, 96 | sizeof(profile->channel_masks)); 97 | 98 | return copy; 99 | } 100 | 101 | static dm_config_port *config_port_dup(const dm_config_port *port, dm_config_module *module) { 102 | dm_config_port *copy = pa_xnew0(dm_config_port, 1); 103 | const dm_list_entry *i; 104 | 105 | copy->module = module; 106 | copy->port_type = port->port_type; 107 | copy->name = pa_xstrdup(port->name); 108 | copy->role = port->role; 109 | copy->profiles = dm_list_new(); 110 | 111 | DM_LIST_FOREACH(i, port->profiles) 112 | dm_list_push_back(copy->profiles, config_profile_dup(i->data)); 113 | 114 | if (port->port_type == DM_CONFIG_TYPE_DEVICE_PORT) { 115 | copy->type = port->type; 116 | copy->address = pa_xstrdup(port->address); 117 | } 118 | 119 | if (port->port_type == DM_CONFIG_TYPE_MIX_PORT) { 120 | copy->flags = port->flags; 121 | copy->max_open_count = port->max_open_count; 122 | copy->max_active_count = port->max_active_count; 123 | } 124 | 125 | return copy; 126 | } 127 | 128 | static dm_config_route *config_route_dup(const dm_config_route *route, dm_list *ports) { 129 | dm_config_route *copy = pa_xnew0(dm_config_route, 1); 130 | dm_config_port *port_copy, *port; 131 | void *state, *state2; 132 | 133 | copy->type = route->type; 134 | copy->sources = dm_list_new(); 135 | 136 | DM_LIST_FOREACH_DATA(port, route->sources, state) { 137 | DM_LIST_FOREACH_DATA(port_copy, ports, state2) { 138 | if (dm_config_port_equal(port, port_copy)) { 139 | dm_list_push_back(copy->sources, port_copy); 140 | break; 141 | } 142 | } 143 | } 144 | 145 | DM_LIST_FOREACH_DATA(port_copy, ports, state) { 146 | if (dm_config_port_equal(port_copy, route->sink)) { 147 | copy->sink = port_copy; 148 | break; 149 | } 150 | } 151 | 152 | return copy; 153 | } 154 | 155 | static dm_config_module *config_module_dup(const dm_config_module *module) { 156 | dm_config_module *copy = pa_xnew0(dm_config_module, 1); 157 | dm_config_port *device_port, *attached_device, *mix_port; 158 | dm_config_route *route; 159 | void *state, *state2; 160 | 161 | copy = pa_xnew0(dm_config_module, 1); 162 | copy->name = pa_xstrdup(module->name); 163 | copy->version_major = module->version_major; 164 | copy->version_minor = module->version_minor; 165 | copy->attached_devices = dm_list_new(); 166 | copy->default_output_device = NULL; 167 | copy->mix_ports = dm_list_new(); 168 | copy->device_ports = dm_list_new(); 169 | copy->ports = dm_list_new(); 170 | copy->routes = dm_list_new(); 171 | 172 | DM_LIST_FOREACH_DATA(device_port, module->device_ports, state) { 173 | dm_config_port *device_port_copy = config_port_dup(device_port, copy); 174 | dm_list_push_back(copy->device_ports, device_port_copy); 175 | dm_list_push_back(copy->ports, device_port_copy); 176 | if (module->default_output_device == device_port) 177 | copy->default_output_device = device_port_copy; 178 | DM_LIST_FOREACH_DATA(attached_device, module->attached_devices, state2) { 179 | if (attached_device == device_port) { 180 | dm_list_push_back(copy->attached_devices, device_port_copy); 181 | break; 182 | } 183 | } 184 | } 185 | 186 | DM_LIST_FOREACH_DATA(mix_port, module->mix_ports, state) { 187 | dm_config_port *mix_port_copy = config_port_dup(mix_port, copy); 188 | dm_list_push_back(copy->mix_ports, mix_port_copy); 189 | dm_list_push_back(copy->ports, mix_port_copy); 190 | } 191 | 192 | DM_LIST_FOREACH_DATA(route, module->routes, state) 193 | dm_list_push_back(copy->routes, config_route_dup(route, copy->ports)); 194 | 195 | return copy; 196 | } 197 | 198 | dm_config_device *dm_config_dup(const dm_config_device *config) { 199 | dm_config_device *copy; 200 | dm_config_module *module; 201 | void *state; 202 | 203 | pa_assert(config); 204 | 205 | copy = pa_xnew0(dm_config_device, 1); 206 | copy->global_config = dm_list_new(); 207 | copy->modules = dm_list_new(); 208 | 209 | if (config->global_config) { 210 | dm_config_global *global, *global_copy; 211 | 212 | DM_LIST_FOREACH_DATA(global, config->global_config, state) { 213 | global_copy = pa_xnew0(dm_config_global, 1); 214 | global_copy->key = pa_xstrdup(global->key); 215 | global_copy->value = pa_xstrdup(global->value); 216 | dm_list_push_back(copy->global_config, global_copy); 217 | } 218 | } 219 | 220 | DM_LIST_FOREACH_DATA(module, config->modules, state) 221 | dm_list_push_back(copy->modules, config_module_dup(module)); 222 | 223 | return copy; 224 | } 225 | 226 | dm_config_device *pa_parse_droid_audio_config(const char *filename) { 227 | return pa_parse_droid_audio_config_xml(filename); 228 | } 229 | 230 | static void config_global_free(void *data) { 231 | dm_config_global *global = data; 232 | 233 | pa_xfree(global->key); 234 | pa_xfree(global->value); 235 | pa_xfree(global); 236 | } 237 | 238 | static void config_profile_free(void *data) { 239 | dm_config_profile *profile = data; 240 | 241 | pa_xfree(profile->name); 242 | pa_xfree(profile); 243 | } 244 | 245 | static void config_port_free(void *data) { 246 | dm_config_port *port = data; 247 | 248 | pa_xfree(port->name); 249 | pa_xfree(port->address); 250 | dm_list_free(port->profiles, config_profile_free); 251 | pa_xfree(port); 252 | } 253 | 254 | static void config_route_free(void *data) { 255 | dm_config_route *route = data; 256 | 257 | dm_list_free(route->sources, NULL); 258 | pa_xfree(route); 259 | } 260 | 261 | static void config_module_free(void *data) { 262 | dm_config_module *module = data; 263 | 264 | pa_xfree(module->name); 265 | dm_list_free(module->attached_devices, NULL); 266 | dm_list_free(module->ports, config_port_free); 267 | dm_list_free(module->device_ports, NULL); 268 | dm_list_free(module->mix_ports, NULL); 269 | dm_list_free(module->routes, config_route_free); 270 | pa_xfree(module); 271 | } 272 | 273 | void dm_config_free(dm_config_device *config) { 274 | if (!config) 275 | return; 276 | 277 | dm_list_free(config->global_config, config_global_free); 278 | dm_list_free(config->modules, config_module_free); 279 | pa_xfree(config); 280 | } 281 | 282 | dm_config_module *dm_config_find_module(dm_config_device *config, const char* module_id) { 283 | dm_config_module *module; 284 | void *state; 285 | 286 | pa_assert(config); 287 | pa_assert(module_id); 288 | 289 | DM_LIST_FOREACH_DATA(module, config->modules, state) { 290 | if (pa_streq(module_id, module->name)) 291 | return module; 292 | } 293 | 294 | return NULL; 295 | } 296 | 297 | dm_config_port *dm_config_find_port(dm_config_module *module, const char* name) { 298 | dm_config_port *port; 299 | void *state; 300 | 301 | pa_assert(module); 302 | pa_assert(name); 303 | 304 | DM_LIST_FOREACH_DATA(port, module->ports, state) { 305 | if (pa_streq(name, port->name)) 306 | return port; 307 | } 308 | 309 | return NULL; 310 | } 311 | 312 | dm_config_port *dm_config_default_output_device(dm_config_module *module) { 313 | pa_assert(module); 314 | 315 | if (module->default_output_device) 316 | return module->default_output_device; 317 | else { 318 | pa_log("Module %s doesn't have default output device.", module->name); 319 | return 0; 320 | } 321 | } 322 | 323 | char *dm_config_escape_string(const char *string) { 324 | if (!string) 325 | return NULL; 326 | 327 | /* Just replace whitespace with underscores for now. */ 328 | 329 | return pa_replace(string, " ", "_"); 330 | } 331 | 332 | dm_config_port *dm_config_find_device_port(dm_config_port *port, audio_devices_t device) { 333 | dm_config_port *device_port; 334 | void *state; 335 | 336 | pa_assert(port); 337 | 338 | DM_LIST_FOREACH_DATA(device_port, port->module->device_ports, state) { 339 | if (device_port->type == device) 340 | return device_port; 341 | } 342 | 343 | return NULL; 344 | } 345 | 346 | bool dm_config_port_equal(const dm_config_port *a, const dm_config_port *b) { 347 | if ((!a && b) || (a && !b)) 348 | return false; 349 | 350 | else if (!a && !b) 351 | return true; 352 | 353 | return (pa_streq(a->name, b->name) && a->type == b->type); 354 | } 355 | 356 | dm_config_port *dm_config_find_mix_port(dm_config_module *module, const char *name) { 357 | dm_config_port *mix_port = NULL; 358 | void *state; 359 | 360 | DM_LIST_FOREACH_DATA(mix_port, module->mix_ports, state) { 361 | if (pa_streq(mix_port->name, name)) 362 | return mix_port; 363 | } 364 | 365 | return NULL; 366 | } 367 | -------------------------------------------------------------------------------- /src/common/include/droid/droid-util.h: -------------------------------------------------------------------------------- 1 | #ifndef foodroidutilfoo 2 | #define foodroidutilfoo 3 | 4 | /* 5 | * Copyright (C) 2013-2022 Jolla Ltd. 6 | * 7 | * Contact: Juho Hämäläinen 8 | * 9 | * These PulseAudio Modules are free software; you can redistribute 10 | * it and/or modify it under the terms of the GNU Lesser General Public 11 | * License as published by the Free Software Foundation 12 | * version 2.1 of the License. 13 | * 14 | * This library is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this library; if not, write to the Free Software 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 22 | * USA. 23 | */ 24 | 25 | #ifdef HAVE_CONFIG_H 26 | #include 27 | #endif 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | 38 | #define PROP_DROID_DEVICES "droid.devices" 39 | #define PROP_DROID_FLAGS "droid.flags" 40 | #define PROP_DROID_HW_MODULE "droid.hw_module" 41 | #define PROP_DROID_API_STRING "droid-hal" 42 | 43 | #define PROP_DROID_OUTPUT_PRIMARY "droid.output.primary" 44 | #define PROP_DROID_OUTPUT_LOW_LATENCY "droid.output.low_latency" 45 | #define PROP_DROID_OUTPUT_MEDIA_LATENCY "droid.output.media_latency" 46 | #define PROP_DROID_OUTPUT_OFFLOAD "droid.output.offload" 47 | #define PROP_DROID_OUTPUT_VOIP "droid.output.voip" 48 | #define PROP_DROID_INPUT_BUILTIN "droid.input.builtin" 49 | #define PROP_DROID_INPUT_EXTERNAL "droid.input.external" 50 | #define PROP_DROID_INPUT_VOIP "droid.input.voip" 51 | 52 | #define EXT_PROP_AUDIO_SOURCE "audio.source" 53 | 54 | #define PA_DROID_PRIMARY_DEVICE "primary" 55 | 56 | typedef struct pa_droid_hw_module pa_droid_hw_module; 57 | typedef struct pa_droid_stream pa_droid_stream; 58 | typedef struct pa_droid_output_stream pa_droid_output_stream; 59 | typedef struct pa_droid_input_stream pa_droid_input_stream; 60 | typedef struct pa_droid_card_data pa_droid_card_data; 61 | 62 | typedef struct pa_droid_options pa_droid_options; 63 | 64 | enum pa_droid_option_type { 65 | DM_OPTION_INPUT_ATOI, 66 | DM_OPTION_CLOSE_INPUT, 67 | DM_OPTION_UNLOAD_NO_CLOSE, 68 | DM_OPTION_HW_VOLUME, 69 | DM_OPTION_REALCALL, 70 | DM_OPTION_UNLOAD_CALL_EXIT, 71 | DM_OPTION_OUTPUT_FAST, 72 | DM_OPTION_OUTPUT_DEEP_BUFFER, 73 | DM_OPTION_AUDIO_CAL_WAIT, 74 | DM_OPTION_SPEAKER_BEFORE_VOICE, 75 | DM_OPTION_OUTPUT_VOIP_RX, 76 | DM_OPTION_RECORD_VOICE_16K, 77 | DM_OPTION_COUNT 78 | }; 79 | 80 | struct pa_droid_options { 81 | bool enabled[DM_OPTION_COUNT]; 82 | }; 83 | 84 | struct pa_droid_hw_module { 85 | PA_REFCNT_DECLARE; 86 | 87 | pa_core *core; 88 | char *shared_name; 89 | 90 | dm_config_device *config; 91 | dm_config_module *enabled_module; 92 | pa_mutex *hw_mutex; 93 | pa_mutex *output_mutex; 94 | pa_mutex *input_mutex; 95 | 96 | struct hw_module_t *hwmod; 97 | audio_hw_device_t *device; 98 | 99 | const char *module_id; 100 | 101 | uint32_t stream_id; 102 | bool bt_sco_enabled; 103 | 104 | pa_idxset *outputs; 105 | pa_idxset *inputs; 106 | pa_hook_slot *sink_put_hook_slot; 107 | pa_hook_slot *sink_unlink_hook_slot; 108 | 109 | pa_atomic_t active_outputs; 110 | 111 | pa_droid_options options; 112 | 113 | /* Mode and input control */ 114 | struct _state { 115 | audio_mode_t mode; 116 | } state; 117 | }; 118 | 119 | struct pa_droid_output_stream { 120 | struct audio_stream_out *stream; 121 | pa_sample_spec sample_spec; 122 | pa_channel_map channel_map; 123 | }; 124 | 125 | struct pa_droid_input_stream { 126 | struct audio_stream_in *stream; 127 | pa_sample_spec default_sample_spec; 128 | pa_channel_map default_channel_map; 129 | pa_sample_spec sample_spec; 130 | pa_channel_map channel_map; 131 | pa_sample_spec req_sample_spec; 132 | pa_channel_map req_channel_map; 133 | 134 | audio_source_t audio_source; 135 | dm_config_port *default_mix_port; 136 | dm_config_port *input_port; 137 | pa_droid_stream *active_input; 138 | 139 | uint32_t flags; 140 | uint32_t device; 141 | bool first; 142 | }; 143 | 144 | struct pa_droid_stream { 145 | PA_REFCNT_DECLARE; 146 | 147 | pa_droid_hw_module *module; 148 | dm_config_port *mix_port; 149 | size_t buffer_size; 150 | void *data; 151 | 152 | audio_io_handle_t io_handle; 153 | audio_patch_handle_t audio_patch; 154 | const dm_config_port *active_device_port; 155 | 156 | pa_droid_output_stream *output; 157 | pa_droid_input_stream *input; 158 | }; 159 | 160 | struct pa_droid_card_data { 161 | void *userdata; 162 | char *module_id; 163 | }; 164 | 165 | 166 | /* Profiles */ 167 | 168 | typedef struct pa_droid_profile_set pa_droid_profile_set; 169 | typedef struct pa_droid_mapping pa_droid_mapping; 170 | 171 | typedef struct pa_droid_port_data { 172 | dm_config_port *device_port; 173 | } pa_droid_port_data; 174 | 175 | typedef struct pa_droid_port { 176 | pa_droid_mapping *mapping; 177 | 178 | dm_config_port *device_port; 179 | char *name; 180 | char *description; 181 | unsigned priority; 182 | } pa_droid_port; 183 | 184 | struct pa_droid_mapping { 185 | pa_droid_profile_set *profile_set; 186 | 187 | dm_config_module *module; 188 | dm_config_port *mix_port; 189 | dm_list *device_ports; 190 | 191 | char *name; 192 | char *description; 193 | unsigned priority; 194 | pa_proplist *proplist; 195 | 196 | /* Mapping doesn't own the ports */ 197 | pa_idxset *ports; 198 | 199 | pa_direction_t direction; 200 | 201 | pa_sink *sink; 202 | pa_source *source; 203 | }; 204 | 205 | typedef struct pa_droid_profile { 206 | pa_droid_profile_set *profile_set; 207 | 208 | dm_config_module *module; 209 | 210 | char *name; 211 | char *description; 212 | unsigned priority; 213 | 214 | /* Idxsets contain pa_droid_mapping objects. 215 | * Profile doesn't own the mappings, these 216 | * are references to structs in profile set 217 | * hashmaps. */ 218 | pa_idxset *output_mappings; 219 | /* Only one input */ 220 | pa_idxset *input_mappings; 221 | pa_droid_mapping *input_mapping; 222 | 223 | } pa_droid_profile; 224 | 225 | struct pa_droid_profile_set { 226 | dm_config_device *config; 227 | 228 | pa_hashmap *all_ports; 229 | pa_hashmap *output_mappings; 230 | pa_hashmap *input_mappings; 231 | pa_hashmap *profiles; 232 | }; 233 | 234 | #define PA_DROID_OUTPUT_PARKING "output-parking" 235 | #define PA_DROID_INPUT_PARKING "input-parking" 236 | 237 | /* Open hardware module */ 238 | /* 'config' can be NULL if it is assumed that hw module with module_id already is open. */ 239 | pa_droid_hw_module *pa_droid_hw_module_get(pa_core *core, dm_config_device *config, const char *module_id); 240 | /* First try to get already open hw module and if none found parse config and options from modargs 241 | * and do initial open. */ 242 | pa_droid_hw_module *pa_droid_hw_module_get2(pa_core *core, pa_modargs *ma, const char *module_id); 243 | pa_droid_hw_module *pa_droid_hw_module_ref(pa_droid_hw_module *hw); 244 | void pa_droid_hw_module_unref(pa_droid_hw_module *hw); 245 | 246 | void pa_droid_hw_module_lock(pa_droid_hw_module *hw); 247 | bool pa_droid_hw_module_try_lock(pa_droid_hw_module *hw); 248 | void pa_droid_hw_module_unlock(pa_droid_hw_module *hw); 249 | 250 | void pa_droid_options_log(pa_droid_hw_module *hw); 251 | 252 | static inline bool pa_droid_option(pa_droid_hw_module *hw, enum pa_droid_option_type option) { 253 | return hw && hw->options.enabled[option]; 254 | } 255 | 256 | bool pa_droid_hw_set_mode(pa_droid_hw_module *hw_module, audio_mode_t mode); 257 | bool pa_droid_hw_has_mic_control(pa_droid_hw_module *hw); 258 | int pa_droid_hw_mic_get_mute(pa_droid_hw_module *hw_module, bool *muted); 259 | void pa_droid_hw_mic_set_mute(pa_droid_hw_module *hw_module, bool muted); 260 | 261 | /* Profiles */ 262 | pa_droid_profile_set *pa_droid_profile_set_default_new(dm_config_module *module); 263 | void pa_droid_profile_set_free(pa_droid_profile_set *ps); 264 | 265 | void pa_droid_profile_free(pa_droid_profile *p); 266 | 267 | bool pa_droid_mapping_is_primary(pa_droid_mapping *am); 268 | /* Go through idxset containing pa_droid_mapping objects and if primary output or input 269 | * mapping is found, return pointer to that mapping. */ 270 | pa_droid_mapping *pa_droid_idxset_get_primary(pa_idxset *i); 271 | void pa_droid_mapping_free(pa_droid_mapping *am); 272 | 273 | /* Add ports from sinks/sources. 274 | * May be called multiple times for one sink/source. */ 275 | void pa_droid_add_ports(pa_hashmap *ports, pa_droid_mapping *am, pa_card *card); 276 | /* Add ports from card. 277 | * May be called multiple times for one card profile. */ 278 | void pa_droid_add_card_ports(pa_card_profile *cp, pa_hashmap *ports, pa_droid_mapping *am, pa_core *core); 279 | 280 | /* Module operations */ 281 | int pa_droid_set_parameters(pa_droid_hw_module *hw, const char *parameters); 282 | pa_droid_stream *pa_droid_hw_primary_output_stream(pa_droid_hw_module *hw); 283 | 284 | /* Stream operations */ 285 | pa_droid_stream *pa_droid_stream_ref(pa_droid_stream *s); 286 | void pa_droid_stream_unref(pa_droid_stream *s); 287 | 288 | int pa_droid_stream_set_parameters(pa_droid_stream *s, const char *parameters); 289 | 290 | /* Output stream operations */ 291 | pa_droid_stream *pa_droid_open_output_stream(pa_droid_hw_module *module, 292 | const pa_sample_spec *spec, 293 | const pa_channel_map *map, 294 | dm_config_port *mix_port, 295 | dm_config_port *device_port); 296 | 297 | /* Set routing to the input or output stream, with following side-effects: 298 | * Output: 299 | * - if routing is set to primary output stream, set routing to all other 300 | * open streams as well 301 | * - if routing is set to non-primary stream and primary stream exists, do nothing 302 | * - if routing is set to non-primary stream and primary stream doesn't exist, set routing 303 | * Input: 304 | * - buffer size or channel count may change 305 | */ 306 | int pa_droid_stream_set_route(pa_droid_stream *s, dm_config_port *device_port); 307 | 308 | /* Open input stream with currently active routing, sample_spec and channel_map 309 | * are requests and may change when opening the stream. */ 310 | pa_droid_stream *pa_droid_open_input_stream(pa_droid_hw_module *hw_module, 311 | const pa_sample_spec *default_sample_spec, 312 | const pa_channel_map *default_channel_map, 313 | const char *mix_port_name); 314 | /* Test if reconfiguring of input stream is needed */ 315 | bool pa_droid_stream_reconfigure_input_needed(pa_droid_stream *s, 316 | const pa_sample_spec *requested_sample_spec, 317 | const pa_channel_map *requested_channel_map, 318 | const pa_proplist *proplist); 319 | bool pa_droid_stream_reconfigure_input(pa_droid_stream *s, 320 | const pa_sample_spec *requested_sample_spec, 321 | const pa_channel_map *requested_channel_map, 322 | const pa_proplist *proplist); 323 | bool pa_droid_hw_set_input_device(pa_droid_stream *s, 324 | dm_config_port *device_port); 325 | 326 | const pa_sample_spec *pa_droid_stream_sample_spec(pa_droid_stream *stream); 327 | const pa_channel_map *pa_droid_stream_channel_map(pa_droid_stream *stream); 328 | 329 | bool pa_droid_stream_is_primary(pa_droid_stream *s); 330 | 331 | int pa_droid_stream_suspend(pa_droid_stream *s, bool suspend); 332 | 333 | size_t pa_droid_stream_buffer_size(pa_droid_stream *s); 334 | pa_usec_t pa_droid_stream_get_latency(pa_droid_stream *s); 335 | 336 | static inline int pa_droid_output_stream_any_active(pa_droid_stream *s) { 337 | return pa_atomic_load(&s->module->active_outputs); 338 | } 339 | 340 | static inline ssize_t pa_droid_stream_write(pa_droid_stream *stream, const void *buffer, size_t bytes) { 341 | return stream->output->stream->write(stream->output->stream, buffer, bytes); 342 | } 343 | 344 | static inline ssize_t pa_droid_stream_read(pa_droid_stream *stream, void *buffer, size_t bytes) { 345 | return stream->input->stream->read(stream->input->stream, buffer, bytes); 346 | } 347 | 348 | void pa_droid_stream_set_data(pa_droid_stream *s, void *data); 349 | void *pa_droid_stream_get_data(pa_droid_stream *s); 350 | bool pa_sink_is_droid_sink(pa_sink *sink); 351 | bool pa_source_is_droid_source(pa_source *source); 352 | 353 | pa_modargs *pa_droid_modargs_new(const char *args, const char* const keys[]); 354 | 355 | /* Misc */ 356 | size_t pa_droid_buffer_size_round_up(size_t buffer_size, size_t block_size); 357 | 358 | #endif 359 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PulseAudio Droid modules 2 | ======================== 3 | 4 | For adapdations for Android versions 4 to 10, 5 | see [pulseaudio-modules-droid-jb2q](https://github.com/mer-hybris/pulseaudio-modules-droid-jb2q) 6 | 7 | Building of droid modules is split to two packages 8 | * **common** (and **common-devel**) which contains shared library code for use in 9 | PulseAudio modules in this package and for inclusion in other projects 10 | * **droid** with actual PulseAudio modules 11 | 12 | Linking to libdroid is **not encouraged**, usually only HAL functions are needed 13 | which can be accessed using the pulsecore shared API (see below). 14 | 15 | Supported Android versions: 16 | 17 | * 11.x 18 | 19 | Headers for defining devices and strings for different droid versions are in 20 | src/common/droid-util-audio.h. 21 | 22 | When new devices with relevant new enums appear, add enum check to configure.ac. 23 | CC_CHECK_DROID_ENUM macro will create macros HAVE_ENUM_FOO, STRING_ENTRY_IF_FOO 24 | and FANCY_ENTRY_IF_FOO if enum FOO exists in HAL audio.h. 25 | 26 | For example: 27 | 28 | # configure.ac: 29 | CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_IP]) 30 | CC_CHECK_DROID_ENUM([${DROIDHEADERS_CFLAGS}], [AUDIO_DEVICE_OUT_OTHER_NEW]) 31 | 32 | # and then in droid-util-audio.h add macros to proper tables: 33 | /* string_conversion_table_output_device[] */ 34 | STRING_ENTRY_IF_OUT_IP 35 | STRING_ENTRY_IF_OUT_OTHER_NEW 36 | 37 | /* string_conversion_table_output_device_fancy[] */ 38 | FANCY_ENTRY_IF_OUT_IP("output-ip") 39 | FANCY_ENTRY_IF_OUT_OTHER_NEW("output-other_new") 40 | 41 | In addition to the above macros there are also now defines 42 | HAVE_ENUM_AUDIO_DEVICE_OUT_IP and HAVE_ENUM_AUDIO_DEVICE_OUT_OTHER_NEW. 43 | 44 | The purpose of droid-modules is to "replace AudioFlinger". Many hardware 45 | adaptations use ALSA as the kernel interface, but there is no saying that 46 | someday vendor would create and use something proprietary or otherwise 47 | different from ALSA. Also the ALSA implementation in droid devices may contain 48 | funny ways to achieve things (notable example is voicecall) which might be 49 | difficult to do if interfacing directly with ALSA to replace AudioFlinger. 50 | Also using ALSA directly would mean that the whole HAL adaptation would need to 51 | be ported for each new device adaptation. With droid-modules this is much more 52 | simpler, with somewhat stable HAL (HALv3 as of now, also different vendors add 53 | their own incompatible extensions) API. In best scenarios using droid-modules 54 | with new device is just compiling against target. 55 | 56 | Components 57 | ========== 58 | 59 | common 60 | ------ 61 | 62 | The common part of PulseAudio Droid modules contains library for handling 63 | most operations towards audio HAL. 64 | 65 | ### Audio policy configuration parsing 66 | 67 | Configuration parser reads audio policy xml files. 68 | 69 | ### Configuration files 70 | 71 | If the configuration is in non-default location for some reason "config" 72 | module argument can be used to point to the configuration file location. 73 | 74 | By default files are tried in following order, 75 | 76 | /odm/etc/audio_policy_configuration.xml 77 | /vendor/etc/audio/audio_policy_configuration.xml 78 | /vendor/etc/audio_policy_configuration.xml 79 | /system/etc/audio_policy_configuration.xml 80 | 81 | module-droid-card 82 | ----------------- 83 | 84 | Ideally only module-droid-card is loaded and then droid-card loads 85 | configuration, creates profiles and loads sinks and sources based on the 86 | selected profile. 87 | 88 | default profile 89 | --------------- 90 | 91 | When module-droid-card is loaded with default arguments, droid-card will 92 | create a default profile (called unsurprisingly "default"). The default 93 | profile will merge supported output and input streams to one profile, 94 | to allow use of possible low latency or deep buffer outputs. 95 | 96 | virtual profiles 97 | ---------------- 98 | 99 | In addition to aforementioned card profile, droid-card creates some additional 100 | virtual profiles. These virtual profiles are used when enabling voicecall 101 | routings etc. When virtual profile is enabled, possible sinks and sources 102 | previously active profile had are not removed. 103 | 104 | As an illustration, following command line sequence enables voicecall mode and 105 | routes audio to internal handsfree (ihf - "handsfree speaker"): 106 | 107 | pactl set-card-profile droid_card.primary voicecall 108 | pactl set-sink-port sink.primary output-parking 109 | pactl set-sink-port sink.primary output-speaker 110 | 111 | After this, when there is an active voicecall (created by ofono for example), 112 | voice audio starts to flow between modem and audio chip. 113 | 114 | To disable voicecall and return to media audio: 115 | 116 | pactl set-card-profile droid_card.primary default 117 | pactl set-sink-port sink.primary output-parking 118 | pactl set-sink-port sink.primary output-speaker 119 | 120 | With this example sequence sinks and sources are the ones from default 121 | card profile, and they are maintained for the whole duration of the voicecall 122 | and after. 123 | 124 | This sequence follows the droid HAL idea that when changing audio mode the mode 125 | change is done when next routing change happens. output-parking and 126 | input-parking ports are just convenience for PulseAudio, where setting already 127 | active port is a no-op (output/input-parking doesn't do any real routing 128 | changes). 129 | 130 | Current virtual profiles are: 131 | * voicecall 132 | * voicecall-record 133 | * communication 134 | * ringtone 135 | 136 | Communication profile is used for VoIP-like applications, to enable some 137 | voicecall related algorithms without being in voicecall. Ringtone profile 138 | should be used when ringtone is playing, to again enable possible loudness 139 | related optimizations etc. Voicecall-record profile can be enabled when 140 | voicecall profile is active. 141 | 142 | If mix port with flag AUDIO_OUTPUT_FLAG_VOIP_RX exists when communication 143 | virtual profile is enabled additional droid-sink is created with the config 144 | defined in the mix port. Voip audio should then be played to this new sink. 145 | 146 | module-droid-sink and module-droid-source 147 | ----------------------------------------- 148 | 149 | Normally user should not need to load droid-sink or droid-source modules by 150 | hand, but droid-card loads appropriate modules based on the active card 151 | profile. 152 | 153 | Changing output routing is as simple as 154 | 155 | pactl set-sink-port sink.primary output-wired_headphone 156 | 157 | Sinks or sources do not track possible headphone/other wired accessory 158 | plugging, but this needs to be handled elsewhere and then that other entity 159 | needs to control sinks and sources. (For example in SailfishOS this entity is 160 | OHM with accessory-plugin and pulseaudio-policy-enforcement module for 161 | actually making the port switching) 162 | 163 | Droid source automatic reconfiguration 164 | -------------------------------------- 165 | 166 | As droid HAL makes assumptions on (input) routing based on what the parameters 167 | for the stream are (device, sample rate, channels, format, etc.) normal 168 | PulseAudio sources are a bit inflexible as only sample rate can change after 169 | source creation and even then there are restrictions based on alternative 170 | sample rate value. 171 | 172 | To overcome this and to allow some more variables affecting the stream being 173 | passed to the input stream droid source is modified to reconfigure itself 174 | with the source-output that connects to it. This means, that just looking at 175 | inactive source from "pactl list" listing doesn't tell the whole story. 176 | 177 | Droid source is always reconfigured with the *last* source-output that 178 | connects to it, possibly already connected source-outputs will continue 179 | to read from the source but through resampler. 180 | 181 | For example, 182 | 183 | 1) source-output 44100Hz, stereo connects (so1) 184 | 1) source is configured with 44100Hz, stereo 185 | 2) so1 connects to the source without resampler 186 | 2) source-output 16000Hz, mono connects (so2) 187 | 1) so1 is detached from the source 188 | 2) source is configured with 16000Hz, mono 189 | 3) so2 connects to the source without resampler 190 | 4) resampler is created for so1, 16000Hz, mono -> 44100Hz stereo 191 | 5) so1 is re-attached to the source through resampler 192 | 3) source-output 16000Hz, mono connects (so3) 193 | 1) so1 and so2 are detached from the source 194 | 2) so3 connects to the source without resampler 195 | 3) so1 is re-attached to the source through resampler 196 | 4) so2 is attached to the source 197 | 198 | Classifying sinks and sources 199 | ----------------------------- 200 | 201 | Certain property values are set to all active sinks and sources based on their 202 | functionality to ease device classification. 203 | 204 | Currently following properties are set: 205 | 206 | * For droid sinks 207 | * droid.output.primary 208 | * droid.output.low_latency 209 | * droid.output.media_latency 210 | * droid.output.offload 211 | * droid.output.voip 212 | * For droid sources 213 | * droid.input.builtin 214 | * droid.input.external 215 | 216 | If the property is set and with value "true", the sink or source should be 217 | used for the property type. If the property is not defined or contains 218 | value "false" it shouldn't be used for the property type. 219 | 220 | For example, we might have sink.primary and sink.low_latency with following 221 | properties: 222 | 223 | * sink.primary 224 | * droid.output.primary "true" 225 | * droid.output.media_latency "true" 226 | * sink.low_latency 227 | * droid.output.low_latency "true" 228 | 229 | There also may be just one sink, with all the properties defined as "true" 230 | and so on. 231 | 232 | Right now there exists only one source (input device) which will always have 233 | both properties as true. 234 | 235 | Options 236 | ------- 237 | 238 | There are some adaptations that require hacks to get things working. These 239 | hacks can be enabled or disabled with module argument "options". Some options 240 | are enabled by default with some adaptations etc. There are also some more 241 | generic options. 242 | 243 | Currently there are following options: 244 | 245 | * input_atoi 246 | * Enabled by default with Android versions 5 and up. 247 | * Due to how atoi works in bionic vs libc we need to pass the input 248 | route a bit funny. If input routing doesn't work switch this on or off. 249 | * close_input 250 | * Enabled by default. 251 | * Close input stream when not in use instead of suspending the stream. 252 | Cannot be changed when multiple inputs are merged to single source. 253 | * unload_no_close 254 | * Disabled by default. 255 | * Don't call audio_hw_device_close() for the hw module when unloading. 256 | Mostly useful for tracking module unload issues. 257 | * hw_volume 258 | * Enabled by default. 259 | * Some broken implementations are incorrectly probed for supporting hw 260 | volume control. This is manifested by always full volume with volume 261 | control not affecting volume level. To fix this disable this option. 262 | * realcall 263 | * Disabled by default. 264 | * Some vendors apply custom realcall parameter to HAL device when 265 | doing voicecall routing. If there is no voicecall audio you can 266 | try enabling this option so that the realcall parameter is applied 267 | when switching to voicecall profile. 268 | * unload_call_exit 269 | * Disabled by default. 270 | * Some HAL module implementations get stuck in mutex or segfault when 271 | trying to unload the module. To avoid confusing segfaults call 272 | exit(0) instead of calling unload for the module. 273 | * output_fast 274 | * Enabled by default. 275 | * Create separate sink if AUDIO_OUTPUT_FLAG_FAST is found. If this sink 276 | is misbehaving try disabling this option. 277 | * output_deep_buffer 278 | * Enabled by default. 279 | * Create separate sink if AUDIO_OUTPUT_FLAG_DEEP_BUFFER is found. If 280 | this sink is misbehaving try disabling this option. 281 | * audio_cal_wait 282 | * Disabled by default. 283 | * Certain devices do audio calibration during hw module open and 284 | writing audio too early will break the calibration. In these cases 285 | this option can be enabled and 10 seconds of sleep is added after 286 | opening hw module. 287 | * speaker_before_voice 288 | * Disabled by default. 289 | * Set route to speaker before changing audio mode to AUDIO_MODE_IN_CALL. 290 | Some devices don't get routing right if the route is something else 291 | (like AUDIO_DEVICE_OUT_WIRED_HEADSET) before calling set_mode(). 292 | If routing is wrong when call starts with wired accessory connected 293 | try enabling this option. 294 | * output_voip_rx 295 | * Enabled by default. 296 | * When audio configuration has AUDIO_OUTPUT_FLAG_VOIP_RX special voip 297 | sink is created when AUDIO_MODE_IN_COMMUNICATION is active and the 298 | sink is classified as droid.output.voip. If this is not desired then 299 | by disabling this option the voip sink is not classified but is still 300 | created normally. 301 | * record_voice_16k 302 | * Disabled by default. 303 | * When enabled voice call recording source is forced to sample rate 304 | of 16kHz. 305 | 306 | Options can be enabled or disabled normally as module arguments, for example: 307 | 308 | load-module module-droid-card hw_volume=false record_voice_16k=true 309 | 310 | Volume control during voicecall 311 | ------------------------------- 312 | 313 | When voicecall virtual profile is enabled, active droid-sink is internally 314 | switched to voicecall volume control mode. What this means is changing the sink 315 | volume or volume of normal streams connected to the sink do not change active 316 | voicecall volume. Special stream is needed to control the voicecall volume 317 | level. By default this stream is identified by stream property media.role, 318 | with value "phone". This can be changed by providing module arguments 319 | voice_property_key and voice_property_value to module-droid-card. 320 | 321 | Usually droid HAL has 6 volume levels for voicecall. 322 | 323 | Temporary sink audio routing 324 | ---------------------------- 325 | 326 | It is possible to add temporary route to sink audio routing with specific 327 | stream property. When stream with property key 328 | droid.device.additional-route connects to droid-sink, this extra route is set 329 | (if possible) as the enabled route for the duration of the stream. 330 | 331 | For example, if droid-sink has active port output-wired_headphone: 332 | 333 | paplay --property=droid.device.additional-route=AUDIO_DEVICE_OUT_SPEAKER a.wav 334 | 335 | As long as the new stream is connected to droid-sink, output routing is 336 | SPEAKER. 337 | 338 | HAL API 339 | ------- 340 | 341 | If there is need to call HAL directly from other modules it can be done with 342 | function pointer API stored in PulseAudio shared map. 343 | 344 | Once the function pointers are acquired when called they will work the same 345 | way as defined in Android audio.h. For example: 346 | 347 | void *handle; 348 | int (*set_parameters)(void *handle, const char *key_value_pairs); 349 | char* (*get_parameters)(void *handle, const char *keys); 350 | 351 | handle = pa_shared_get(core, "droid.handle.v1"); 352 | set_parameters = pa_shared_get(core, "droid.set_parameters.v1"); 353 | get_parameters = pa_shared_get(core, "droid.get_parameters.v1"); 354 | 355 | set_parameters(handle, "route=2;"); 356 | char *value = get_parameters(handle, "connected"); 357 | -------------------------------------------------------------------------------- /src/common/conversion.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2022 Jolla Ltd. 3 | * 4 | * Contact: Juho Hämäläinen 5 | * 6 | * These PulseAudio Modules are free software; you can redistribute 7 | * it and/or modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation 9 | * version 2.1 of the License. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 | * USA. 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | 26 | #include "droid/version.h" 27 | #if ANDROID_VERSION_MAJOR == 4 && ANDROID_VERSION_MINOR == 1 28 | #include "droid-util-41qc.h" 29 | #else 30 | #include "droid-util-audio.h" 31 | #endif 32 | 33 | #include 34 | 35 | #include 36 | 37 | #include "droid/conversion.h" 38 | #include "droid/droid-config.h" 39 | 40 | #define CONVERT_FUNC(TABL) \ 41 | bool pa_convert_ ## TABL (uint32_t value, pa_conversion_field_t field, uint32_t *to_value) { \ 42 | for (unsigned int i = 0; i < sizeof( conversion_table_ ## TABL )/(sizeof(uint32_t)*2); i++) { \ 43 | if ( conversion_table_ ## TABL [i][field] == value) { \ 44 | *to_value = conversion_table_ ## TABL [i][!field]; \ 45 | return true; \ 46 | } \ 47 | } \ 48 | return false; \ 49 | } struct __funny_extra_to_allow_semicolon 50 | 51 | /* Creates convert_format convert_channel etc. 52 | * bool pa_convert_func(uint32_t value, pa_conversion_field_t field, uint32_t *to_value); 53 | * return true if conversion succesful */ 54 | CONVERT_FUNC(format); 55 | CONVERT_FUNC(output_channel); 56 | CONVERT_FUNC(input_channel); 57 | 58 | #define VALUE_SEPARATOR " ," 59 | 60 | static bool string_convert_num_to_str(const struct string_conversion *list, const uint32_t value, const char **to_str) { 61 | pa_assert(list); 62 | pa_assert(to_str); 63 | 64 | for (unsigned int i = 0; list[i].str; i++) { 65 | if (list[i].value == value) { 66 | *to_str = list[i].str; 67 | return true; 68 | } 69 | } 70 | return false; 71 | } 72 | 73 | static bool string_convert_str_to_num(const struct string_conversion *list, const char *str, uint32_t *to_value) { 74 | pa_assert(list); 75 | pa_assert(str); 76 | pa_assert(to_value); 77 | 78 | for (unsigned int i = 0; list[i].str; i++) { 79 | if (pa_streq(list[i].str, str)) { 80 | *to_value = list[i].value; 81 | return true; 82 | } 83 | } 84 | return false; 85 | } 86 | 87 | static char *list_string(struct string_conversion *list, uint32_t flags) { 88 | char *str = NULL; 89 | char *tmp; 90 | 91 | for (unsigned int i = 0; list[i].str; i++) { 92 | if (popcount(list[i].value) != 1) 93 | continue; 94 | 95 | if (flags & list[i].value) { 96 | if (str) { 97 | tmp = pa_sprintf_malloc("%s|%s", str, list[i].str); 98 | pa_xfree(str); 99 | str = tmp; 100 | } else { 101 | str = pa_sprintf_malloc("%s", list[i].str); 102 | } 103 | } 104 | } 105 | 106 | return str; 107 | } 108 | 109 | /* Generic conversion */ 110 | bool pa_string_convert_num_to_str(pa_conversion_string_t type, uint32_t value, const char **to_str) { 111 | switch (type) { 112 | case CONV_STRING_FORMAT: 113 | return string_convert_num_to_str(string_conversion_table_format, value, to_str); 114 | 115 | case CONV_STRING_OUTPUT_CHANNELS: 116 | return string_convert_num_to_str(string_conversion_table_output_channels, value, to_str); 117 | 118 | case CONV_STRING_INPUT_CHANNELS: 119 | return string_convert_num_to_str(string_conversion_table_input_channels, value, to_str); 120 | 121 | case CONV_STRING_OUTPUT_DEVICE: 122 | return string_convert_num_to_str(string_conversion_table_output_device, value, to_str); 123 | 124 | case CONV_STRING_INPUT_DEVICE: 125 | return string_convert_num_to_str(string_conversion_table_input_device, value, to_str); 126 | 127 | case CONV_STRING_OUTPUT_FLAG: 128 | return string_convert_num_to_str(string_conversion_table_output_flag, value, to_str); 129 | 130 | case CONV_STRING_INPUT_FLAG: 131 | return string_convert_num_to_str(string_conversion_table_input_flag, value, to_str); 132 | 133 | case CONV_STRING_AUDIO_SOURCE_FANCY: 134 | return string_convert_num_to_str(string_conversion_table_audio_source_fancy, value, to_str); 135 | } 136 | 137 | pa_assert_not_reached(); 138 | return false; 139 | } 140 | 141 | bool pa_string_convert_str_to_num(pa_conversion_string_t type, const char *str, uint32_t *to_value) { 142 | switch (type) { 143 | case CONV_STRING_FORMAT: 144 | return string_convert_str_to_num(string_conversion_table_format, str, to_value); 145 | 146 | case CONV_STRING_OUTPUT_CHANNELS: 147 | return string_convert_str_to_num(string_conversion_table_output_channels, str, to_value); 148 | 149 | case CONV_STRING_INPUT_CHANNELS: 150 | return string_convert_str_to_num(string_conversion_table_input_channels, str, to_value); 151 | 152 | case CONV_STRING_OUTPUT_DEVICE: 153 | return string_convert_str_to_num(string_conversion_table_output_device, str, to_value); 154 | 155 | case CONV_STRING_INPUT_DEVICE: 156 | return string_convert_str_to_num(string_conversion_table_input_device, str, to_value); 157 | 158 | case CONV_STRING_OUTPUT_FLAG: 159 | return string_convert_str_to_num(string_conversion_table_output_flag, str, to_value); 160 | 161 | case CONV_STRING_INPUT_FLAG: 162 | return string_convert_str_to_num(string_conversion_table_input_flag, str, to_value); 163 | 164 | case CONV_STRING_AUDIO_SOURCE_FANCY: 165 | return string_convert_str_to_num(string_conversion_table_audio_source_fancy, str, to_value); 166 | } 167 | 168 | pa_assert_not_reached(); 169 | return false; 170 | } 171 | 172 | /* Output device */ 173 | bool pa_string_convert_output_device_num_to_str(audio_devices_t value, const char **to_str) { 174 | return string_convert_num_to_str(string_conversion_table_output_device, (uint32_t) value, to_str); 175 | } 176 | 177 | bool pa_string_convert_output_device_str_to_num(const char *str, audio_devices_t *to_value) { 178 | return string_convert_str_to_num(string_conversion_table_output_device, str, (uint32_t*) to_value); 179 | } 180 | 181 | /* Input device */ 182 | bool pa_string_convert_input_device_num_to_str(audio_devices_t value, const char **to_str) { 183 | return string_convert_num_to_str(string_conversion_table_input_device, (uint32_t) value, to_str); 184 | } 185 | 186 | bool pa_string_convert_input_device_str_to_num(const char *str, audio_devices_t *to_value) { 187 | return string_convert_str_to_num(string_conversion_table_input_device, str, (uint32_t*) to_value); 188 | } 189 | 190 | /* Flags */ 191 | bool pa_string_convert_flag_num_to_str(audio_output_flags_t value, const char **to_str) { 192 | return string_convert_num_to_str(string_conversion_table_output_flag, (uint32_t) value, to_str); 193 | } 194 | 195 | bool pa_string_convert_flag_str_to_num(const char *str, audio_output_flags_t *to_value) { 196 | return string_convert_str_to_num(string_conversion_table_output_flag, str, (uint32_t*) to_value); 197 | } 198 | 199 | char *pa_list_string_flags(audio_output_flags_t flags) { 200 | return list_string(string_conversion_table_output_flag, flags); 201 | } 202 | 203 | bool pa_input_device_default_audio_source(audio_devices_t input_device, audio_source_t *default_source) 204 | { 205 | /* Note converting HAL values to different HAL values! */ 206 | for (unsigned int i = 0; i < sizeof(conversion_table_default_audio_source) / (sizeof(uint32_t) * 2); i++) { 207 | if (conversion_table_default_audio_source[i][0] == input_device) { 208 | *default_source = conversion_table_default_audio_source[i][1]; 209 | return true; 210 | } 211 | } 212 | return false; 213 | } 214 | 215 | 216 | bool pa_droid_output_port_name(audio_devices_t value, const char **to_str) { 217 | return string_convert_num_to_str(string_conversion_table_output_device_fancy, (uint32_t) value, to_str); 218 | } 219 | 220 | bool pa_droid_input_port_name(audio_devices_t value, const char **to_str) { 221 | return string_convert_num_to_str(string_conversion_table_input_device_fancy, (uint32_t) value, to_str); 222 | } 223 | 224 | static int parse_list(const struct string_conversion *table, 225 | const char *separator, 226 | const char *str, 227 | uint32_t *dst, 228 | char **unknown_entries) { 229 | int count = 0; 230 | char *entry; 231 | char *unknown = NULL; 232 | const char *state = NULL; 233 | 234 | pa_assert(table); 235 | pa_assert(separator); 236 | pa_assert(str); 237 | pa_assert(dst); 238 | pa_assert(unknown_entries); 239 | 240 | *dst = 0; 241 | *unknown_entries = NULL; 242 | 243 | while ((entry = pa_split(str, separator, &state))) { 244 | uint32_t d = 0; 245 | 246 | if (!string_convert_str_to_num(table, entry, &d)) { 247 | if (*unknown_entries) { 248 | unknown = pa_sprintf_malloc("%s|%s", *unknown_entries, entry); 249 | pa_xfree(*unknown_entries); 250 | pa_xfree(entry); 251 | } else 252 | unknown = entry; 253 | 254 | *unknown_entries = unknown; 255 | continue; 256 | } 257 | 258 | *dst |= d; 259 | count++; 260 | 261 | pa_xfree(entry); 262 | } 263 | 264 | return count; 265 | } 266 | 267 | int pa_conversion_parse_list(pa_conversion_string_t type, const char *separator, 268 | const char *str, uint32_t *dst, char **unknown_entries) { 269 | switch (type) { 270 | case CONV_STRING_FORMAT: 271 | return parse_list(string_conversion_table_format, separator, str, dst, unknown_entries); 272 | 273 | case CONV_STRING_OUTPUT_CHANNELS: 274 | return parse_list(string_conversion_table_output_channels, separator, str, dst, unknown_entries); 275 | 276 | case CONV_STRING_INPUT_CHANNELS: 277 | return parse_list(string_conversion_table_input_channels, separator, str, dst, unknown_entries); 278 | 279 | case CONV_STRING_OUTPUT_DEVICE: 280 | return parse_list(string_conversion_table_output_device, separator, str, dst, unknown_entries); 281 | 282 | case CONV_STRING_INPUT_DEVICE: 283 | return parse_list(string_conversion_table_input_device, separator, str, dst, unknown_entries); 284 | 285 | case CONV_STRING_OUTPUT_FLAG: 286 | return parse_list(string_conversion_table_output_flag, separator, str, dst, unknown_entries); 287 | 288 | case CONV_STRING_INPUT_FLAG: 289 | return parse_list(string_conversion_table_input_flag, separator, str, dst, unknown_entries); 290 | 291 | /* Not handled in this context */ 292 | case CONV_STRING_AUDIO_SOURCE_FANCY: 293 | return 0; 294 | } 295 | 296 | pa_assert_not_reached(); 297 | return 0; 298 | } 299 | 300 | bool pa_conversion_parse_sampling_rates(const char *fn, const unsigned ln, 301 | const char *str, 302 | uint32_t sampling_rates[AUDIO_MAX_SAMPLING_RATES]) { 303 | pa_assert(fn); 304 | pa_assert(str); 305 | 306 | char *entry; 307 | const char *state = NULL; 308 | 309 | uint32_t pos = 0; 310 | while ((entry = pa_split(str, VALUE_SEPARATOR, &state))) { 311 | int32_t val; 312 | 313 | if (pos == 0 && pa_streq(entry, "dynamic")) { 314 | sampling_rates[pos++] = (uint32_t) -1; 315 | pa_xfree(entry); 316 | break; 317 | } 318 | 319 | if (pos == AUDIO_MAX_SAMPLING_RATES) { 320 | pa_log("[%s:%u] Too many sample rate entries (> %d)", fn, ln, AUDIO_MAX_SAMPLING_RATES); 321 | pa_xfree(entry); 322 | return false; 323 | } 324 | 325 | if (pa_atoi(entry, &val) < 0) { 326 | pa_log("[%s:%u] Bad sample rate value %s", fn, ln, entry); 327 | pa_xfree(entry); 328 | return false; 329 | } 330 | 331 | sampling_rates[pos++] = val; 332 | 333 | pa_xfree(entry); 334 | 335 | } 336 | 337 | sampling_rates[pos] = 0; 338 | 339 | return true; 340 | } 341 | 342 | static bool check_and_log(const char *fn, const unsigned ln, const char *field, 343 | const int count, const char *str, char *unknown, 344 | const bool must_recognize_all) { 345 | bool fail; 346 | 347 | pa_assert(fn); 348 | pa_assert(field); 349 | pa_assert(str); 350 | 351 | fail = must_recognize_all && unknown; 352 | 353 | if (unknown) { 354 | pa_log_info("[%s:%u] Unknown %s entries: %s", fn, ln, field, unknown); 355 | pa_xfree(unknown); 356 | } 357 | 358 | if (count == 0 || fail) { 359 | pa_log("[%s:%u] Failed to parse %s (%s).", fn, ln, field, str); 360 | return false; 361 | } 362 | 363 | return true; 364 | } 365 | 366 | bool pa_conversion_parse_formats(const char *fn, const unsigned ln, 367 | const char *str, 368 | audio_format_t *formats) { 369 | int count; 370 | char *unknown = NULL; 371 | 372 | pa_assert(fn); 373 | pa_assert(str); 374 | pa_assert(formats); 375 | 376 | /* Needs to be probed later */ 377 | if (pa_streq(str, "dynamic")) { 378 | *formats = 0; 379 | return true; 380 | } 381 | 382 | count = pa_conversion_parse_list(CONV_STRING_FORMAT, VALUE_SEPARATOR, str, formats, &unknown); 383 | 384 | /* As the new XML configuration lists formats as one per profile, unknown 385 | * formats will cause the parser to quit. As a workaround for non-legacy 386 | * conversions with no recognized formats log only info level and return false. */ 387 | check_and_log(fn, ln, "format", count == 0 ? 1 : count, str, unknown, false); 388 | return count > 0; 389 | } 390 | 391 | static int parse_channels(const char *fn, const unsigned ln, 392 | const char *str, bool in_output, 393 | audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]) { 394 | bool success; 395 | int count = 0; 396 | char *unknown = NULL; 397 | char *entry; 398 | const char *state = NULL; 399 | 400 | pa_assert(fn); 401 | pa_assert(str); 402 | 403 | /* Needs to be probed later */ 404 | if (pa_streq(str, "dynamic")) { 405 | channel_masks[0] = 0; 406 | return 1; 407 | } 408 | 409 | while ((entry = pa_split(str, VALUE_SEPARATOR, &state))) { 410 | uint32_t val; 411 | 412 | if (count == AUDIO_MAX_CHANNEL_MASKS) { 413 | pa_log("[%s:%u] Too many channel mask entries (> %d)", fn, ln, AUDIO_MAX_CHANNEL_MASKS); 414 | pa_xfree(entry); 415 | return false; 416 | } 417 | 418 | if (!string_convert_str_to_num(in_output ? string_conversion_table_output_channels 419 | : string_conversion_table_input_channels, 420 | entry, 421 | &val)) { 422 | pa_log_debug("[%s:%u] Ignore unknown channel mask value %s", fn, ln, entry); 423 | pa_xfree(entry); 424 | continue; 425 | } 426 | 427 | channel_masks[count++] = val; 428 | 429 | pa_xfree(entry); 430 | } 431 | 432 | channel_masks[count] = 0; 433 | 434 | /* Avoid aborting parsing when no supported channel is found */ 435 | success = check_and_log(fn, ln, in_output ? "output channel_masks" : "input channel_masks", 436 | count == 0 ? 1 : count, str, unknown, false); 437 | return success ? count : -1; 438 | } 439 | 440 | int pa_conversion_parse_output_channels(const char *fn, const unsigned ln, 441 | const char *str, 442 | audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]) { 443 | return parse_channels(fn, ln, str, true, channel_masks); 444 | } 445 | 446 | int pa_conversion_parse_input_channels(const char *fn, const unsigned ln, 447 | const char *str, 448 | audio_channel_mask_t channel_masks[AUDIO_MAX_CHANNEL_MASKS]) { 449 | return parse_channels(fn, ln, str, false, channel_masks); 450 | } 451 | 452 | static bool parse_devices(const char *fn, const unsigned ln, 453 | const char *str, bool in_output, 454 | bool must_recognize_all, 455 | audio_devices_t *devices) { 456 | int count; 457 | char *unknown = NULL; 458 | 459 | pa_assert(fn); 460 | pa_assert(str); 461 | pa_assert(devices); 462 | 463 | count = pa_conversion_parse_list(in_output ? CONV_STRING_OUTPUT_DEVICE : CONV_STRING_INPUT_DEVICE, 464 | VALUE_SEPARATOR, str, devices, &unknown); 465 | 466 | /* As the new XML configuration lists devices as one per devicePort, unknown 467 | * devices will cause the parser to quit. As a workaround for non-legacy 468 | * conversions with no recognized devices log only info level and return false. */ 469 | check_and_log(fn, ln, in_output ? "output device" : "input device", 470 | count == 0 ? 1 : count, str, unknown, must_recognize_all); 471 | return count > 0; 472 | } 473 | 474 | bool pa_conversion_parse_output_devices(const char *fn, const unsigned ln, 475 | char *str, bool must_recognize_all, 476 | audio_devices_t *devices) { 477 | return parse_devices(fn, ln, str, true, must_recognize_all, devices); 478 | } 479 | 480 | bool pa_conversion_parse_input_devices(const char *fn, const unsigned ln, 481 | char *str, bool must_recognize_all, 482 | audio_devices_t *devices) { 483 | return parse_devices(fn, ln, str, false, must_recognize_all, devices); 484 | } 485 | 486 | bool pa_conversion_parse_output_flags(const char *fn, const unsigned ln, 487 | const char *str, audio_output_flags_t *flags) { 488 | int count; 489 | char *unknown = NULL; 490 | 491 | pa_assert(fn); 492 | pa_assert(str); 493 | pa_assert(flags); 494 | 495 | count = pa_conversion_parse_list(CONV_STRING_OUTPUT_FLAG, "| ", str, flags, &unknown); 496 | 497 | return check_and_log(fn, ln, "flags", count, str, unknown, false); 498 | } 499 | 500 | bool pa_conversion_parse_input_flags(const char *fn, const unsigned ln, 501 | const char *str, uint32_t *flags) { 502 | int count; 503 | char *unknown = NULL; 504 | 505 | pa_assert(fn); 506 | pa_assert(str); 507 | pa_assert(flags); 508 | 509 | count = pa_conversion_parse_list(CONV_STRING_INPUT_FLAG, "| ", str, flags, &unknown); 510 | 511 | return check_and_log(fn, ln, "flags", count, str, unknown, false); 512 | } 513 | 514 | bool pa_conversion_parse_version(const char *fn, const unsigned ln, const char *str, uint32_t *version) { 515 | uint32_t version_maj; 516 | uint32_t version_min; 517 | 518 | pa_assert(fn); 519 | pa_assert(str); 520 | pa_assert(version); 521 | 522 | if ((sscanf(str, "%u.%u", &version_maj, &version_min)) != 2) { 523 | pa_log("[%s:%u] Failed to parse %s (%s).", fn, ln, AUDIO_HAL_VERSION_TAG, str); 524 | return false; 525 | } else { 526 | *version = HARDWARE_DEVICE_API_VERSION(version_maj, version_min); 527 | return true; 528 | } 529 | } 530 | -------------------------------------------------------------------------------- /src/droid/droid-source.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2022 Jolla Ltd. 3 | * 4 | * Contact: Juho Hämäläinen 5 | * 6 | * These PulseAudio Modules are free software; you can redistribute 7 | * it and/or modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation 9 | * version 2.1 of the License. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 | * USA. 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | 26 | #include 27 | #include 28 | 29 | #ifdef HAVE_VALGRIND_MEMCHECK_H 30 | #include 31 | #endif 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #include "droid-source.h" 58 | #include 59 | #include 60 | 61 | struct userdata { 62 | pa_core *core; 63 | pa_module *module; 64 | pa_card *card; 65 | pa_source *source; 66 | 67 | pa_thread *thread; 68 | pa_thread_mq thread_mq; 69 | pa_rtpoll *rtpoll; 70 | 71 | pa_memchunk memchunk; 72 | 73 | size_t source_buffer_size; 74 | size_t buffer_size; 75 | pa_usec_t timestamp; 76 | 77 | pa_resampler *resampler; 78 | 79 | pa_droid_card_data *card_data; 80 | pa_droid_hw_module *hw_module; 81 | pa_droid_stream *stream; 82 | bool stream_valid; 83 | }; 84 | 85 | #define DEFAULT_MODULE_ID "primary" 86 | 87 | #define DROID_AUDIO_SOURCE "droid.audio_source" 88 | #define DROID_AUDIO_SOURCE_UNDEFINED "undefined" 89 | 90 | static void userdata_free(struct userdata *u); 91 | static int suspend(struct userdata *u); 92 | static void unsuspend(struct userdata *u); 93 | static void source_reconfigure(struct userdata *u, 94 | const pa_sample_spec *reconfigure_sample_spec, 95 | const pa_channel_map *reconfigure_channel_map, 96 | const pa_proplist *proplist, 97 | dm_config_port *update_device_port); 98 | 99 | /* Our droid source may be left in a state of not having an input stream 100 | * if reconfiguration fails and fallback to previously active values fails 101 | * as well. In this case just avoid using the stream but don't die. */ 102 | #define assert_stream(x, action) if (!x) do { pa_log_warn("Assert " #x " failed."); action; } while(0) 103 | 104 | static int thread_read(struct userdata *u) { 105 | void *p; 106 | ssize_t readd; 107 | pa_memchunk chunk; 108 | 109 | chunk.index = 0; 110 | chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) u->buffer_size); 111 | 112 | if (!u->stream_valid) { 113 | /* try to resume or post silence */ 114 | unsuspend(u); 115 | if (!u->stream_valid) { 116 | p = pa_memblock_acquire(chunk.memblock); 117 | chunk.length = pa_memblock_get_length(chunk.memblock); 118 | pa_silence_memory(p, chunk.length, &u->source->sample_spec); 119 | pa_source_post(u->source, &chunk); 120 | pa_memblock_release(chunk.memblock); 121 | goto end; 122 | } 123 | } 124 | 125 | p = pa_memblock_acquire(chunk.memblock); 126 | readd = pa_droid_stream_read(u->stream, p, pa_memblock_get_length(chunk.memblock)); 127 | pa_memblock_release(chunk.memblock); 128 | 129 | if (readd < 0) { 130 | pa_log("Failed to read from stream. (err %zd)", readd); 131 | goto end; 132 | } 133 | 134 | u->timestamp += pa_bytes_to_usec(readd, &u->source->sample_spec); 135 | 136 | chunk.length = readd; 137 | 138 | if (u->resampler) { 139 | pa_memchunk rchunk; 140 | 141 | pa_resampler_run(u->resampler, &chunk, &rchunk); 142 | 143 | if (rchunk.length > 0) 144 | pa_source_post(u->source, &rchunk); 145 | if (rchunk.memblock) 146 | pa_memblock_unref(rchunk.memblock); 147 | 148 | goto end; 149 | } 150 | 151 | if (chunk.length > 0) 152 | pa_source_post(u->source, &chunk); 153 | 154 | end: 155 | pa_memblock_unref(chunk.memblock); 156 | 157 | return 0; 158 | } 159 | 160 | static void thread_func(void *userdata) { 161 | struct userdata *u = userdata; 162 | 163 | pa_assert(u); 164 | pa_assert(u->stream); 165 | 166 | pa_log_debug("Thread starting up."); 167 | 168 | if (u->core->realtime_scheduling) 169 | #if PA_CHECK_VERSION(13,0,0) 170 | pa_thread_make_realtime(u->core->realtime_priority); 171 | #else 172 | pa_make_realtime(u->core->realtime_priority); 173 | #endif 174 | 175 | pa_thread_mq_install(&u->thread_mq); 176 | 177 | u->timestamp = pa_rtclock_now(); 178 | 179 | for (;;) { 180 | int ret; 181 | 182 | if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { 183 | thread_read(u); 184 | 185 | pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp); 186 | } else 187 | pa_rtpoll_set_timer_disabled(u->rtpoll); 188 | 189 | /* Sleep */ 190 | if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) 191 | goto fail; 192 | 193 | if (ret == 0) 194 | goto finish; 195 | 196 | } 197 | 198 | fail: 199 | /* If this was no regular exit from the loop we have to continue 200 | * processing messages until we received PA_MESSAGE_SHUTDOWN */ 201 | pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); 202 | pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); 203 | 204 | finish: 205 | pa_log_debug("Thread shutting down."); 206 | } 207 | 208 | /* Called from IO context */ 209 | static int suspend(struct userdata *u) { 210 | int ret; 211 | 212 | pa_assert(u); 213 | assert_stream(u->stream, return 0); 214 | 215 | ret = pa_droid_stream_suspend(u->stream, true); 216 | 217 | if (ret == 0) 218 | pa_log_info("Device suspended."); 219 | 220 | return ret; 221 | } 222 | 223 | /* Called from IO context */ 224 | static void unsuspend(struct userdata *u) { 225 | pa_assert(u); 226 | 227 | if (!u->stream) { 228 | assert_stream(u->stream, u->stream_valid = false); 229 | } else if (pa_droid_stream_suspend(u->stream, false) >= 0) { 230 | u->stream_valid = true; 231 | pa_log_info("Resuming..."); 232 | } else 233 | u->stream_valid = false; 234 | } 235 | 236 | /* Called from IO context */ 237 | static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_state, pa_suspend_cause_t new_suspend_cause) { 238 | struct userdata *u; 239 | int r; 240 | 241 | pa_assert(s); 242 | pa_assert_se(u = s->userdata); 243 | 244 | /* It may be that only the suspend cause is changing, in which case there's 245 | * nothing more to do. */ 246 | if (new_state == s->thread_info.state) 247 | return 0; 248 | 249 | switch (new_state) { 250 | case PA_SOURCE_SUSPENDED: 251 | if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { 252 | if ((r = suspend(u)) < 0) 253 | return r; 254 | } 255 | 256 | break; 257 | 258 | case PA_SOURCE_IDLE: 259 | /* Fall through */ 260 | case PA_SOURCE_RUNNING: 261 | if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) { 262 | unsuspend(u); 263 | u->timestamp = pa_rtclock_now(); 264 | } 265 | break; 266 | 267 | case PA_SOURCE_UNLINKED: 268 | /* Suspending since some implementations do not want to free running stream. */ 269 | suspend(u); 270 | break; 271 | 272 | /* not needed */ 273 | case PA_SOURCE_INIT: 274 | case PA_SOURCE_INVALID_STATE: 275 | break; 276 | } 277 | 278 | return 0; 279 | } 280 | 281 | static int source_set_port_cb(pa_source *s, pa_device_port *p) { 282 | struct userdata *u = s->userdata; 283 | pa_droid_port_data *data; 284 | 285 | pa_assert(u); 286 | pa_assert(p); 287 | 288 | data = PA_DEVICE_PORT_DATA(p); 289 | 290 | if (!data->device_port) { 291 | /* If there is no device defined, just return 0 to say everything is ok. 292 | * Then next port change can be whatever source port, even the one enabled 293 | * before parking. */ 294 | pa_log_debug("Source set port to parking"); 295 | return 0; 296 | } 297 | 298 | pa_log_debug("Source set port %#010x (%s)", data->device_port->type, data->device_port->name); 299 | 300 | if (!PA_SOURCE_IS_OPENED(u->source->state)) 301 | pa_droid_stream_set_route(u->stream, data->device_port); 302 | else 303 | source_reconfigure(u, NULL, NULL, NULL, data->device_port); 304 | 305 | return 0; 306 | } 307 | 308 | static void source_set_name(pa_modargs *ma, pa_source_new_data *data, const char *module_id) { 309 | const char *tmp; 310 | 311 | pa_assert(ma); 312 | pa_assert(data); 313 | 314 | if ((tmp = pa_modargs_get_value(ma, "source_name", NULL))) { 315 | pa_source_new_data_set_name(data, tmp); 316 | data->namereg_fail = true; 317 | pa_proplist_sets(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid source"); 318 | } else { 319 | char *tt; 320 | pa_assert(module_id); 321 | tt = pa_sprintf_malloc("source.%s", module_id); 322 | pa_source_new_data_set_name(data, tt); 323 | pa_xfree(tt); 324 | data->namereg_fail = false; 325 | pa_proplist_setf(data->proplist, PA_PROP_DEVICE_DESCRIPTION, "Droid source %s", module_id); 326 | } 327 | } 328 | 329 | static int source_get_mute_cb(pa_source *s, bool *muted) { 330 | struct userdata *u = s->userdata; 331 | 332 | pa_assert(u); 333 | pa_assert(u->hw_module); 334 | 335 | return pa_droid_hw_mic_get_mute(u->hw_module, muted); 336 | } 337 | 338 | static void source_set_mute_cb(pa_source *s) { 339 | struct userdata *u = s->userdata; 340 | 341 | pa_assert(u); 342 | 343 | pa_droid_hw_mic_set_mute(u->hw_module, s->muted); 344 | } 345 | 346 | static void source_set_mute_control(struct userdata *u) { 347 | pa_assert(u); 348 | pa_assert(u->hw_module && u->hw_module->device); 349 | 350 | if (pa_droid_hw_has_mic_control(u->hw_module)) { 351 | pa_source_set_get_mute_callback(u->source, source_get_mute_cb); 352 | pa_source_set_set_mute_callback(u->source, source_set_mute_cb); 353 | } 354 | } 355 | 356 | /* Called from main and IO context */ 357 | static void update_latency(struct userdata *u) { 358 | pa_assert(u); 359 | pa_assert(u->source); 360 | 361 | if (u->stream) 362 | u->buffer_size = pa_droid_stream_buffer_size(u->stream); 363 | else 364 | u->buffer_size = 1024; /* Random valid value */ 365 | 366 | assert_stream(u->stream, return); 367 | 368 | if (u->source_buffer_size) { 369 | u->buffer_size = pa_droid_buffer_size_round_up(u->source_buffer_size, u->buffer_size); 370 | pa_log_info("Using buffer size %zu (requested %zu).", u->buffer_size, u->source_buffer_size); 371 | } else 372 | pa_log_info("Using buffer size %zu.", u->buffer_size); 373 | 374 | if (pa_thread_mq_get()) 375 | pa_source_set_fixed_latency_within_thread(u->source, pa_bytes_to_usec(u->buffer_size, pa_droid_stream_sample_spec(u->stream))); 376 | else 377 | pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, pa_droid_stream_sample_spec(u->stream))); 378 | 379 | pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, pa_droid_stream_sample_spec(u->stream))); 380 | } 381 | 382 | static void source_reconfigure(struct userdata *u, 383 | const pa_sample_spec *reconfigure_sample_spec, 384 | const pa_channel_map *reconfigure_channel_map, 385 | const pa_proplist *proplist, 386 | dm_config_port *update_device_port) { 387 | pa_channel_map old_channel_map; 388 | pa_sample_spec old_sample_spec; 389 | pa_channel_map new_channel_map; 390 | pa_sample_spec new_sample_spec; 391 | pa_queue *source_outputs = NULL; 392 | 393 | if (pa_source_used_by(u->source)) { 394 | /* If we already have connected source outputs detach those 395 | * so that when re-attaching them to our source resampling etc. 396 | * is renegotiated correctly. */ 397 | source_outputs = pa_source_move_all_start(u->source, NULL); 398 | } 399 | 400 | pa_source_suspend(u->source, true, PA_SUSPEND_UNAVAILABLE); 401 | 402 | old_channel_map = *pa_droid_stream_channel_map(u->stream); 403 | old_sample_spec = *pa_droid_stream_sample_spec(u->stream); 404 | new_channel_map = reconfigure_channel_map ? *reconfigure_channel_map : old_channel_map; 405 | new_sample_spec = reconfigure_sample_spec ? *reconfigure_sample_spec : old_sample_spec; 406 | 407 | if (update_device_port) 408 | pa_droid_stream_set_route(u->stream, update_device_port); 409 | 410 | if (pa_droid_stream_reconfigure_input(u->stream, &new_sample_spec, &new_channel_map, proplist)) 411 | pa_log_info("Source reconfigured."); 412 | else 413 | pa_log_info("Failed to reconfigure input stream, no worries, using defaults."); 414 | 415 | /* We need to be really careful here as we are modifying 416 | * quite profound internal structures. */ 417 | new_sample_spec = *pa_droid_stream_sample_spec(u->stream); 418 | new_channel_map = *pa_droid_stream_channel_map(u->stream); 419 | u->source->channel_map = new_channel_map; 420 | u->source->sample_spec = new_sample_spec; 421 | pa_assert_se(pa_cvolume_remap(&u->source->reference_volume, &old_channel_map, &new_channel_map)); 422 | pa_assert_se(pa_cvolume_remap(&u->source->real_volume, &old_channel_map, &new_channel_map)); 423 | pa_assert_se(pa_cvolume_remap(&u->source->soft_volume, &old_channel_map, &new_channel_map)); 424 | 425 | update_latency(u); 426 | pa_source_suspend(u->source, false, PA_SUSPEND_UNAVAILABLE); 427 | 428 | if (source_outputs && u->source) { 429 | pa_source_move_all_finish(u->source, source_outputs, false); 430 | } 431 | } 432 | 433 | static pa_hook_result_t source_output_new_hook_callback(void *hook_data, 434 | void *call_data, 435 | void *slot_data) { 436 | pa_source_output_new_data *new_data = call_data; 437 | struct userdata *u = slot_data; 438 | pa_droid_stream *primary_output; 439 | 440 | /* Not meant for us */ 441 | if (new_data->source != u->source) 442 | return PA_HOOK_OK; 443 | 444 | if (!pa_droid_stream_reconfigure_input_needed(u->stream, 445 | &new_data->sample_spec, 446 | &new_data->channel_map, 447 | new_data->proplist)) 448 | return PA_HOOK_OK; 449 | 450 | pa_log_info("New source-output connecting and our source needs to be reconfigured."); 451 | 452 | /* Workaround for fm-radio loopback */ 453 | if (pa_safe_streq(pa_proplist_gets(new_data->proplist, "media.name"), "fmradio-loopback-source") && 454 | (primary_output = pa_droid_hw_primary_output_stream(u->hw_module))) { 455 | pa_log_debug("Workaround for fm-radio loopback."); 456 | source_reconfigure(u, 457 | pa_droid_stream_sample_spec(primary_output), 458 | pa_droid_stream_channel_map(primary_output), 459 | new_data->proplist, 460 | NULL); 461 | 462 | } else 463 | source_reconfigure(u, &new_data->sample_spec, &new_data->channel_map, new_data->proplist, NULL); 464 | 465 | return PA_HOOK_OK; 466 | } 467 | 468 | static void source_reconfigure_after_changes(struct userdata *u) { 469 | pa_source_output *so = NULL; 470 | pa_source_output *so_i; 471 | void *state = NULL; 472 | 473 | if (!pa_source_used_by(u->source)) 474 | return; 475 | 476 | /* Find last inserted source-output */ 477 | so = pa_idxset_iterate(u->source->outputs, &state, NULL); 478 | if (so) { 479 | while ((so_i = pa_idxset_iterate(u->source->outputs, &state, NULL))) 480 | so = so_i; 481 | } 482 | 483 | if (so && pa_droid_stream_reconfigure_input_needed(u->stream, 484 | &so->sample_spec, 485 | &so->channel_map, 486 | so->proplist)) { 487 | pa_log_info("Source-output disconnected and our source needs to be reconfigured."); 488 | source_reconfigure(u, &so->sample_spec, &so->channel_map, so->proplist, NULL); 489 | } 490 | } 491 | 492 | static pa_hook_result_t source_output_unlink_post_hook_callback(void *hook_data, 493 | void *call_data, 494 | void *slot_data) { 495 | source_reconfigure_after_changes(slot_data); 496 | return PA_HOOK_OK; 497 | } 498 | 499 | pa_source *pa_droid_source_new(pa_module *m, 500 | pa_modargs *ma, 501 | const char *driver, 502 | pa_droid_card_data *card_data, 503 | pa_droid_mapping *am, 504 | pa_card *card) { 505 | 506 | struct userdata *u = NULL; 507 | char *thread_name = NULL; 508 | pa_source_new_data data; 509 | const char *module_id = NULL; 510 | uint32_t alternate_sample_rate; 511 | pa_sample_spec sample_spec; 512 | pa_channel_map channel_map; 513 | const char *format; 514 | bool namereg_fail = false; 515 | uint32_t source_buffer = 0; 516 | 517 | pa_assert(m); 518 | pa_assert(ma); 519 | pa_assert(driver); 520 | 521 | pa_log_info("Create new droid-source"); 522 | 523 | /* When running under card use hw module name for source by default. */ 524 | if (am) 525 | module_id = am->mix_port->name; 526 | else 527 | module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID); 528 | 529 | sample_spec = m->core->default_sample_spec; 530 | channel_map = m->core->default_channel_map; 531 | 532 | /* First parse both sample spec and channel map, then see if source_* override some 533 | * of the values. */ 534 | if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) { 535 | pa_log("Failed to parse source sample specification and channel map."); 536 | goto fail; 537 | } 538 | 539 | if (pa_modargs_get_value(ma, "source_channel_map", NULL)) { 540 | if (pa_modargs_get_channel_map(ma, "source_channel_map", &channel_map) < 0) { 541 | pa_log("Failed to parse source channel map."); 542 | goto fail; 543 | } 544 | 545 | sample_spec.channels = channel_map.channels; 546 | } 547 | 548 | if ((format = pa_modargs_get_value(ma, "source_format", NULL))) { 549 | if ((sample_spec.format = pa_parse_sample_format(format)) < 0) { 550 | pa_log("Failed to parse source format."); 551 | goto fail; 552 | } 553 | } 554 | 555 | if (pa_modargs_get_value_u32(ma, "source_rate", &sample_spec.rate) < 0) { 556 | pa_log("Failed to parse source_rate."); 557 | goto fail; 558 | } 559 | 560 | if (!pa_sample_spec_valid(&sample_spec)) { 561 | pa_log("Sample spec is not valid."); 562 | goto fail; 563 | } 564 | 565 | alternate_sample_rate = m->core->alternate_sample_rate; 566 | if (pa_modargs_get_alternate_sample_rate(ma, &alternate_sample_rate) < 0) { 567 | pa_log("Failed to parse alternate sample rate."); 568 | goto fail; 569 | } 570 | 571 | if (pa_modargs_get_value_u32(ma, "source_buffer", &source_buffer) < 0) { 572 | pa_log("Failed to parse source_buffer. Needs to be integer >= 0."); 573 | goto fail; 574 | } 575 | 576 | u = pa_xnew0(struct userdata, 1); 577 | u->stream_valid = true; 578 | u->core = m->core; 579 | u->module = m; 580 | u->card = card; 581 | u->rtpoll = pa_rtpoll_new(); 582 | pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); 583 | 584 | if (card_data) { 585 | pa_assert(card); 586 | u->card_data = card_data; 587 | pa_assert_se((u->hw_module = pa_droid_hw_module_get(u->core, NULL, card_data->module_id))); 588 | } else { 589 | /* Source wasn't created from inside card module, so we'll need to open 590 | * hw module ourself. */ 591 | 592 | if (!(u->hw_module = pa_droid_hw_module_get2(u->core, ma, module_id))) 593 | goto fail; 594 | } 595 | 596 | u->stream = pa_droid_open_input_stream(u->hw_module, &sample_spec, &channel_map, am->mix_port->name); 597 | 598 | if (!u->stream) { 599 | pa_log("Failed to open input stream."); 600 | goto fail; 601 | } 602 | 603 | pa_source_new_data_init(&data); 604 | data.driver = driver; 605 | data.module = m; 606 | data.card = card; 607 | /* Start suspended */ 608 | data.suspend_cause = PA_SUSPEND_IDLE; 609 | 610 | if (am) 611 | source_set_name(ma, &data, am->name); 612 | else 613 | source_set_name(ma, &data, module_id); 614 | 615 | pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound"); 616 | pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, PROP_DROID_API_STRING); 617 | pa_proplist_sets(data.proplist, PROP_DROID_INPUT_EXTERNAL, "true"); 618 | pa_proplist_sets(data.proplist, PROP_DROID_INPUT_BUILTIN, "true"); 619 | 620 | /* We need to give pa_modargs_get_value_boolean() a pointer to a local 621 | * variable instead of using &data.namereg_fail directly, because 622 | * data.namereg_fail is a bitfield and taking the address of a bitfield 623 | * variable is impossible. */ 624 | namereg_fail = data.namereg_fail; 625 | if (pa_modargs_get_value_boolean(ma, "namereg_fail", &namereg_fail) < 0) { 626 | pa_log("Failed to parse namereg_fail argument."); 627 | pa_source_new_data_done(&data); 628 | goto fail; 629 | } 630 | data.namereg_fail = namereg_fail; 631 | 632 | pa_source_new_data_set_sample_spec(&data, pa_droid_stream_sample_spec(u->stream)); 633 | pa_source_new_data_set_channel_map(&data, pa_droid_stream_channel_map(u->stream)); 634 | pa_source_new_data_set_alternate_sample_rate(&data, alternate_sample_rate); 635 | 636 | if (am && card) 637 | pa_droid_add_ports(data.ports, am, card); 638 | 639 | u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE); 640 | pa_source_new_data_done(&data); 641 | 642 | if (!u->source) { 643 | pa_log("Failed to create source."); 644 | goto fail; 645 | } 646 | 647 | u->source->userdata = u; 648 | 649 | u->source->parent.process_msg = pa_source_process_msg; 650 | u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb; 651 | 652 | source_set_mute_control(u); 653 | 654 | u->source->set_port = source_set_port_cb; 655 | 656 | pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); 657 | pa_source_set_rtpoll(u->source, u->rtpoll); 658 | 659 | /* Disable rewind for droid source */ 660 | pa_source_set_max_rewind(u->source, 0); 661 | 662 | thread_name = pa_sprintf_malloc("droid-source-%s", module_id); 663 | if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) { 664 | pa_log("Failed to create thread."); 665 | goto fail; 666 | } 667 | pa_xfree(thread_name); 668 | thread_name = NULL; 669 | 670 | update_latency(u); 671 | 672 | if (u->source->active_port) 673 | source_set_port_cb(u->source, u->source->active_port); 674 | 675 | /* Since we started in suspended mode suspend our stream immediately as well. */ 676 | pa_droid_stream_suspend(u->stream, true); 677 | 678 | pa_droid_stream_set_data(u->stream, u->source); 679 | pa_source_put(u->source); 680 | 681 | /* As late as possible */ 682 | pa_module_hook_connect(u->module, 683 | &u->module->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], 684 | PA_HOOK_LATE * 2, 685 | source_output_new_hook_callback, u); 686 | 687 | pa_module_hook_connect(u->module, 688 | &u->module->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], 689 | PA_HOOK_LATE * 2, 690 | source_output_unlink_post_hook_callback, u); 691 | 692 | return u->source; 693 | 694 | fail: 695 | pa_xfree(thread_name); 696 | 697 | if (u) 698 | userdata_free(u); 699 | 700 | return NULL; 701 | } 702 | 703 | void pa_droid_source_free(pa_source *s) { 704 | struct userdata *u; 705 | 706 | pa_source_assert_ref(s); 707 | pa_assert_se(u = s->userdata); 708 | 709 | userdata_free(u); 710 | } 711 | 712 | static void userdata_free(struct userdata *u) { 713 | pa_assert(u); 714 | 715 | if (u->source) 716 | pa_source_unlink(u->source); 717 | 718 | if (u->thread) { 719 | pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); 720 | pa_thread_free(u->thread); 721 | } 722 | 723 | pa_thread_mq_done(&u->thread_mq); 724 | 725 | if (u->source) 726 | pa_source_unref(u->source); 727 | 728 | if (u->memchunk.memblock) 729 | pa_memblock_unref(u->memchunk.memblock); 730 | 731 | if (u->stream) 732 | pa_droid_stream_unref(u->stream); 733 | 734 | if (u->hw_module) 735 | pa_droid_hw_module_unref(u->hw_module); 736 | 737 | pa_xfree(u); 738 | } 739 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | GNU LESSER GENERAL PUBLIC LICENSE 3 | Version 2.1, February 1999 4 | 5 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 6 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | [This is the first released version of the Lesser GPL. It also counts 11 | as the successor of the GNU Library Public License, version 2, hence 12 | the version number 2.1.] 13 | 14 | Preamble 15 | 16 | The licenses for most software are designed to take away your 17 | freedom to share and change it. By contrast, the GNU General Public 18 | Licenses are intended to guarantee your freedom to share and change 19 | free software--to make sure the software is free for all its users. 20 | 21 | This license, the Lesser General Public License, applies to some 22 | specially designated software packages--typically libraries--of the 23 | Free Software Foundation and other authors who decide to use it. You 24 | can use it too, but we suggest you first think carefully about whether 25 | this license or the ordinary General Public License is the better 26 | strategy to use in any particular case, based on the explanations 27 | below. 28 | 29 | When we speak of free software, we are referring to freedom of use, 30 | not price. Our General Public Licenses are designed to make sure that 31 | you have the freedom to distribute copies of free software (and charge 32 | for this service if you wish); that you receive source code or can get 33 | it if you want it; that you can change the software and use pieces of 34 | it in new free programs; and that you are informed that you can do 35 | these things. 36 | 37 | To protect your rights, we need to make restrictions that forbid 38 | distributors to deny you these rights or to ask you to surrender these 39 | rights. These restrictions translate to certain responsibilities for 40 | you if you distribute copies of the library or if you modify it. 41 | 42 | For example, if you distribute copies of the library, whether gratis 43 | or for a fee, you must give the recipients all the rights that we gave 44 | you. You must make sure that they, too, receive or can get the source 45 | code. If you link other code with the library, you must provide 46 | complete object files to the recipients, so that they can relink them 47 | with the library after making changes to the library and recompiling 48 | it. And you must show them these terms so they know their rights. 49 | 50 | We protect your rights with a two-step method: (1) we copyright the 51 | library, and (2) we offer you this license, which gives you legal 52 | permission to copy, distribute and/or modify the library. 53 | 54 | To protect each distributor, we want to make it very clear that 55 | there is no warranty for the free library. Also, if the library is 56 | modified by someone else and passed on, the recipients should know 57 | that what they have is not the original version, so that the original 58 | author's reputation will not be affected by problems that might be 59 | introduced by others. 60 | 61 | Finally, software patents pose a constant threat to the existence of 62 | any free program. We wish to make sure that a company cannot 63 | effectively restrict the users of a free program by obtaining a 64 | restrictive license from a patent holder. Therefore, we insist that 65 | any patent license obtained for a version of the library must be 66 | consistent with the full freedom of use specified in this license. 67 | 68 | Most GNU software, including some libraries, is covered by the 69 | ordinary GNU General Public License. This license, the GNU Lesser 70 | General Public License, applies to certain designated libraries, and 71 | is quite different from the ordinary General Public License. We use 72 | this license for certain libraries in order to permit linking those 73 | libraries into non-free programs. 74 | 75 | When a program is linked with a library, whether statically or using 76 | a shared library, the combination of the two is legally speaking a 77 | combined work, a derivative of the original library. The ordinary 78 | General Public License therefore permits such linking only if the 79 | entire combination fits its criteria of freedom. The Lesser General 80 | Public License permits more lax criteria for linking other code with 81 | the library. 82 | 83 | We call this license the "Lesser" General Public License because it 84 | does Less to protect the user's freedom than the ordinary General 85 | Public License. It also provides other free software developers Less 86 | of an advantage over competing non-free programs. These disadvantages 87 | are the reason we use the ordinary General Public License for many 88 | libraries. However, the Lesser license provides advantages in certain 89 | special circumstances. 90 | 91 | For example, on rare occasions, there may be a special need to 92 | encourage the widest possible use of a certain library, so that it 93 | becomes a de-facto standard. To achieve this, non-free programs must 94 | be allowed to use the library. A more frequent case is that a free 95 | library does the same job as widely used non-free libraries. In this 96 | case, there is little to gain by limiting the free library to free 97 | software only, so we use the Lesser General Public License. 98 | 99 | In other cases, permission to use a particular library in non-free 100 | programs enables a greater number of people to use a large body of 101 | free software. For example, permission to use the GNU C Library in 102 | non-free programs enables many more people to use the whole GNU 103 | operating system, as well as its variant, the GNU/Linux operating 104 | system. 105 | 106 | Although the Lesser General Public License is Less protective of the 107 | users' freedom, it does ensure that the user of a program that is 108 | linked with the Library has the freedom and the wherewithal to run 109 | that program using a modified version of the Library. 110 | 111 | The precise terms and conditions for copying, distribution and 112 | modification follow. Pay close attention to the difference between a 113 | "work based on the library" and a "work that uses the library". The 114 | former contains code derived from the library, whereas the latter must 115 | be combined with the library in order to run. 116 | 117 | GNU LESSER GENERAL PUBLIC LICENSE 118 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 119 | 120 | 0. This License Agreement applies to any software library or other 121 | program which contains a notice placed by the copyright holder or 122 | other authorized party saying it may be distributed under the terms of 123 | this Lesser General Public License (also called "this License"). 124 | Each licensee is addressed as "you". 125 | 126 | A "library" means a collection of software functions and/or data 127 | prepared so as to be conveniently linked with application programs 128 | (which use some of those functions and data) to form executables. 129 | 130 | The "Library", below, refers to any such software library or work 131 | which has been distributed under these terms. A "work based on the 132 | Library" means either the Library or any derivative work under 133 | copyright law: that is to say, a work containing the Library or a 134 | portion of it, either verbatim or with modifications and/or translated 135 | straightforwardly into another language. (Hereinafter, translation is 136 | included without limitation in the term "modification".) 137 | 138 | "Source code" for a work means the preferred form of the work for 139 | making modifications to it. For a library, complete source code means 140 | all the source code for all modules it contains, plus any associated 141 | interface definition files, plus the scripts used to control 142 | compilation and installation of the library. 143 | 144 | Activities other than copying, distribution and modification are not 145 | covered by this License; they are outside its scope. The act of 146 | running a program using the Library is not restricted, and output from 147 | such a program is covered only if its contents constitute a work based 148 | on the Library (independent of the use of the Library in a tool for 149 | writing it). Whether that is true depends on what the Library does 150 | and what the program that uses the Library does. 151 | 152 | 1. You may copy and distribute verbatim copies of the Library's 153 | complete source code as you receive it, in any medium, provided that 154 | you conspicuously and appropriately publish on each copy an 155 | appropriate copyright notice and disclaimer of warranty; keep intact 156 | all the notices that refer to this License and to the absence of any 157 | warranty; and distribute a copy of this License along with the 158 | Library. 159 | 160 | You may charge a fee for the physical act of transferring a copy, 161 | and you may at your option offer warranty protection in exchange for a 162 | fee. 163 | 164 | 2. You may modify your copy or copies of the Library or any portion 165 | of it, thus forming a work based on the Library, and copy and 166 | distribute such modifications or work under the terms of Section 1 167 | above, provided that you also meet all of these conditions: 168 | 169 | a) The modified work must itself be a software library. 170 | 171 | b) You must cause the files modified to carry prominent notices 172 | stating that you changed the files and the date of any change. 173 | 174 | c) You must cause the whole of the work to be licensed at no 175 | charge to all third parties under the terms of this License. 176 | 177 | d) If a facility in the modified Library refers to a function or a 178 | table of data to be supplied by an application program that uses 179 | the facility, other than as an argument passed when the facility 180 | is invoked, then you must make a good faith effort to ensure that, 181 | in the event an application does not supply such function or 182 | table, the facility still operates, and performs whatever part of 183 | its purpose remains meaningful. 184 | 185 | (For example, a function in a library to compute square roots has 186 | a purpose that is entirely well-defined independent of the 187 | application. Therefore, Subsection 2d requires that any 188 | application-supplied function or table used by this function must 189 | be optional: if the application does not supply it, the square 190 | root function must still compute square roots.) 191 | 192 | These requirements apply to the modified work as a whole. If 193 | identifiable sections of that work are not derived from the Library, 194 | and can be reasonably considered independent and separate works in 195 | themselves, then this License, and its terms, do not apply to those 196 | sections when you distribute them as separate works. But when you 197 | distribute the same sections as part of a whole which is a work based 198 | on the Library, the distribution of the whole must be on the terms of 199 | this License, whose permissions for other licensees extend to the 200 | entire whole, and thus to each and every part regardless of who wrote 201 | it. 202 | 203 | Thus, it is not the intent of this section to claim rights or contest 204 | your rights to work written entirely by you; rather, the intent is to 205 | exercise the right to control the distribution of derivative or 206 | collective works based on the Library. 207 | 208 | In addition, mere aggregation of another work not based on the Library 209 | with the Library (or with a work based on the Library) on a volume of 210 | a storage or distribution medium does not bring the other work under 211 | the scope of this License. 212 | 213 | 3. You may opt to apply the terms of the ordinary GNU General Public 214 | License instead of this License to a given copy of the Library. To do 215 | this, you must alter all the notices that refer to this License, so 216 | that they refer to the ordinary GNU General Public License, version 2, 217 | instead of to this License. (If a newer version than version 2 of the 218 | ordinary GNU General Public License has appeared, then you can specify 219 | that version instead if you wish.) Do not make any other change in 220 | these notices. 221 | 222 | Once this change is made in a given copy, it is irreversible for 223 | that copy, so the ordinary GNU General Public License applies to all 224 | subsequent copies and derivative works made from that copy. 225 | 226 | This option is useful when you wish to copy part of the code of 227 | the Library into a program that is not a library. 228 | 229 | 4. You may copy and distribute the Library (or a portion or 230 | derivative of it, under Section 2) in object code or executable form 231 | under the terms of Sections 1 and 2 above provided that you accompany 232 | it with the complete corresponding machine-readable source code, which 233 | must be distributed under the terms of Sections 1 and 2 above on a 234 | medium customarily used for software interchange. 235 | 236 | If distribution of object code is made by offering access to copy 237 | from a designated place, then offering equivalent access to copy the 238 | source code from the same place satisfies the requirement to 239 | distribute the source code, even though third parties are not 240 | compelled to copy the source along with the object code. 241 | 242 | 5. A program that contains no derivative of any portion of the 243 | Library, but is designed to work with the Library by being compiled or 244 | linked with it, is called a "work that uses the Library". Such a 245 | work, in isolation, is not a derivative work of the Library, and 246 | therefore falls outside the scope of this License. 247 | 248 | However, linking a "work that uses the Library" with the Library 249 | creates an executable that is a derivative of the Library (because it 250 | contains portions of the Library), rather than a "work that uses the 251 | library". The executable is therefore covered by this License. 252 | Section 6 states terms for distribution of such executables. 253 | 254 | When a "work that uses the Library" uses material from a header file 255 | that is part of the Library, the object code for the work may be a 256 | derivative work of the Library even though the source code is not. 257 | Whether this is true is especially significant if the work can be 258 | linked without the Library, or if the work is itself a library. The 259 | threshold for this to be true is not precisely defined by law. 260 | 261 | If such an object file uses only numerical parameters, data 262 | structure layouts and accessors, and small macros and small inline 263 | functions (ten lines or less in length), then the use of the object 264 | file is unrestricted, regardless of whether it is legally a derivative 265 | work. (Executables containing this object code plus portions of the 266 | Library will still fall under Section 6.) 267 | 268 | Otherwise, if the work is a derivative of the Library, you may 269 | distribute the object code for the work under the terms of Section 6. 270 | Any executables containing that work also fall under Section 6, 271 | whether or not they are linked directly with the Library itself. 272 | 273 | 6. As an exception to the Sections above, you may also combine or 274 | link a "work that uses the Library" with the Library to produce a 275 | work containing portions of the Library, and distribute that work 276 | under terms of your choice, provided that the terms permit 277 | modification of the work for the customer's own use and reverse 278 | engineering for debugging such modifications. 279 | 280 | You must give prominent notice with each copy of the work that the 281 | Library is used in it and that the Library and its use are covered by 282 | this License. You must supply a copy of this License. If the work 283 | during execution displays copyright notices, you must include the 284 | copyright notice for the Library among them, as well as a reference 285 | directing the user to the copy of this License. Also, you must do one 286 | of these things: 287 | 288 | a) Accompany the work with the complete corresponding 289 | machine-readable source code for the Library including whatever 290 | changes were used in the work (which must be distributed under 291 | Sections 1 and 2 above); and, if the work is an executable linked 292 | with the Library, with the complete machine-readable "work that 293 | uses the Library", as object code and/or source code, so that the 294 | user can modify the Library and then relink to produce a modified 295 | executable containing the modified Library. (It is understood 296 | that the user who changes the contents of definitions files in the 297 | Library will not necessarily be able to recompile the application 298 | to use the modified definitions.) 299 | 300 | b) Use a suitable shared library mechanism for linking with the 301 | Library. A suitable mechanism is one that (1) uses at run time a 302 | copy of the library already present on the user's computer system, 303 | rather than copying library functions into the executable, and (2) 304 | will operate properly with a modified version of the library, if 305 | the user installs one, as long as the modified version is 306 | interface-compatible with the version that the work was made with. 307 | 308 | c) Accompany the work with a written offer, valid for at least 309 | three years, to give the same user the materials specified in 310 | Subsection 6a, above, for a charge no more than the cost of 311 | performing this distribution. 312 | 313 | d) If distribution of the work is made by offering access to copy 314 | from a designated place, offer equivalent access to copy the above 315 | specified materials from the same place. 316 | 317 | e) Verify that the user has already received a copy of these 318 | materials or that you have already sent this user a copy. 319 | 320 | For an executable, the required form of the "work that uses the 321 | Library" must include any data and utility programs needed for 322 | reproducing the executable from it. However, as a special exception, 323 | the materials to be distributed need not include anything that is 324 | normally distributed (in either source or binary form) with the major 325 | components (compiler, kernel, and so on) of the operating system on 326 | which the executable runs, unless that component itself accompanies 327 | the executable. 328 | 329 | It may happen that this requirement contradicts the license 330 | restrictions of other proprietary libraries that do not normally 331 | accompany the operating system. Such a contradiction means you cannot 332 | use both them and the Library together in an executable that you 333 | distribute. 334 | 335 | 7. You may place library facilities that are a work based on the 336 | Library side-by-side in a single library together with other library 337 | facilities not covered by this License, and distribute such a combined 338 | library, provided that the separate distribution of the work based on 339 | the Library and of the other library facilities is otherwise 340 | permitted, and provided that you do these two things: 341 | 342 | a) Accompany the combined library with a copy of the same work 343 | based on the Library, uncombined with any other library 344 | facilities. This must be distributed under the terms of the 345 | Sections above. 346 | 347 | b) Give prominent notice with the combined library of the fact 348 | that part of it is a work based on the Library, and explaining 349 | where to find the accompanying uncombined form of the same work. 350 | 351 | 8. You may not copy, modify, sublicense, link with, or distribute 352 | the Library except as expressly provided under this License. Any 353 | attempt otherwise to copy, modify, sublicense, link with, or 354 | distribute the Library is void, and will automatically terminate your 355 | rights under this License. However, parties who have received copies, 356 | or rights, from you under this License will not have their licenses 357 | terminated so long as such parties remain in full compliance. 358 | 359 | 9. You are not required to accept this License, since you have not 360 | signed it. However, nothing else grants you permission to modify or 361 | distribute the Library or its derivative works. These actions are 362 | prohibited by law if you do not accept this License. Therefore, by 363 | modifying or distributing the Library (or any work based on the 364 | Library), you indicate your acceptance of this License to do so, and 365 | all its terms and conditions for copying, distributing or modifying 366 | the Library or works based on it. 367 | 368 | 10. Each time you redistribute the Library (or any work based on the 369 | Library), the recipient automatically receives a license from the 370 | original licensor to copy, distribute, link with or modify the Library 371 | subject to these terms and conditions. You may not impose any further 372 | restrictions on the recipients' exercise of the rights granted herein. 373 | You are not responsible for enforcing compliance by third parties with 374 | this License. 375 | 376 | 11. If, as a consequence of a court judgment or allegation of patent 377 | infringement or for any other reason (not limited to patent issues), 378 | conditions are imposed on you (whether by court order, agreement or 379 | otherwise) that contradict the conditions of this License, they do not 380 | excuse you from the conditions of this License. If you cannot 381 | distribute so as to satisfy simultaneously your obligations under this 382 | License and any other pertinent obligations, then as a consequence you 383 | may not distribute the Library at all. For example, if a patent 384 | license would not permit royalty-free redistribution of the Library by 385 | all those who receive copies directly or indirectly through you, then 386 | the only way you could satisfy both it and this License would be to 387 | refrain entirely from distribution of the Library. 388 | 389 | If any portion of this section is held invalid or unenforceable under 390 | any particular circumstance, the balance of the section is intended to 391 | apply, and the section as a whole is intended to apply in other 392 | circumstances. 393 | 394 | It is not the purpose of this section to induce you to infringe any 395 | patents or other property right claims or to contest validity of any 396 | such claims; this section has the sole purpose of protecting the 397 | integrity of the free software distribution system which is 398 | implemented by public license practices. Many people have made 399 | generous contributions to the wide range of software distributed 400 | through that system in reliance on consistent application of that 401 | system; it is up to the author/donor to decide if he or she is willing 402 | to distribute software through any other system and a licensee cannot 403 | impose that choice. 404 | 405 | This section is intended to make thoroughly clear what is believed to 406 | be a consequence of the rest of this License. 407 | 408 | 12. If the distribution and/or use of the Library is restricted in 409 | certain countries either by patents or by copyrighted interfaces, the 410 | original copyright holder who places the Library under this License 411 | may add an explicit geographical distribution limitation excluding those 412 | countries, so that distribution is permitted only in or among 413 | countries not thus excluded. In such case, this License incorporates 414 | the limitation as if written in the body of this License. 415 | 416 | 13. The Free Software Foundation may publish revised and/or new 417 | versions of the Lesser General Public License from time to time. 418 | Such new versions will be similar in spirit to the present version, 419 | but may differ in detail to address new problems or concerns. 420 | 421 | Each version is given a distinguishing version number. If the Library 422 | specifies a version number of this License which applies to it and 423 | "any later version", you have the option of following the terms and 424 | conditions either of that version or of any later version published by 425 | the Free Software Foundation. If the Library does not specify a 426 | license version number, you may choose any version ever published by 427 | the Free Software Foundation. 428 | 429 | 14. If you wish to incorporate parts of the Library into other free 430 | programs whose distribution conditions are incompatible with these, 431 | write to the author to ask for permission. For software which is 432 | copyrighted by the Free Software Foundation, write to the Free 433 | Software Foundation; we sometimes make exceptions for this. Our 434 | decision will be guided by the two goals of preserving the free status 435 | of all derivatives of our free software and of promoting the sharing 436 | and reuse of software generally. 437 | 438 | NO WARRANTY 439 | 440 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 441 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 442 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 443 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 444 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 445 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 446 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 447 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 448 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 449 | 450 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 451 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 452 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 453 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 454 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 455 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 456 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 457 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 458 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 459 | DAMAGES. 460 | 461 | END OF TERMS AND CONDITIONS 462 | 463 | How to Apply These Terms to Your New Libraries 464 | 465 | If you develop a new library, and you want it to be of the greatest 466 | possible use to the public, we recommend making it free software that 467 | everyone can redistribute and change. You can do so by permitting 468 | redistribution under these terms (or, alternatively, under the terms 469 | of the ordinary General Public License). 470 | 471 | To apply these terms, attach the following notices to the library. 472 | It is safest to attach them to the start of each source file to most 473 | effectively convey the exclusion of warranty; and each file should 474 | have at least the "copyright" line and a pointer to where the full 475 | notice is found. 476 | 477 | 478 | 479 | Copyright (C) 480 | 481 | This library is free software; you can redistribute it and/or 482 | modify it under the terms of the GNU Lesser General Public 483 | License as published by the Free Software Foundation; either 484 | version 2.1 of the License, or (at your option) any later version. 485 | 486 | This library is distributed in the hope that it will be useful, 487 | but WITHOUT ANY WARRANTY; without even the implied warranty of 488 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 489 | Lesser General Public License for more details. 490 | 491 | You should have received a copy of the GNU Lesser General Public 492 | License along with this library; if not, write to the Free Software 493 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 494 | 495 | Also add information on how to contact you by electronic and paper mail. 496 | 497 | You should also get your employer (if you work as a programmer) or 498 | your school, if any, to sign a "copyright disclaimer" for the library, 499 | if necessary. Here is a sample; alter the names: 500 | 501 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 502 | library `Frob' (a library for tweaking knobs) written by James 503 | Random Hacker. 504 | 505 | , 1 April 1990 506 | Ty Coon, President of Vice 507 | 508 | That's all there is to it! 509 | 510 | 511 | -------------------------------------------------------------------------------- /src/common/droid-util-audio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017-2022 Jolla Ltd. 3 | * 4 | * Contact: Juho Hämäläinen 5 | * 6 | * These PulseAudio Modules are free software; you can redistribute 7 | * it and/or modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation 9 | * version 2.1 of the License. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 | * USA. 20 | */ 21 | 22 | #ifndef _DROID_UTIL_AUDIO_H_ 23 | #define _DROID_UTIL_AUDIO_H_ 24 | 25 | #include 26 | #ifdef QCOM_BSP 27 | #define QCOM_HARDWARE 28 | #endif 29 | 30 | #include 31 | 32 | #include 33 | 34 | 35 | #ifdef STRING_ENTRY 36 | #error Macro clashing with our helper macro already defined somewhere, fix this droid lib. 37 | #endif 38 | 39 | struct string_conversion { 40 | uint32_t value; 41 | const char *str; 42 | }; 43 | 44 | #define STRING_ENTRY(str) { str, #str } 45 | 46 | // PulseAudio value - Android value 47 | 48 | uint32_t conversion_table_output_channel[][2] = { 49 | { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_OUT_MONO }, 50 | { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_OUT_FRONT_LEFT }, 51 | { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_OUT_FRONT_RIGHT }, 52 | { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_OUT_FRONT_CENTER }, 53 | { PA_CHANNEL_POSITION_SUBWOOFER, AUDIO_CHANNEL_OUT_LOW_FREQUENCY }, 54 | { PA_CHANNEL_POSITION_REAR_LEFT, AUDIO_CHANNEL_OUT_BACK_LEFT }, 55 | { PA_CHANNEL_POSITION_REAR_RIGHT, AUDIO_CHANNEL_OUT_BACK_RIGHT }, 56 | { PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER }, 57 | { PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER }, 58 | { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_OUT_BACK_CENTER }, 59 | { PA_CHANNEL_POSITION_SIDE_LEFT, AUDIO_CHANNEL_OUT_SIDE_LEFT }, 60 | { PA_CHANNEL_POSITION_SIDE_RIGHT, AUDIO_CHANNEL_OUT_SIDE_RIGHT }, 61 | { PA_CHANNEL_POSITION_TOP_CENTER, AUDIO_CHANNEL_OUT_TOP_CENTER }, 62 | { PA_CHANNEL_POSITION_TOP_FRONT_LEFT, AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT }, 63 | { PA_CHANNEL_POSITION_TOP_FRONT_CENTER, AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER }, 64 | { PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT }, 65 | { PA_CHANNEL_POSITION_TOP_REAR_LEFT, AUDIO_CHANNEL_OUT_TOP_BACK_LEFT }, 66 | { PA_CHANNEL_POSITION_TOP_REAR_CENTER, AUDIO_CHANNEL_OUT_TOP_BACK_CENTER }, 67 | { PA_CHANNEL_POSITION_TOP_REAR_RIGHT, AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT } 68 | }; 69 | 70 | uint32_t conversion_table_input_channel[][2] = { 71 | { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_MONO }, 72 | { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_IN_LEFT }, 73 | { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_IN_RIGHT }, 74 | { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_IN_FRONT }, 75 | { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_IN_BACK }, 76 | /* Following are missing suitable counterparts on PulseAudio side. */ 77 | { PA_CHANNEL_POSITION_FRONT_LEFT, AUDIO_CHANNEL_IN_LEFT_PROCESSED }, 78 | { PA_CHANNEL_POSITION_FRONT_RIGHT, AUDIO_CHANNEL_IN_RIGHT_PROCESSED }, 79 | { PA_CHANNEL_POSITION_FRONT_CENTER, AUDIO_CHANNEL_IN_FRONT_PROCESSED }, 80 | { PA_CHANNEL_POSITION_REAR_CENTER, AUDIO_CHANNEL_IN_BACK_PROCESSED }, 81 | { PA_CHANNEL_POSITION_SUBWOOFER, AUDIO_CHANNEL_IN_PRESSURE }, 82 | { PA_CHANNEL_POSITION_AUX0, AUDIO_CHANNEL_IN_X_AXIS }, 83 | { PA_CHANNEL_POSITION_AUX1, AUDIO_CHANNEL_IN_Y_AXIS }, 84 | { PA_CHANNEL_POSITION_AUX2, AUDIO_CHANNEL_IN_Z_AXIS }, 85 | { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_VOICE_UPLINK }, 86 | { PA_CHANNEL_POSITION_MONO, AUDIO_CHANNEL_IN_VOICE_DNLINK } 87 | }; 88 | 89 | uint32_t conversion_table_format[][2] = { 90 | { PA_SAMPLE_U8, AUDIO_FORMAT_PCM_8_BIT }, 91 | { PA_SAMPLE_S16LE, AUDIO_FORMAT_PCM_16_BIT }, 92 | { PA_SAMPLE_S24_32LE, AUDIO_FORMAT_PCM_8_24_BIT }, 93 | { PA_SAMPLE_S24LE, AUDIO_FORMAT_PCM_24_BIT_PACKED }, 94 | { PA_SAMPLE_S32LE, AUDIO_FORMAT_PCM_32_BIT } 95 | }; 96 | 97 | uint32_t conversion_table_default_audio_source[][2] = { 98 | { AUDIO_DEVICE_IN_COMMUNICATION, AUDIO_SOURCE_MIC }, 99 | { AUDIO_DEVICE_IN_AMBIENT, AUDIO_SOURCE_MIC }, 100 | { AUDIO_DEVICE_IN_BUILTIN_MIC, AUDIO_SOURCE_MIC }, 101 | { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, AUDIO_SOURCE_MIC }, 102 | { AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_SOURCE_MIC }, 103 | { AUDIO_DEVICE_IN_AUX_DIGITAL, AUDIO_SOURCE_MIC }, 104 | { AUDIO_DEVICE_IN_VOICE_CALL, AUDIO_SOURCE_VOICE_CALL }, 105 | { AUDIO_DEVICE_IN_TELEPHONY_RX, AUDIO_SOURCE_VOICE_CALL }, 106 | { AUDIO_DEVICE_IN_BACK_MIC, AUDIO_SOURCE_MIC }, 107 | { AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_SOURCE_REMOTE_SUBMIX }, 108 | { AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET, AUDIO_SOURCE_MIC }, 109 | { AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET, AUDIO_SOURCE_MIC }, 110 | { AUDIO_DEVICE_IN_USB_ACCESSORY, AUDIO_SOURCE_MIC }, 111 | { AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_SOURCE_MIC }, 112 | { AUDIO_DEVICE_IN_FM_TUNER, AUDIO_SOURCE_FM_TUNER }, 113 | { AUDIO_DEVICE_IN_TV_TUNER, AUDIO_SOURCE_MIC }, 114 | { AUDIO_DEVICE_IN_LINE, AUDIO_SOURCE_MIC }, 115 | { AUDIO_DEVICE_IN_SPDIF, AUDIO_SOURCE_MIC }, 116 | { AUDIO_DEVICE_IN_BLUETOOTH_A2DP, AUDIO_SOURCE_MIC }, 117 | { AUDIO_DEVICE_IN_LOOPBACK, AUDIO_SOURCE_MIC }, 118 | { AUDIO_DEVICE_IN_IP, AUDIO_SOURCE_MIC }, 119 | { AUDIO_DEVICE_IN_BUS, AUDIO_SOURCE_MIC }, 120 | { AUDIO_DEVICE_IN_PROXY, AUDIO_SOURCE_MIC }, 121 | { AUDIO_DEVICE_IN_USB_HEADSET, AUDIO_SOURCE_MIC }, 122 | { AUDIO_DEVICE_IN_BLUETOOTH_BLE, AUDIO_SOURCE_MIC }, 123 | { AUDIO_DEVICE_IN_HDMI_ARC, AUDIO_SOURCE_MIC }, 124 | { AUDIO_DEVICE_IN_ECHO_REFERENCE, AUDIO_SOURCE_MIC }, 125 | 126 | #if defined(HAVE_ENUM_AUDIO_DEVICE_IN_FM_RX) && defined(HAVE_ENUM_AUDIO_SOURCE_FM_RX) 127 | { AUDIO_DEVICE_IN_FM_RX, AUDIO_SOURCE_FM_RX }, 128 | #endif 129 | #if defined(HAVE_ENUM_AUDIO_DEVICE_IN_FM_RX_A2DP) && defined(HAVE_ENUM_AUDIO_SOURCE_FM_RX_A2DP) 130 | { AUDIO_DEVICE_IN_FM_RX_A2DP, AUDIO_SOURCE_FM_RX_A2DP }, 131 | #endif 132 | }; 133 | 134 | /* Output devices */ 135 | struct string_conversion string_conversion_table_output_device[] = { 136 | /* Each device listed here needs fancy name counterpart 137 | * in string_conversion_table_output_device_fancy. */ 138 | STRING_ENTRY( AUDIO_DEVICE_OUT_EARPIECE ), 139 | STRING_ENTRY( AUDIO_DEVICE_OUT_SPEAKER ), 140 | STRING_ENTRY( AUDIO_DEVICE_OUT_WIRED_HEADSET ), 141 | STRING_ENTRY( AUDIO_DEVICE_OUT_WIRED_HEADPHONE ), 142 | STRING_ENTRY( AUDIO_DEVICE_OUT_BLUETOOTH_SCO ), 143 | STRING_ENTRY( AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET ), 144 | STRING_ENTRY( AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT ), 145 | STRING_ENTRY( AUDIO_DEVICE_OUT_BLUETOOTH_A2DP ), 146 | STRING_ENTRY( AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES ), 147 | STRING_ENTRY( AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER ), 148 | STRING_ENTRY( AUDIO_DEVICE_OUT_AUX_DIGITAL ), 149 | STRING_ENTRY( AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET ), 150 | STRING_ENTRY( AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET ), 151 | STRING_ENTRY( AUDIO_DEVICE_OUT_USB_ACCESSORY ), 152 | STRING_ENTRY( AUDIO_DEVICE_OUT_USB_DEVICE ), 153 | STRING_ENTRY( AUDIO_DEVICE_OUT_REMOTE_SUBMIX ), 154 | STRING_ENTRY( AUDIO_DEVICE_OUT_TELEPHONY_TX ), 155 | STRING_ENTRY( AUDIO_DEVICE_OUT_LINE ), 156 | STRING_ENTRY( AUDIO_DEVICE_OUT_HDMI_ARC ), 157 | STRING_ENTRY( AUDIO_DEVICE_OUT_SPDIF ), 158 | STRING_ENTRY( AUDIO_DEVICE_OUT_FM ), 159 | STRING_ENTRY( AUDIO_DEVICE_OUT_AUX_LINE ), 160 | STRING_ENTRY( AUDIO_DEVICE_OUT_SPEAKER_SAFE ), 161 | STRING_ENTRY( AUDIO_DEVICE_OUT_IP ), 162 | STRING_ENTRY( AUDIO_DEVICE_OUT_BUS ), 163 | STRING_ENTRY( AUDIO_DEVICE_OUT_PROXY ), 164 | STRING_ENTRY( AUDIO_DEVICE_OUT_USB_HEADSET ), 165 | STRING_ENTRY( AUDIO_DEVICE_OUT_HEARING_AID ), 166 | STRING_ENTRY( AUDIO_DEVICE_OUT_ECHO_CANCELLER ), 167 | STRING_ENTRY( AUDIO_DEVICE_OUT_DEFAULT ), 168 | 169 | { 0, NULL } 170 | }; 171 | 172 | struct string_conversion string_conversion_table_audio_mode_fancy[] = { 173 | { AUDIO_MODE_NORMAL, "normal" }, 174 | { AUDIO_MODE_RINGTONE, "ringtone" }, 175 | { AUDIO_MODE_IN_CALL, "in call" }, 176 | { AUDIO_MODE_IN_COMMUNICATION, "in communication" }, 177 | { AUDIO_MODE_CALL_SCREEN, "call screen" }, 178 | 179 | { 0, NULL } 180 | }; 181 | 182 | struct string_conversion string_conversion_table_output_device_fancy[] = { 183 | { AUDIO_DEVICE_OUT_EARPIECE, "output-earpiece" }, 184 | { AUDIO_DEVICE_OUT_SPEAKER, "output-speaker" }, 185 | { AUDIO_DEVICE_OUT_WIRED_HEADSET, "output-wired_headset" }, 186 | { AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "output-wired_headphone" }, 187 | { AUDIO_DEVICE_OUT_BLUETOOTH_SCO, "output-bluetooth_sco" }, 188 | { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, "output-sco_headset" }, 189 | { AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, "output-sco_carkit" }, 190 | { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, "output-a2dp" }, 191 | { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, "output-a2dp_headphones" }, 192 | { AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, "output-a2dp_speaker" }, 193 | { AUDIO_DEVICE_OUT_AUX_DIGITAL, "output-aux_digital" }, 194 | { AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, "output-analog_dock_headset" }, 195 | { AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, "output-digital_dock_headset" }, 196 | { AUDIO_DEVICE_OUT_USB_ACCESSORY, "output-usb_accessory" }, 197 | { AUDIO_DEVICE_OUT_USB_DEVICE, "output-usb_device" }, 198 | { AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "output-remote_submix" }, 199 | { AUDIO_DEVICE_OUT_TELEPHONY_TX, "output-telephony_tx" }, 200 | { AUDIO_DEVICE_OUT_LINE, "output-line" }, 201 | { AUDIO_DEVICE_OUT_HDMI_ARC, "output-hdmi_arc" }, 202 | { AUDIO_DEVICE_OUT_SPDIF, "output-spdif" }, 203 | { AUDIO_DEVICE_OUT_FM, "output-fm" }, 204 | { AUDIO_DEVICE_OUT_AUX_LINE, "output-aux_line" }, 205 | { AUDIO_DEVICE_OUT_SPEAKER_SAFE, "output-speaker_safe" }, 206 | { AUDIO_DEVICE_OUT_IP, "output-ip" }, 207 | { AUDIO_DEVICE_OUT_BUS, "output-bus" }, 208 | { AUDIO_DEVICE_OUT_PROXY, "output-proxy" }, 209 | { AUDIO_DEVICE_OUT_USB_HEADSET, "output-usb_headset" }, 210 | { AUDIO_DEVICE_OUT_HEARING_AID, "output-hearing_aid" }, 211 | { AUDIO_DEVICE_OUT_ECHO_CANCELLER, "output-echo_canceller" }, 212 | { AUDIO_DEVICE_OUT_DEFAULT, "output-default" }, 213 | 214 | { 0, NULL } 215 | }; 216 | 217 | /* Input devices */ 218 | struct string_conversion string_conversion_table_input_device[] = { 219 | /* Each device listed here needs fancy name counterpart 220 | * in string_conversion_table_input_device_fancy. */ 221 | STRING_ENTRY( AUDIO_DEVICE_IN_COMMUNICATION ), 222 | STRING_ENTRY( AUDIO_DEVICE_IN_AMBIENT ), 223 | STRING_ENTRY( AUDIO_DEVICE_IN_BUILTIN_MIC ), 224 | STRING_ENTRY( AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET ), 225 | STRING_ENTRY( AUDIO_DEVICE_IN_WIRED_HEADSET ), 226 | STRING_ENTRY( AUDIO_DEVICE_IN_AUX_DIGITAL ), 227 | STRING_ENTRY( AUDIO_DEVICE_IN_HDMI ), /* Same as AUDIO_DEVICE_IN_AUX_DIGITAL */ 228 | STRING_ENTRY( AUDIO_DEVICE_IN_VOICE_CALL ), 229 | STRING_ENTRY( AUDIO_DEVICE_IN_TELEPHONY_RX ), /* Same as AUDIO_DEVICE_IN_VOICE_CALL */ 230 | STRING_ENTRY( AUDIO_DEVICE_IN_BACK_MIC ), 231 | STRING_ENTRY( AUDIO_DEVICE_IN_REMOTE_SUBMIX ), 232 | STRING_ENTRY( AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET ), 233 | STRING_ENTRY( AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET ), 234 | STRING_ENTRY( AUDIO_DEVICE_IN_USB_ACCESSORY ), 235 | STRING_ENTRY( AUDIO_DEVICE_IN_USB_DEVICE ), 236 | STRING_ENTRY( AUDIO_DEVICE_IN_FM_TUNER ), 237 | STRING_ENTRY( AUDIO_DEVICE_IN_TV_TUNER ), 238 | STRING_ENTRY( AUDIO_DEVICE_IN_LINE ), 239 | STRING_ENTRY( AUDIO_DEVICE_IN_SPDIF ), 240 | STRING_ENTRY( AUDIO_DEVICE_IN_BLUETOOTH_A2DP ), 241 | STRING_ENTRY( AUDIO_DEVICE_IN_LOOPBACK ), 242 | STRING_ENTRY( AUDIO_DEVICE_IN_IP ), 243 | STRING_ENTRY( AUDIO_DEVICE_IN_BUS ), 244 | STRING_ENTRY( AUDIO_DEVICE_IN_PROXY ), 245 | STRING_ENTRY( AUDIO_DEVICE_IN_USB_HEADSET ), 246 | STRING_ENTRY( AUDIO_DEVICE_IN_BLUETOOTH_BLE ), 247 | STRING_ENTRY( AUDIO_DEVICE_IN_HDMI_ARC ), 248 | STRING_ENTRY( AUDIO_DEVICE_IN_ECHO_REFERENCE ), 249 | STRING_ENTRY( AUDIO_DEVICE_IN_DEFAULT ), 250 | 251 | /* Devices which may or may not be defined for all devices. */ 252 | STRING_ENTRY_IF_AUDIO_DEVICE_IN_FM_RX 253 | STRING_ENTRY_IF_AUDIO_DEVICE_IN_FM_RX_A2DP 254 | 255 | { 0, NULL } 256 | }; 257 | 258 | struct string_conversion string_conversion_table_input_device_fancy[] = { 259 | { AUDIO_DEVICE_IN_COMMUNICATION, "input-communication" }, 260 | { AUDIO_DEVICE_IN_AMBIENT, "input-ambient" }, 261 | { AUDIO_DEVICE_IN_BUILTIN_MIC, "input-builtin_mic" }, 262 | { AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, "input-bluetooth_sco_headset" }, 263 | { AUDIO_DEVICE_IN_WIRED_HEADSET, "input-wired_headset" }, 264 | { AUDIO_DEVICE_IN_AUX_DIGITAL, "input-aux_digital" }, 265 | { AUDIO_DEVICE_IN_VOICE_CALL, "input-voice_call" }, 266 | { AUDIO_DEVICE_IN_TELEPHONY_RX, "input-telephony_rx", }, 267 | { AUDIO_DEVICE_IN_BACK_MIC, "input-back_mic" }, 268 | { AUDIO_DEVICE_IN_REMOTE_SUBMIX, "input-remote_submix" }, 269 | { AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET, "input-analog_dock_headset" }, 270 | { AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET, "input-digital_dock_headset" }, 271 | { AUDIO_DEVICE_IN_USB_ACCESSORY, "input-usb_accessory" }, 272 | { AUDIO_DEVICE_IN_USB_DEVICE, "input-usb_device" }, 273 | { AUDIO_DEVICE_IN_FM_TUNER, "input-fm_tuner" }, 274 | { AUDIO_DEVICE_IN_TV_TUNER, "input-tv_tuner" }, 275 | { AUDIO_DEVICE_IN_LINE, "input-line" }, 276 | { AUDIO_DEVICE_IN_SPDIF, "input-spdif" }, 277 | { AUDIO_DEVICE_IN_BLUETOOTH_A2DP, "input-bluetooth_a2dp" }, 278 | { AUDIO_DEVICE_IN_LOOPBACK, "input-loopback" }, 279 | { AUDIO_DEVICE_IN_IP, "input-ip" }, 280 | { AUDIO_DEVICE_IN_BUS, "input-bus" }, 281 | { AUDIO_DEVICE_IN_PROXY, "input-proxy" }, 282 | { AUDIO_DEVICE_IN_USB_HEADSET, "input-usb_headset" }, 283 | { AUDIO_DEVICE_IN_BLUETOOTH_BLE, "input-bluetooth_ble" }, 284 | { AUDIO_DEVICE_IN_HDMI_ARC, "input-hdmi_arc" }, 285 | { AUDIO_DEVICE_IN_ECHO_REFERENCE, "input-echo_reference" }, 286 | { AUDIO_DEVICE_IN_DEFAULT, "input-default" }, 287 | 288 | /* Devices which may or may not be defined for all devices. */ 289 | FANCY_ENTRY_IF_AUDIO_DEVICE_IN_FM_RX ( "input-fm_rx" ) 290 | FANCY_ENTRY_IF_AUDIO_DEVICE_IN_FM_RX_A2DP ( "input-fm_rx_a2dp" ) 291 | 292 | { 0, NULL } 293 | }; 294 | 295 | /* Audio source fancy names */ 296 | struct string_conversion string_conversion_table_audio_source_fancy[] = { 297 | { AUDIO_SOURCE_DEFAULT, "default" }, 298 | { AUDIO_SOURCE_MIC, "mic" }, 299 | { AUDIO_SOURCE_VOICE_UPLINK, "voice uplink" }, 300 | { AUDIO_SOURCE_VOICE_DOWNLINK, "voice downlink" }, 301 | { AUDIO_SOURCE_VOICE_CALL, "voice call" }, 302 | { AUDIO_SOURCE_CAMCORDER, "camcorder" }, 303 | { AUDIO_SOURCE_VOICE_RECOGNITION, "voice recognition" }, 304 | { AUDIO_SOURCE_VOICE_COMMUNICATION, "voice communication" }, 305 | { AUDIO_SOURCE_REMOTE_SUBMIX, "remote submix" }, 306 | { AUDIO_SOURCE_UNPROCESSED, "unprocessed" }, 307 | { AUDIO_SOURCE_VOICE_PERFORMANCE, "voice performance" }, 308 | 309 | /* Audio sources which may or may not be defined for all devices. */ 310 | FANCY_ENTRY_IF_AUDIO_SOURCE_ECHO_REFERENCE ( "echo reference" ) 311 | FANCY_ENTRY_IF_AUDIO_SOURCE_FM_TUNER ( "fm tuner" ) 312 | FANCY_ENTRY_IF_AUDIO_SOURCE_FM_RX ( "fm rx" ) 313 | FANCY_ENTRY_IF_AUDIO_SOURCE_FM_RX_A2DP ( "fm rx a2dp" ) 314 | 315 | { (uint32_t)-1, NULL } 316 | }; 317 | 318 | /* Flags */ 319 | struct string_conversion string_conversion_table_output_flag[] = { 320 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_NONE ), 321 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_DIRECT ), 322 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_PRIMARY ), 323 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_FAST ), 324 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_DEEP_BUFFER ), 325 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD ), 326 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_NON_BLOCKING ), 327 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_HW_AV_SYNC ), 328 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_TTS ), 329 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_RAW ), 330 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_SYNC ), 331 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO ), 332 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_DIRECT_PCM ), 333 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_MMAP_NOIRQ ), 334 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_VOIP_RX ), 335 | STRING_ENTRY( AUDIO_OUTPUT_FLAG_INCALL_MUSIC ), 336 | 337 | /* Audio output flags which may or may not be defined for all devices. */ 338 | STRING_ENTRY_IF_AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH 339 | 340 | { 0, NULL } 341 | }; 342 | 343 | struct string_conversion string_conversion_table_input_flag[] = { 344 | STRING_ENTRY( AUDIO_INPUT_FLAG_NONE ), 345 | STRING_ENTRY( AUDIO_INPUT_FLAG_FAST ), 346 | STRING_ENTRY( AUDIO_INPUT_FLAG_HW_HOTWORD ), 347 | STRING_ENTRY( AUDIO_INPUT_FLAG_RAW ), 348 | STRING_ENTRY( AUDIO_INPUT_FLAG_SYNC ), 349 | STRING_ENTRY( AUDIO_INPUT_FLAG_MMAP_NOIRQ ), 350 | STRING_ENTRY( AUDIO_INPUT_FLAG_VOIP_TX ), 351 | STRING_ENTRY( AUDIO_INPUT_FLAG_HW_AV_SYNC ), 352 | STRING_ENTRY( AUDIO_INPUT_FLAG_DIRECT ), 353 | 354 | { 0, NULL } 355 | }; 356 | 357 | /* Channels */ 358 | struct string_conversion string_conversion_table_output_channels[] = { 359 | STRING_ENTRY( AUDIO_CHANNEL_OUT_FRONT_LEFT ), 360 | STRING_ENTRY( AUDIO_CHANNEL_OUT_FRONT_RIGHT ), 361 | STRING_ENTRY( AUDIO_CHANNEL_OUT_FRONT_CENTER ), 362 | STRING_ENTRY( AUDIO_CHANNEL_OUT_LOW_FREQUENCY ), 363 | STRING_ENTRY( AUDIO_CHANNEL_OUT_BACK_LEFT ), 364 | STRING_ENTRY( AUDIO_CHANNEL_OUT_BACK_RIGHT ), 365 | STRING_ENTRY( AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER ), 366 | STRING_ENTRY( AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER ), 367 | STRING_ENTRY( AUDIO_CHANNEL_OUT_BACK_CENTER ), 368 | STRING_ENTRY( AUDIO_CHANNEL_OUT_SIDE_LEFT ), 369 | STRING_ENTRY( AUDIO_CHANNEL_OUT_SIDE_RIGHT ), 370 | STRING_ENTRY( AUDIO_CHANNEL_OUT_TOP_CENTER ), 371 | STRING_ENTRY( AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT ), 372 | STRING_ENTRY( AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER ), 373 | STRING_ENTRY( AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT ), 374 | STRING_ENTRY( AUDIO_CHANNEL_OUT_TOP_BACK_LEFT ), 375 | STRING_ENTRY( AUDIO_CHANNEL_OUT_TOP_BACK_CENTER ), 376 | STRING_ENTRY( AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT ), 377 | STRING_ENTRY( AUDIO_CHANNEL_OUT_MONO ), 378 | STRING_ENTRY( AUDIO_CHANNEL_OUT_STEREO ), 379 | STRING_ENTRY( AUDIO_CHANNEL_OUT_QUAD ), 380 | 381 | { 0, NULL } 382 | }; 383 | 384 | struct string_conversion string_conversion_table_input_channels[] = { 385 | STRING_ENTRY( AUDIO_CHANNEL_IN_LEFT ), 386 | STRING_ENTRY( AUDIO_CHANNEL_IN_RIGHT ), 387 | STRING_ENTRY( AUDIO_CHANNEL_IN_FRONT ), 388 | STRING_ENTRY( AUDIO_CHANNEL_IN_BACK ), 389 | STRING_ENTRY( AUDIO_CHANNEL_IN_LEFT_PROCESSED ), 390 | STRING_ENTRY( AUDIO_CHANNEL_IN_RIGHT_PROCESSED ), 391 | STRING_ENTRY( AUDIO_CHANNEL_IN_FRONT_PROCESSED ), 392 | STRING_ENTRY( AUDIO_CHANNEL_IN_BACK_PROCESSED ), 393 | STRING_ENTRY( AUDIO_CHANNEL_IN_PRESSURE ), 394 | STRING_ENTRY( AUDIO_CHANNEL_IN_X_AXIS ), 395 | STRING_ENTRY( AUDIO_CHANNEL_IN_Y_AXIS ), 396 | STRING_ENTRY( AUDIO_CHANNEL_IN_Z_AXIS ), 397 | STRING_ENTRY( AUDIO_CHANNEL_IN_VOICE_UPLINK ), 398 | STRING_ENTRY( AUDIO_CHANNEL_IN_VOICE_DNLINK ), 399 | STRING_ENTRY( AUDIO_CHANNEL_IN_MONO ), 400 | STRING_ENTRY( AUDIO_CHANNEL_IN_STEREO ), 401 | STRING_ENTRY( AUDIO_CHANNEL_IN_FRONT_BACK ), 402 | STRING_ENTRY_IF_AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO 403 | STRING_ENTRY_IF_AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO 404 | STRING_ENTRY_IF_AUDIO_CHANNEL_IN_VOICE_CALL_MONO 405 | 406 | { 0, NULL } 407 | }; 408 | 409 | /* Formats */ 410 | struct string_conversion string_conversion_table_format[] = { 411 | /* Omit most formats as we aren't usually interested in 412 | * other than the pcm formats anyway. */ 413 | STRING_ENTRY( AUDIO_FORMAT_INVALID ), 414 | STRING_ENTRY( AUDIO_FORMAT_DEFAULT ), 415 | STRING_ENTRY( AUDIO_FORMAT_PCM ), 416 | STRING_ENTRY( AUDIO_FORMAT_AMR_NB ), 417 | STRING_ENTRY( AUDIO_FORMAT_AMR_WB ), 418 | STRING_ENTRY( AUDIO_FORMAT_FLAC ), 419 | STRING_ENTRY( AUDIO_FORMAT_MP3 ), 420 | STRING_ENTRY( AUDIO_FORMAT_OPUS ), 421 | STRING_ENTRY( AUDIO_FORMAT_SBC ), 422 | STRING_ENTRY( AUDIO_FORMAT_VORBIS ), 423 | 424 | STRING_ENTRY( AUDIO_FORMAT_PCM_16_BIT ), 425 | STRING_ENTRY( AUDIO_FORMAT_PCM_8_BIT ), 426 | STRING_ENTRY( AUDIO_FORMAT_PCM_32_BIT ), 427 | STRING_ENTRY( AUDIO_FORMAT_PCM_8_24_BIT ), 428 | STRING_ENTRY( AUDIO_FORMAT_PCM_24_BIT_PACKED ), 429 | 430 | { 0, NULL } 431 | }; 432 | 433 | #undef STRING_ENTRY 434 | #endif 435 | -------------------------------------------------------------------------------- /src/droid/module-droid-card.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2022 Jolla Ltd. 3 | * 4 | * Contact: Juho Hämäläinen 5 | * 6 | * These PulseAudio Modules are free software; you can redistribute 7 | * it and/or modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation 9 | * version 2.1 of the License. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 19 | * USA. 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | 26 | #include 27 | #include 28 | 29 | #ifdef HAVE_VALGRIND_MEMCHECK_H 30 | #include 31 | #endif 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | //#include 60 | //#include 61 | 62 | #include 63 | #include 64 | #include 65 | #include "droid-sink.h" 66 | #include "droid-source.h" 67 | 68 | PA_MODULE_AUTHOR("Juho Hämäläinen"); 69 | PA_MODULE_DESCRIPTION("Droid card"); 70 | PA_MODULE_VERSION(PACKAGE_VERSION); 71 | PA_MODULE_USAGE( 72 | "card_name= " 73 | "sink_name= " 74 | "source_name= " 75 | "namereg_fail= " 76 | "rate= " 77 | "module_id= " 78 | "voice_source_routing= " 79 | "deferred_volume= " 80 | "config= " 81 | "voice_property_key= " 82 | "voice_property_value= " 83 | "options=" 84 | ); 85 | 86 | static const char* const valid_modargs[] = { 87 | "card_name", 88 | "sink_name", 89 | "source_name", 90 | "namereg_fail", 91 | "format", 92 | "rate", 93 | "channels", 94 | "channel_map", 95 | "sink_rate", 96 | "sink_format", 97 | "sink_channel_map", 98 | "source_rate", 99 | "source_format", 100 | "source_channel_map", 101 | "module_id", 102 | "voice_source_routing", 103 | "sink_buffer", 104 | "source_buffer", 105 | "deferred_volume", 106 | "config", 107 | "voice_property_key", 108 | "voice_property_value", 109 | /* DM_OPTIONS */ 110 | NULL, 111 | }; 112 | 113 | #define DEFAULT_MODULE_ID "primary" 114 | #define VOICE_CALL_PROFILE_NAME "voicecall" 115 | #define VOICE_CALL_PROFILE_DESC "Call mode" 116 | #define VOICE_RECORD_PROFILE_NAME "voicecall-record" 117 | #define VOICE_RECORD_PROFILE_DESC "Call mode record" 118 | #define RINGTONE_PROFILE_NAME "ringtone" 119 | #define RINGTONE_PROFILE_DESC "Ringtone mode" 120 | #define COMMUNICATION_PROFILE_NAME "communication" 121 | #define COMMUNICATION_PROFILE_DESC "Communication mode" 122 | 123 | #define VENDOR_EXT_REALCALL_ON "realcall=on" 124 | #define VENDOR_EXT_REALCALL_OFF "realcall=off" 125 | 126 | struct userdata; 127 | 128 | typedef bool (*virtual_profile_event_cb)(struct userdata *u, pa_droid_profile *p, bool enabling); 129 | 130 | struct virtual_profile { 131 | bool enabled; 132 | pa_card_profile *parent; 133 | virtual_profile_event_cb event_cb; 134 | }; 135 | 136 | struct userdata { 137 | pa_core *core; 138 | pa_module *module; 139 | 140 | pa_thread *thread; 141 | pa_thread_mq thread_mq; 142 | pa_rtpoll *rtpoll; 143 | 144 | pa_droid_profile_set *profile_set; 145 | 146 | pa_droid_hw_module *hw_module; 147 | pa_droid_card_data card_data; 148 | 149 | pa_card_profile *real_profile; 150 | 151 | pa_modargs *modargs; 152 | pa_card *card; 153 | }; 154 | 155 | struct profile_data { 156 | pa_droid_profile *droid_profile; 157 | pa_card_profile *card_profile; 158 | audio_mode_t mode; 159 | bool virtual_profile; 160 | /* Variables for virtual profiles: */ 161 | struct virtual_profile virtual; 162 | }; 163 | 164 | #ifdef DROID_AUDIO_HAL_DEBUG_VSID 165 | 166 | /* From hal/voice_extn/voice_extn.c */ 167 | #define AUDIO_PARAMETER_KEY_VSID "vsid" 168 | #define AUDIO_PARAMETER_KEY_CALL_STATE "call_state" 169 | 170 | /* From hal/voice_extn/voice_extn.c */ 171 | #define VOICE2_VSID (0x10DC1000) 172 | #define VOLTE_VSID (0x10C02000) 173 | #define QCHAT_VSID (0x10803000) 174 | #define VOWLAN_VSID (0x10002000) 175 | #define VOICEMMODE1_VSID (0x11C05000) 176 | #define VOICEMMODE2_VSID (0x11DC5000) 177 | 178 | /* From hal/voice.h */ 179 | #define BASE_CALL_STATE 1 180 | #define CALL_INACTIVE (BASE_CALL_STATE) 181 | #define CALL_ACTIVE (BASE_CALL_STATE + 1) 182 | #define VOICE_VSID 0x10C01000 183 | 184 | /* For virtual profiles */ 185 | #define VOICE_SESSION_VOICE1_PROFILE_NAME "voicecall-voice1" 186 | #define VOICE_SESSION_VOICE1_PROFILE_DESC "Call mode, default to voice 1 vsid" 187 | #define VOICE_SESSION_VOICE2_PROFILE_NAME "voicecall-voice2" 188 | #define VOICE_SESSION_VOICE2_PROFILE_DESC "Call mode, default to voice 2 vsid" 189 | #define VOICE_SESSION_VOLTE_PROFILE_NAME "voicecall-volte" 190 | #define VOICE_SESSION_VOLTE_PROFILE_DESC "Call mode, default to volte vsid" 191 | #define VOICE_SESSION_QCHAT_PROFILE_NAME "voicecall-qchat" 192 | #define VOICE_SESSION_QCHAT_PROFILE_DESC "Call mode, default to qchat vsid" 193 | #define VOICE_SESSION_VOWLAN_PROFILE_NAME "voicecall-vowlan" 194 | #define VOICE_SESSION_VOWLAN_PROFILE_DESC "Call mode, default to vowlan vsid" 195 | #define VOICE_SESSION_VOICEMMODE1_PROFILE_NAME "voicecall-voicemmode1" 196 | #define VOICE_SESSION_VOICEMMODE1_PROFILE_DESC "Call mode, default to voicemmode1 vsid" 197 | #define VOICE_SESSION_VOICEMMODE2_PROFILE_NAME "voicecall-voicemmode2" 198 | #define VOICE_SESSION_VOICEMMODE2_PROFILE_DESC "Call mode, default to voicemmode2 vsid" 199 | 200 | static bool voicecall_voice1_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling); 201 | static bool voicecall_voice2_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling); 202 | static bool voicecall_volte_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling); 203 | static bool voicecall_qchat_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling); 204 | static bool voicecall_vowlan_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling); 205 | static bool voicecall_voicemmode1_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling); 206 | static bool voicecall_voicemmode2_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling); 207 | 208 | #endif /* DROID_AUDIO_HAL_DEBUG_VSID */ 209 | 210 | static void add_disabled_profile(pa_hashmap *profiles) { 211 | pa_card_profile *cp; 212 | struct profile_data *d; 213 | 214 | cp = pa_card_profile_new("off", _("Off"), sizeof(struct profile_data)); 215 | cp->available = PA_AVAILABLE_YES; 216 | 217 | d = PA_CARD_PROFILE_DATA(cp); 218 | d->droid_profile = NULL; 219 | d->card_profile = cp; 220 | 221 | pa_hashmap_put(profiles, cp->name, cp); 222 | } 223 | 224 | /* Special profile for calls */ 225 | static pa_card_profile* add_virtual_profile(struct userdata *u, const char *name, const char *description, 226 | audio_mode_t audio_mode, virtual_profile_event_cb event_cb, 227 | pa_available_t available, pa_card_profile *extension_to, 228 | pa_hashmap *profiles) { 229 | pa_droid_profile *ap; 230 | pa_card_profile *cp; 231 | struct profile_data *d; 232 | 233 | pa_assert(u); 234 | pa_assert(u->profile_set); 235 | 236 | pa_log_debug("New virtual profile: %s", name); 237 | 238 | ap = pa_xnew0(pa_droid_profile, 1); 239 | ap->profile_set = u->profile_set; 240 | ap->name = pa_xstrdup(name); 241 | ap->description = pa_xstrdup(description); 242 | ap->priority = 50; 243 | 244 | pa_hashmap_put(u->profile_set->profiles, ap->name, ap); 245 | 246 | cp = pa_card_profile_new(ap->name, ap->description, sizeof(struct profile_data)); 247 | cp->available = available; 248 | d = PA_CARD_PROFILE_DATA(cp); 249 | d->droid_profile = ap; 250 | d->card_profile = cp; 251 | d->virtual_profile = true; 252 | d->mode = audio_mode; 253 | d->virtual.event_cb = event_cb; 254 | d->virtual.parent = extension_to; 255 | 256 | pa_hashmap_put(profiles, cp->name, cp); 257 | 258 | return cp; 259 | } 260 | 261 | static void set_card_name(pa_modargs *ma, pa_card_new_data *data, const char *module_id) { 262 | const char *tmp; 263 | char *name; 264 | 265 | pa_assert(ma); 266 | pa_assert(data); 267 | pa_assert(module_id); 268 | 269 | if ((tmp = pa_modargs_get_value(ma, "card_name", NULL))) { 270 | pa_card_new_data_set_name(data, tmp); 271 | data->namereg_fail = true; 272 | return; 273 | } 274 | 275 | name = pa_sprintf_malloc("droid_card.%s", module_id); 276 | pa_card_new_data_set_name(data, name); 277 | pa_xfree(name); 278 | data->namereg_fail = false; 279 | } 280 | 281 | static bool output_enabled(struct userdata *u, pa_droid_mapping *am) { 282 | bool enabled = false; 283 | 284 | pa_assert(u); 285 | pa_assert(am); 286 | 287 | 288 | if (am->mix_port->flags & AUDIO_OUTPUT_FLAG_PRIMARY) 289 | enabled = true; 290 | 291 | else if (am->mix_port->flags & AUDIO_OUTPUT_FLAG_RAW) 292 | enabled = false; 293 | 294 | else if (pa_droid_option(u->hw_module, DM_OPTION_OUTPUT_FAST) && am->mix_port->flags & AUDIO_OUTPUT_FLAG_FAST) 295 | enabled = true; 296 | 297 | else if (pa_droid_option(u->hw_module, DM_OPTION_OUTPUT_DEEP_BUFFER) && am->mix_port->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) 298 | enabled = true; 299 | 300 | pa_log_debug("Output mix port \"%s\" %s", am->name, enabled ? "enabled" : "disabled"); 301 | 302 | return enabled; 303 | } 304 | 305 | static bool input_enabled(struct userdata *u, pa_droid_mapping *am) { 306 | bool enabled = false; 307 | 308 | pa_assert(u); 309 | pa_assert(am); 310 | 311 | /* Look for primary mix port as the one used for creating droid-source. */ 312 | if (dm_strcasestr(am->name, "primary")) 313 | enabled = true; 314 | 315 | pa_log_debug("Input mix port \"%s\" %s", am->name, enabled ? "enabled" : "disabled"); 316 | 317 | return enabled; 318 | } 319 | 320 | static uint32_t max_channels_for_mix_port(dm_config_port *mix_port, uint32_t previous_max_channels) { 321 | uint32_t max_channels = 0; 322 | dm_config_profile *profile; 323 | void *state; 324 | 325 | DM_LIST_FOREACH_DATA(profile, mix_port->profiles, state) { 326 | for (int i = 0; profile->channel_masks[i]; i++) { 327 | max_channels = audio_channel_count_from_out_mask(profile->channel_masks[i]) > max_channels 328 | ? audio_channel_count_from_out_mask(profile->channel_masks[i]) : max_channels; 329 | } 330 | } 331 | 332 | return max_channels > previous_max_channels ? max_channels : previous_max_channels; 333 | } 334 | 335 | static void add_profile(struct userdata *u, pa_hashmap *h, pa_hashmap *ports, pa_droid_profile *ap) { 336 | pa_card_profile *cp; 337 | struct profile_data *d; 338 | pa_droid_mapping *am; 339 | int max_channels; 340 | uint32_t idx; 341 | 342 | pa_assert(u); 343 | pa_assert(h); 344 | pa_assert(ports); 345 | pa_assert(ap); 346 | 347 | pa_log_debug("Card profile %s", ap->name); 348 | 349 | cp = pa_card_profile_new(ap->name, ap->description, sizeof(struct profile_data)); 350 | cp->available = PA_AVAILABLE_YES; 351 | cp->priority = ap->priority; 352 | 353 | /* Output mappings */ 354 | 355 | max_channels = 0; 356 | PA_IDXSET_FOREACH(am, ap->output_mappings, idx) { 357 | cp->n_sinks++; 358 | pa_droid_add_card_ports(cp, ports, am, u->core); 359 | max_channels = max_channels_for_mix_port(am->mix_port, max_channels); 360 | } 361 | cp->max_sink_channels = max_channels; 362 | 363 | /* Input mappings */ 364 | 365 | max_channels = 0; 366 | PA_IDXSET_FOREACH(am, ap->input_mappings, idx) { 367 | cp->n_sources++; 368 | pa_droid_add_card_ports(cp, ports, am, u->core); 369 | max_channels = max_channels_for_mix_port(am->mix_port, max_channels); 370 | } 371 | cp->max_source_channels = max_channels; 372 | 373 | d = PA_CARD_PROFILE_DATA(cp); 374 | d->droid_profile = ap; 375 | d->card_profile = cp; 376 | d->virtual_profile = false; 377 | d->mode = AUDIO_MODE_NORMAL; 378 | 379 | pa_hashmap_put(h, cp->name, cp); 380 | } 381 | 382 | static void add_profiles(struct userdata *u, pa_hashmap *h, pa_hashmap *ports) { 383 | void *state; 384 | pa_droid_profile *ap; 385 | 386 | pa_assert(u); 387 | pa_assert(h); 388 | pa_assert(ports); 389 | 390 | PA_HASHMAP_FOREACH(ap, u->profile_set->profiles, state) { 391 | add_profile(u, h, ports, ap); 392 | } 393 | } 394 | 395 | static void init_profile(struct userdata *u) { 396 | pa_droid_mapping *am; 397 | struct profile_data *d; 398 | uint32_t idx; 399 | 400 | pa_assert(u); 401 | 402 | pa_log_debug("Init profile."); 403 | 404 | d = PA_CARD_PROFILE_DATA(u->card->active_profile); 405 | 406 | if (d->droid_profile && pa_idxset_size(d->droid_profile->output_mappings) > 0) { 407 | PA_IDXSET_FOREACH(am, d->droid_profile->output_mappings, idx) { 408 | if (!output_enabled(u, am)) 409 | continue; 410 | 411 | am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card); 412 | } 413 | } 414 | 415 | if (d->droid_profile && pa_idxset_size(d->droid_profile->input_mappings) > 0) { 416 | PA_IDXSET_FOREACH(am, d->droid_profile->input_mappings, idx) { 417 | if (!input_enabled(u, am)) 418 | continue; 419 | 420 | am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, &u->card_data, am, u->card); 421 | } 422 | } 423 | } 424 | 425 | static void park_profile(pa_droid_profile *dp) { 426 | pa_droid_mapping *am; 427 | uint32_t idx; 428 | 429 | pa_assert(dp); 430 | 431 | /* Virtual profiles don't have output mappings. */ 432 | if (dp->output_mappings) { 433 | PA_IDXSET_FOREACH(am, dp->output_mappings, idx) { 434 | if (pa_droid_mapping_is_primary(am)) 435 | pa_sink_set_port(am->sink, PA_DROID_OUTPUT_PARKING, false); 436 | } 437 | }; 438 | 439 | /* Virtual profiles don't have input mappings. */ 440 | if ((am = dp->input_mapping)) { 441 | if (pa_droid_mapping_is_primary(am)) 442 | pa_source_set_port(am->source, PA_DROID_INPUT_PARKING, false); 443 | }; 444 | } 445 | 446 | static pa_droid_profile *card_get_droid_profile(pa_card_profile *cp) { 447 | struct profile_data *pd; 448 | 449 | pa_assert(cp); 450 | 451 | pd = PA_CARD_PROFILE_DATA(cp); 452 | return pd->droid_profile; 453 | } 454 | 455 | static bool voicecall_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) { 456 | pa_droid_profile *dp = NULL; 457 | pa_droid_mapping *am_output; 458 | 459 | pa_assert(u); 460 | pa_assert(p); 461 | pa_assert(u->real_profile); 462 | 463 | dp = card_get_droid_profile(u->real_profile); 464 | if (!(am_output = pa_droid_idxset_get_primary(dp->output_mappings))) { 465 | pa_log("Active profile doesn't have primary output device."); 466 | return false; 467 | } 468 | 469 | /* call mode specialities */ 470 | if (enabling) { 471 | pa_droid_sink_set_voice_control(am_output->sink, true); 472 | 473 | if (pa_droid_option(u->hw_module, DM_OPTION_REALCALL)) 474 | pa_droid_set_parameters(u->hw_module, VENDOR_EXT_REALCALL_ON); 475 | } else { 476 | pa_droid_sink_set_voice_control(am_output->sink, false); 477 | 478 | if (pa_droid_option(u->hw_module, DM_OPTION_REALCALL)) 479 | pa_droid_set_parameters(u->hw_module, VENDOR_EXT_REALCALL_OFF); 480 | } 481 | 482 | return true; 483 | } 484 | 485 | static bool in_communication_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) { 486 | pa_droid_profile *dp; 487 | 488 | pa_assert(u); 489 | pa_assert(u->real_profile); 490 | 491 | dp = card_get_droid_profile(u->real_profile); 492 | 493 | if (pa_idxset_size(dp->output_mappings) > 0) { 494 | pa_droid_mapping *am; 495 | uint32_t idx; 496 | 497 | PA_IDXSET_FOREACH(am, dp->output_mappings, idx) { 498 | 499 | if (am->mix_port->flags & AUDIO_OUTPUT_FLAG_VOIP_RX) { 500 | if (enabling && !am->sink) { 501 | pa_log_info("in communication: enable VOIP sink"); 502 | am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card); 503 | } else if (!enabling && am->sink) { 504 | /* Don't rescue sink-inputs. */ 505 | pa_log_info("in communication: disable VOIP sink"); 506 | pa_droid_sink_free(am->sink); 507 | am->sink = NULL; 508 | } 509 | break; 510 | } 511 | } 512 | } 513 | 514 | return true; 515 | } 516 | 517 | #ifdef DROID_AUDIO_HAL_DEBUG_VSID 518 | static bool voicecall_vsid(struct userdata *u, pa_droid_profile *p, uint32_t vsid, bool enabling) 519 | { 520 | char *setparam; 521 | 522 | setparam = pa_sprintf_malloc("%s=%u;%s=%d", AUDIO_PARAMETER_KEY_VSID, vsid, 523 | AUDIO_PARAMETER_KEY_CALL_STATE, 524 | enabling ? CALL_ACTIVE : CALL_INACTIVE); 525 | 526 | pa_droid_set_parameters(u->hw_module, setparam); 527 | pa_xfree(setparam); 528 | 529 | return true; 530 | } 531 | 532 | static bool voicecall_voice1_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) 533 | { 534 | return voicecall_vsid(u, p, VOICE_VSID, enabling); 535 | } 536 | 537 | static bool voicecall_voice2_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) 538 | { 539 | return voicecall_vsid(u, p, VOICE2_VSID, enabling); 540 | } 541 | 542 | static bool voicecall_volte_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) 543 | { 544 | return voicecall_vsid(u, p, VOLTE_VSID, enabling); 545 | } 546 | 547 | static bool voicecall_qchat_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) 548 | { 549 | return voicecall_vsid(u, p, QCHAT_VSID, enabling); 550 | } 551 | 552 | static bool voicecall_vowlan_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) 553 | { 554 | return voicecall_vsid(u, p, VOWLAN_VSID, enabling); 555 | } 556 | 557 | static bool voicecall_voicemmode1_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) 558 | { 559 | return voicecall_vsid(u, p, VOICEMMODE1_VSID, enabling); 560 | } 561 | 562 | static bool voicecall_voicemmode2_vsid_profile_event_cb(struct userdata *u, pa_droid_profile *p, bool enabling) 563 | { 564 | return voicecall_vsid(u, p, VOICEMMODE2_VSID, enabling); 565 | } 566 | #endif /* DROID_AUDIO_HAL_DEBUG_VSID */ 567 | 568 | static void virtual_event(struct userdata *u, struct profile_data *profile, bool enabling) { 569 | pa_assert(u); 570 | pa_assert(profile); 571 | pa_assert(profile->virtual_profile); 572 | 573 | if (profile->virtual.enabled == enabling) 574 | return; 575 | 576 | pa_log_info("Virtual profile %s changes to %s%s", profile->droid_profile->name, 577 | enabling ? "enabled" : "disabled", 578 | profile->virtual.event_cb ? " (calling event callback)" : ""); 579 | 580 | if (profile->virtual.event_cb) 581 | profile->virtual.event_cb(u, profile->droid_profile, enabling); 582 | 583 | profile->virtual.enabled = enabling; 584 | } 585 | 586 | static pa_card_profile *leave_virtual_profile(struct userdata *u, pa_card *c, 587 | struct profile_data *current, struct profile_data *next) { 588 | pa_card_profile *real = NULL; 589 | 590 | pa_assert(u); 591 | pa_assert(c); 592 | pa_assert(current); 593 | pa_assert(next); 594 | pa_assert(current->virtual_profile); 595 | 596 | pa_log_debug("Leave virtual profile %s", current->droid_profile->name); 597 | 598 | if (next->mode != current->mode) { 599 | park_profile(card_get_droid_profile(u->real_profile)); 600 | pa_droid_hw_set_mode(u->hw_module, next->mode); 601 | } 602 | 603 | virtual_event(u, current, false); 604 | 605 | /* If new profile is the same as from which we switched to 606 | * virtual profile, transfer ownership back to that profile. 607 | * Otherwise destroy sinks & sources and switch to new profile. */ 608 | if (!next->virtual_profile) { 609 | if (current->virtual.parent) { 610 | struct profile_data *pd = PA_CARD_PROFILE_DATA(current->virtual.parent); 611 | virtual_event(u, pd, false); 612 | } 613 | 614 | if (next->card_profile != u->real_profile) 615 | real = u->real_profile; 616 | u->real_profile = NULL; 617 | } 618 | 619 | pa_log_debug("Left virtual profile %s%s", current->droid_profile->name, 620 | next->virtual_profile ? "" : " for real profile"); 621 | 622 | return real; 623 | } 624 | 625 | static void enter_virtual_profile(struct userdata *u, pa_card *c, 626 | struct profile_data *current, struct profile_data *next) { 627 | pa_assert(u); 628 | pa_assert(c); 629 | pa_assert(current); 630 | pa_assert(next); 631 | pa_assert(next->virtual_profile); 632 | 633 | pa_log_debug("Enter virtual profile %s", next->droid_profile->name); 634 | 635 | /* real_profile should always be real profile. */ 636 | if (u->real_profile == NULL) { 637 | pa_assert(!current->virtual_profile); 638 | u->real_profile = current->card_profile; 639 | } 640 | 641 | if (current->virtual_profile) { 642 | if (current->card_profile != next->virtual.parent) { 643 | struct profile_data *parent = current; 644 | while (parent && parent != next && parent->card_profile != next->virtual.parent) { 645 | virtual_event(u, parent, false); 646 | parent = parent->virtual.parent ? PA_CARD_PROFILE_DATA(parent->virtual.parent) : NULL; 647 | } 648 | } 649 | } 650 | 651 | if (next->mode != current->mode) { 652 | park_profile(card_get_droid_profile(u->real_profile)); 653 | pa_droid_hw_set_mode(u->hw_module, next->mode); 654 | } 655 | 656 | if (next->virtual.parent) { 657 | if (next->virtual.parent != current->card_profile) { 658 | struct profile_data *pd = PA_CARD_PROFILE_DATA(next->virtual.parent); 659 | virtual_event(u, pd, true); 660 | } 661 | } 662 | 663 | virtual_event(u, next, true); 664 | 665 | pa_log_debug("Entered virtual profile %s", next->droid_profile->name); 666 | } 667 | 668 | static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { 669 | struct userdata *u; 670 | pa_card_profile *real_profile; 671 | pa_droid_mapping *am; 672 | struct profile_data *next, *curr; 673 | pa_queue *sink_inputs = NULL, *source_outputs = NULL; 674 | pa_sink *primary_sink = NULL; 675 | uint32_t idx; 676 | 677 | pa_assert(c); 678 | pa_assert(new_profile); 679 | pa_assert_se(u = c->userdata); 680 | 681 | if (new_profile->available != PA_AVAILABLE_YES) { 682 | pa_log("Profile %s is not available.", new_profile->name); 683 | return -1; 684 | } 685 | 686 | next = PA_CARD_PROFILE_DATA(new_profile); 687 | curr = PA_CARD_PROFILE_DATA(c->active_profile); 688 | 689 | if (next->virtual_profile) { 690 | enter_virtual_profile(u, c, curr, next); 691 | return 0; 692 | } else { 693 | if (curr->virtual_profile) { 694 | if ((real_profile = leave_virtual_profile(u, c, curr, next))) 695 | curr = PA_CARD_PROFILE_DATA(real_profile); 696 | else 697 | return 0; 698 | } 699 | 700 | /* Continue to sink-input/source-output transfer below. */ 701 | } 702 | 703 | /* If there are connected sink inputs/source outputs in old profile's sinks/sources move 704 | * them all to new sinks/sources. */ 705 | pa_log_debug("Update sinks and sources for profile %s", new_profile->name); 706 | 707 | if (curr->droid_profile && pa_idxset_size(curr->droid_profile->output_mappings) > 0) { 708 | PA_IDXSET_FOREACH(am, curr->droid_profile->output_mappings, idx) { 709 | if (!am->sink) 710 | continue; 711 | 712 | if (next->droid_profile && 713 | pa_idxset_get_by_data(next->droid_profile->output_mappings, am, NULL)) { 714 | 715 | if (pa_droid_mapping_is_primary(am)) 716 | primary_sink = am->sink; 717 | continue; 718 | } 719 | 720 | sink_inputs = pa_sink_move_all_start(am->sink, sink_inputs); 721 | pa_droid_sink_free(am->sink); 722 | am->sink = NULL; 723 | } 724 | } 725 | 726 | if (curr->droid_profile && (am = curr->droid_profile->input_mapping)) { 727 | if (am->source && next->droid_profile && next->droid_profile->input_mapping) { 728 | source_outputs = pa_source_move_all_start(am->source, source_outputs); 729 | pa_droid_source_free(am->source); 730 | am->source = NULL; 731 | } 732 | } 733 | 734 | if (next->droid_profile && pa_idxset_size(next->droid_profile->output_mappings) > 0) { 735 | PA_IDXSET_FOREACH(am, next->droid_profile->output_mappings, idx) { 736 | if (!output_enabled(u, am)) 737 | continue; 738 | 739 | if (!am->sink) 740 | am->sink = pa_droid_sink_new(u->module, u->modargs, __FILE__, &u->card_data, 0, am, u->card); 741 | 742 | if (sink_inputs && am->sink) { 743 | pa_sink_move_all_finish(am->sink, sink_inputs, false); 744 | sink_inputs = NULL; 745 | } 746 | } 747 | } 748 | 749 | if (next->droid_profile && pa_idxset_size(next->droid_profile->input_mappings) > 0) { 750 | PA_IDXSET_FOREACH(am, next->droid_profile->input_mappings, idx) { 751 | if (!input_enabled(u, am)) 752 | continue; 753 | 754 | if (!am->source) 755 | am->source = pa_droid_source_new(u->module, u->modargs, __FILE__, &u->card_data, am, u->card); 756 | 757 | if (source_outputs && am->source) { 758 | pa_source_move_all_finish(am->source, source_outputs, false); 759 | source_outputs = NULL; 760 | } 761 | } 762 | } 763 | 764 | /* if only primary sink is left after profile change and we have detached sink-inputs attach 765 | * them to primary sink. */ 766 | if (sink_inputs && primary_sink) { 767 | pa_sink_move_all_finish(primary_sink, sink_inputs, false); 768 | sink_inputs = NULL; 769 | } 770 | 771 | if (sink_inputs) 772 | pa_sink_move_all_fail(sink_inputs); 773 | 774 | if (source_outputs) 775 | pa_source_move_all_fail(source_outputs); 776 | 777 | return 0; 778 | } 779 | 780 | 781 | int pa__init(pa_module *m) { 782 | struct userdata *u = NULL; 783 | pa_modargs *ma = NULL; 784 | pa_card_new_data data; 785 | const char *module_id; 786 | bool namereg_fail = false; 787 | pa_card_profile *voicecall = NULL; 788 | 789 | pa_assert(m); 790 | 791 | pa_log_info("Create new droid-card"); 792 | 793 | if (!(ma = pa_droid_modargs_new(m->argument, valid_modargs))) { 794 | pa_log("Failed to parse module arguments."); 795 | goto fail; 796 | } 797 | 798 | u = pa_xnew0(struct userdata, 1); 799 | u->core = m->core; 800 | m->userdata = u; 801 | 802 | module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID); 803 | 804 | if (!(u->hw_module = pa_droid_hw_module_get2(u->core, ma, module_id))) 805 | goto fail; 806 | 807 | pa_droid_options_log(u->hw_module); 808 | 809 | u->card_data.module_id = pa_xstrdup(module_id); 810 | u->card_data.userdata = u; 811 | 812 | u->profile_set = pa_droid_profile_set_default_new(u->hw_module->enabled_module); 813 | 814 | pa_card_new_data_init(&data); 815 | data.driver = __FILE__; 816 | data.module = m; 817 | 818 | set_card_name(ma, &data, u->hw_module->module_id); 819 | 820 | /* We need to give pa_modargs_get_value_boolean() a pointer to a local 821 | * variable instead of using &data.namereg_fail directly, because 822 | * data.namereg_fail is a bitfield and taking the address of a bitfield 823 | * variable is impossible. */ 824 | namereg_fail = data.namereg_fail; 825 | if (pa_modargs_get_value_boolean(ma, "namereg_fail", &namereg_fail) < 0) { 826 | pa_log("Failed to parse namereg_fail argument."); 827 | pa_card_new_data_done(&data); 828 | goto fail; 829 | } 830 | data.namereg_fail = namereg_fail; 831 | 832 | add_profiles(u, data.profiles, data.ports); 833 | 834 | if (pa_hashmap_isempty(data.profiles)) { 835 | pa_log("Failed to find a working profile."); 836 | pa_card_new_data_done(&data); 837 | goto fail; 838 | } 839 | 840 | voicecall = 841 | add_virtual_profile(u, VOICE_CALL_PROFILE_NAME, VOICE_CALL_PROFILE_DESC, 842 | AUDIO_MODE_IN_CALL, voicecall_profile_event_cb, 843 | PA_AVAILABLE_YES, NULL, data.profiles); 844 | add_virtual_profile(u, VOICE_RECORD_PROFILE_NAME, VOICE_RECORD_PROFILE_DESC, 845 | AUDIO_MODE_IN_CALL, NULL, 846 | PA_AVAILABLE_YES, voicecall, data.profiles); 847 | add_virtual_profile(u, COMMUNICATION_PROFILE_NAME, COMMUNICATION_PROFILE_DESC, 848 | AUDIO_MODE_IN_COMMUNICATION, in_communication_profile_event_cb, 849 | PA_AVAILABLE_YES, NULL, data.profiles); 850 | add_virtual_profile(u, RINGTONE_PROFILE_NAME, RINGTONE_PROFILE_DESC, 851 | AUDIO_MODE_RINGTONE, NULL, 852 | PA_AVAILABLE_YES, NULL, data.profiles); 853 | #ifdef DROID_AUDIO_HAL_DEBUG_VSID 854 | add_virtual_profile(u, VOICE_SESSION_VOICE1_PROFILE_NAME, VOICE_SESSION_VOICE1_PROFILE_DESC, 855 | AUDIO_MODE_IN_CALL, voicecall_voice1_vsid_profile_event_cb, 856 | PA_AVAILABLE_YES, voicecall, data.profiles); 857 | add_virtual_profile(u, VOICE_SESSION_VOICE2_PROFILE_NAME, VOICE_SESSION_VOICE2_PROFILE_DESC, 858 | AUDIO_MODE_IN_CALL, voicecall_voice2_vsid_profile_event_cb, 859 | PA_AVAILABLE_YES, voicecall, data.profiles); 860 | add_virtual_profile(u, VOICE_SESSION_VOLTE_PROFILE_NAME, VOICE_SESSION_VOLTE_PROFILE_DESC, 861 | AUDIO_MODE_IN_CALL, voicecall_volte_vsid_profile_event_cb, 862 | PA_AVAILABLE_YES, voicecall, data.profiles); 863 | add_virtual_profile(u, VOICE_SESSION_QCHAT_PROFILE_NAME, VOICE_SESSION_QCHAT_PROFILE_DESC, 864 | AUDIO_MODE_IN_CALL, voicecall_qchat_vsid_profile_event_cb, 865 | PA_AVAILABLE_YES, voicecall, data.profiles); 866 | add_virtual_profile(u, VOICE_SESSION_VOWLAN_PROFILE_NAME, VOICE_SESSION_VOWLAN_PROFILE_DESC, 867 | AUDIO_MODE_IN_CALL, voicecall_vowlan_vsid_profile_event_cb, 868 | PA_AVAILABLE_YES, voicecall, data.profiles); 869 | add_virtual_profile(u, VOICE_SESSION_VOICEMMODE1_PROFILE_NAME, VOICE_SESSION_VOICEMMODE1_PROFILE_DESC, 870 | AUDIO_MODE_IN_CALL, voicecall_voicemmode1_vsid_profile_event_cb, 871 | PA_AVAILABLE_YES, voicecall, data.profiles); 872 | add_virtual_profile(u, VOICE_SESSION_VOICEMMODE2_PROFILE_NAME, VOICE_SESSION_VOICEMMODE2_PROFILE_DESC, 873 | AUDIO_MODE_IN_CALL, voicecall_voicemmode2_vsid_profile_event_cb, 874 | PA_AVAILABLE_YES, voicecall, data.profiles); 875 | #endif /* DROID_AUDIO_HAL_DEBUG_VSID */ 876 | 877 | add_disabled_profile(data.profiles); 878 | 879 | pa_proplist_sets(data.proplist, PROP_DROID_HW_MODULE, u->hw_module->module_id); 880 | 881 | u->card = pa_card_new(m->core, &data); 882 | pa_card_new_data_done(&data); 883 | 884 | if (!u->card) { 885 | pa_log("Couldn't create card."); 886 | goto fail; 887 | } 888 | 889 | u->card->userdata = u; 890 | u->card->set_profile = card_set_profile; 891 | 892 | u->modargs = ma; 893 | u->module = m; 894 | 895 | pa_card_choose_initial_profile(u->card); 896 | init_profile(u); 897 | pa_card_put(u->card); 898 | 899 | return 0; 900 | 901 | fail: 902 | if (ma) 903 | pa_modargs_free(ma); 904 | 905 | pa__done(m); 906 | 907 | return -1; 908 | } 909 | 910 | void pa__done(pa_module *m) { 911 | struct userdata *u; 912 | 913 | pa_assert(m); 914 | 915 | if ((u = m->userdata)) { 916 | 917 | if (u->card && u->card->sinks) 918 | pa_idxset_remove_all(u->card->sinks, (pa_free_cb_t) pa_droid_sink_free); 919 | 920 | if (u->card && u->card->sources) 921 | pa_idxset_remove_all(u->card->sources, (pa_free_cb_t) pa_droid_source_free); 922 | 923 | if (u->card) 924 | pa_card_free(u->card); 925 | 926 | if (u->modargs) 927 | pa_modargs_free(u->modargs); 928 | 929 | if (u->profile_set) 930 | pa_droid_profile_set_free(u->profile_set); 931 | 932 | if (u->card_data.module_id) 933 | pa_xfree(u->card_data.module_id); 934 | 935 | if (u->hw_module) 936 | pa_droid_hw_module_unref(u->hw_module); 937 | 938 | pa_xfree(u); 939 | } 940 | } 941 | --------------------------------------------------------------------------------