diff options
author | Seungbae Shin <seungbae.shin@samsung.com> | 2019-01-08 17:39:04 +0900 |
---|---|---|
committer | Seungbae Shin <seungbae.shin@samsung.com> | 2019-01-08 17:44:00 +0900 |
commit | 348a5c758f4bdbeb9d12f26a091c6f2527d9706e (patch) | |
tree | 33441f05543e533f5fe616b7102e3266b7930aa5 | |
parent | b06afa8f8940a551cc036a0e4bab31205fcae0bc (diff) | |
download | audio-hal-exynos9110-348a5c758f4bdbeb9d12f26a091c6f2527d9706e.tar.gz audio-hal-exynos9110-348a5c758f4bdbeb9d12f26a091c6f2527d9706e.tar.bz2 audio-hal-exynos9110-348a5c758f4bdbeb9d12f26a091c6f2527d9706e.zip |
Initial AudioHAL for Exynos9110submit/tizen/20190108.100021submit/tizen/20190108.095844accepted/tizen/unified/20190109.060053
Base import from audio-hal-wm1831-tw2 and modifiy default alsa card name for exynos9110
[Version] 0.1.0
[Issue Type] Bring-Up
Change-Id: I71ac3d01293a846715b82fca8a76e60e6db08c46
-rw-r--r-- | LICENSE.Apache-2.0 | 202 | ||||
-rw-r--r-- | Makefile.am | 21 | ||||
-rw-r--r-- | NOTICE | 3 | ||||
-rw-r--r-- | audio-hal-exynos9110.manifest | 5 | ||||
-rwxr-xr-x | autogen.sh | 10 | ||||
-rw-r--r-- | configure.ac | 60 | ||||
-rw-r--r-- | packaging/audio-hal-exynos9110.spec | 48 | ||||
-rw-r--r-- | tizen-audio-comm.c | 117 | ||||
-rw-r--r-- | tizen-audio-ctrl.c | 46 | ||||
-rw-r--r-- | tizen-audio-impl-ctrl.c | 251 | ||||
-rw-r--r-- | tizen-audio-impl-pcm.c | 1042 | ||||
-rw-r--r-- | tizen-audio-impl-ucm.c | 663 | ||||
-rw-r--r-- | tizen-audio-impl.h | 66 | ||||
-rw-r--r-- | tizen-audio-internal.h | 327 | ||||
-rw-r--r-- | tizen-audio-pcm.c | 196 | ||||
-rw-r--r-- | tizen-audio-routing.c | 594 | ||||
-rw-r--r-- | tizen-audio-stream.c | 54 | ||||
-rw-r--r-- | tizen-audio-util.c | 80 | ||||
-rw-r--r-- | tizen-audio-volume.c | 415 | ||||
-rw-r--r-- | tizen-audio.c | 90 | ||||
-rw-r--r-- | tizen-audio.h | 523 |
21 files changed, 4813 insertions, 0 deletions
diff --git a/LICENSE.Apache-2.0 b/LICENSE.Apache-2.0 new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE.Apache-2.0 @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..98494e4 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,21 @@ +lib_LTLIBRARIES = libtizen-audio.la + +libtizen_audio_la_SOURCES = tizen-audio.c \ + tizen-audio-volume.c \ + tizen-audio-routing.c \ + tizen-audio-stream.c \ + tizen-audio-pcm.c \ + tizen-audio-util.c \ + tizen-audio-comm.c \ + tizen-audio-ctrl.c \ + tizen-audio-impl-pcm.c \ + tizen-audio-impl-ucm.c \ + tizen-audio-impl-ctrl.c +libtizen_audio_la_LDFLAGS = $(AM_LDFLAGS) -disable-static -avoid-version +libtizen_audio_la_LIBADD = $(AM_LDADD) $(ASOUNDLIB_LIBS) $(VCONF_LIBS) $(DLOG_LIBS) $(INIPARSER_LIBS) $(EXPAT_LIBS) +libtizen_audio_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) $(VCONF_CFLAGS) $(DLOG_CFLAGS) $(INIPARSER_CFLAGS) $(EXPAT_CFLAGS) -DUSE_DLOG + +if USE_TINYALSA +libtizen_audio_la_LIBADD += $(TINYALSA_LIBS) +libtizen_audio_la_CFLAGS += $(TINYALSA_CFLAGS) -D__USE_TINYALSA__ +endif @@ -0,0 +1,3 @@ +Copyright (c) Samsung Electronics Co., Ltd. All rights reserved. +Except as noted, this software is licensed under Apache License, Version 2. +Please, see the LICENSE file for Apache License terms and conditions. diff --git a/audio-hal-exynos9110.manifest b/audio-hal-exynos9110.manifest new file mode 100644 index 0000000..86dbb26 --- /dev/null +++ b/audio-hal-exynos9110.manifest @@ -0,0 +1,5 @@ +<manifest> + <request> + <domain name="_" /> + </request> +</manifest> diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..8e229ef --- /dev/null +++ b/autogen.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# autogen.sh -- Autotools bootstrapping +# + +libtoolize --copy --force +aclocal && \ +autoheader && \ +autoconf && \ +automake --add-missing --copy diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..abb8e4a --- /dev/null +++ b/configure.ac @@ -0,0 +1,60 @@ +AC_PREREQ([2.67]) + +AC_INIT([audio-hal-exynos9110], [0.1]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AC_CONFIG_HEADERS([config.h]) + +AC_CONFIG_MACRO_DIR([m4]) + +# Checks for programs. +m4_pattern_allow([AM_PROG_AR]) +AM_PROG_AR +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX +AC_PROG_LIBTOOL +AC_PROG_AWK +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +PKG_PROG_PKG_CONFIG + +# Checks for libraries. + +PKG_CHECK_MODULES(ASOUNDLIB, alsa >= 1.0.24) +AC_SUBST(ASOUNDLIB_CFLAGS) +AC_SUBST(ASOUNDLIB_LIBS) + +if test $USE_TINYALSA = "1"; then +PKG_CHECK_MODULES(TINYALSA, tinyalsa) +AC_SUBST(TINYALSA_CFLAGS) +AC_SUBST(TINYALSA_LIBS) +AM_CONDITIONAL(USE_TINYALSA, true) +else +AM_CONDITIONAL(USE_TINYALSA, false) +fi + +PKG_CHECK_MODULES(VCONF, vconf) +AC_SUBST(VCONF_CFLAGS) +AC_SUBST(VCONF_LIBS) + +PKG_CHECK_MODULES(INIPARSER, iniparser) +AC_SUBST(INIPARSER_CFLAGS) +AC_SUBST(INIPARSER_LIBS) + +PKG_CHECK_MODULES(DLOG, dlog) +AC_SUBST(DLOG_CFLAGS) +AC_SUBST(DLOG_LIBS) + +# Checks for header files. + +# Checks for typedefs, structures, and compiler characteristics. + +# Checks for library functions. + + +AC_CONFIG_FILES([ \ + Makefile + ]) +AC_OUTPUT diff --git a/packaging/audio-hal-exynos9110.spec b/packaging/audio-hal-exynos9110.spec new file mode 100644 index 0000000..1f137ec --- /dev/null +++ b/packaging/audio-hal-exynos9110.spec @@ -0,0 +1,48 @@ +Name: audio-hal-exynos9110 +Summary: TIZEN Audio HAL for Exynos9110(TW3) +Version: 0.1.0 +Release: 0 +Group: System/Libraries +License: Apache-2.0 +URL: http://tizen.org +Source0: audio-hal-exynos9110-%{version}.tar.gz +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(iniparser) +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(alsa) +#BuildRequires: pkgconfig(tinyalsa) +Provides: libtizen-audio.so + +%description +TIZEN Audio HAL for Exynos9110(TW3) + +%prep +%setup -q -n %{name}-%{version} + +%build +export CFLAGS="$CFLAGS -DTIZEN_DEBUG_ENABLE -DSYSCONFDIR=\\\"%{_sysconfdir}\\\"" +export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE" +export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE" + +export USE_TINYALSA="0" + +%autogen +%configure + +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +%make_install + +%post +/sbin/ldconfig + +%postun +/sbin/ldconfig + +%files +%manifest audio-hal-exynos9110.manifest +%defattr(-,root,root,-) +%{_libdir}/libtizen-audio.so +%license LICENSE.Apache-2.0 diff --git a/tizen-audio-comm.c b/tizen-audio-comm.c new file mode 100644 index 0000000..628e8d9 --- /dev/null +++ b/tizen-audio-comm.c @@ -0,0 +1,117 @@ +/* + * audio-hal + * + * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "tizen-audio-internal.h" + +static audio_return_t __set_message_callback(audio_hal_t *ah, message_cb callback, void *user_data) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(callback, AUDIO_ERR_PARAMETER); + + ah->comm.msg_cb = callback; + ah->comm.user_data = user_data; + + AUDIO_LOG_DEBUG("message callback is set, callback(%p), user_data(%p)", ah->comm.msg_cb, ah->comm.user_data); + + return audio_ret; +} + +static audio_return_t __unset_message_callback(audio_hal_t *ah) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + ah->comm.msg_cb = NULL; + ah->comm.user_data = NULL; + + AUDIO_LOG_DEBUG("message callback is unset"); + + return audio_ret; +} + +audio_return_t _audio_comm_init(audio_hal_t *ah) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + ah->comm.msg_cb = NULL; + ah->comm.user_data = NULL; + + return audio_ret; +} + +audio_return_t _audio_comm_deinit(audio_hal_t *ah) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + ah->comm.msg_cb = NULL; + ah->comm.user_data = NULL; + + return audio_ret; +} + +audio_return_t _audio_comm_send_message(audio_hal_t *ah, const char *name, int value) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(name, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_DEBUG("send message : name(%s), value(%d)", name, value); + if (ah->comm.msg_cb) { + ah->comm.msg_cb(name, value, ah->comm.user_data); + } + + return audio_ret; +} + +audio_return_t audio_add_message_cb(void *audio_handle, message_cb callback, void *user_data) +{ + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(callback, AUDIO_ERR_PARAMETER); + + /* NOTE: Management of several callbacks could be implemented. + But we do not care of it for now.*/ + ret = __set_message_callback((audio_hal_t *)audio_handle, callback, user_data); + + return ret; +} + +audio_return_t audio_remove_message_cb(void *audio_handle, message_cb callback) +{ + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(callback, AUDIO_ERR_PARAMETER); + + ret = __unset_message_callback((audio_hal_t *)audio_handle); + + return ret; +}
\ No newline at end of file diff --git a/tizen-audio-ctrl.c b/tizen-audio-ctrl.c new file mode 100644 index 0000000..2b9bacd --- /dev/null +++ b/tizen-audio-ctrl.c @@ -0,0 +1,46 @@ +/* + * audio-hal + * + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "tizen-audio-internal.h" +#include "tizen-audio-impl.h" + +audio_return_t _audio_ctrl_init(audio_hal_t *ah) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + audio_ret = _mixer_control_init(ah); + + return audio_ret; +} + +audio_return_t _audio_ctrl_deinit(audio_hal_t *ah) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + audio_ret = _mixer_control_deinit(ah); + + return audio_ret; +} diff --git a/tizen-audio-impl-ctrl.c b/tizen-audio-impl-ctrl.c new file mode 100644 index 0000000..1c54bc4 --- /dev/null +++ b/tizen-audio-impl-ctrl.c @@ -0,0 +1,251 @@ +/* + * audio-hal + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + +#include "tizen-audio-internal.h" + +#ifdef __MIXER_PARAM_DUMP +static void __dump_mixer_param(char *dump, long *param, int size) +{ + int i, len; + + for (i = 0; i < size; i++) { + len = sprintf(dump, "%ld", *param); + if (len > 0) + dump += len; + if (i != size -1) { + *dump++ = ','; + } + + param++; + } + *dump = '\0'; +} +#endif + +audio_return_t _mixer_control_init(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + pthread_mutex_init(&(ah->mixer.mutex), NULL); + return AUDIO_RET_OK; +} + +audio_return_t _mixer_control_deinit(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + pthread_mutex_destroy(&(ah->mixer.mutex)); + return AUDIO_RET_OK; +} + +audio_return_t _mixer_control_set_param(audio_hal_t *ah, const char* ctl_name, snd_ctl_elem_value_t* param, int size) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + /* TODO. */ + return AUDIO_RET_OK; +} + +audio_return_t _mixer_control_get_value(audio_hal_t *ah, const char *ctl_name, int *val) +{ + snd_ctl_t *handle; + snd_ctl_elem_value_t *control; + snd_ctl_elem_id_t *id; + snd_ctl_elem_info_t *info; + snd_ctl_elem_type_t type; + + int ret = 0, count = 0, i = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + pthread_mutex_lock(&(ah->mixer.mutex)); + + ret = snd_ctl_open(&handle, ALSA_DEFAULT_CARD, 0); + if (ret < 0) { + AUDIO_LOG_ERROR("snd_ctl_open error, %s\n", snd_strerror(ret)); + pthread_mutex_unlock(&(ah->mixer.mutex)); + return AUDIO_ERR_IOCTL; + } + + // Get Element Info + + snd_ctl_elem_id_alloca(&id); + snd_ctl_elem_info_alloca(&info); + snd_ctl_elem_value_alloca(&control); + + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); + snd_ctl_elem_id_set_name(id, ctl_name); + + snd_ctl_elem_info_set_id(info, id); + if (snd_ctl_elem_info(handle, info) < 0) { + AUDIO_LOG_ERROR("Cannot find control element: %s\n", ctl_name); + goto close; + } + snd_ctl_elem_info_get_id(info, id); + + type = snd_ctl_elem_info_get_type(info); + count = snd_ctl_elem_info_get_count(info); + + snd_ctl_elem_value_set_id(control, id); + + if (snd_ctl_elem_read(handle, control) < 0) { + AUDIO_LOG_ERROR("snd_ctl_elem_read failed \n"); + goto close; +} + + switch (type) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + *val = snd_ctl_elem_value_get_boolean(control, i); + break; + case SND_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < count; i++) + *val = snd_ctl_elem_value_get_integer(control, i); + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < count; i++) + *val = snd_ctl_elem_value_get_enumerated(control, i); + break; + default: + AUDIO_LOG_WARN("unsupported control element type\n"); + goto close; + } + + snd_ctl_close(handle); + +#ifdef AUDIO_DEBUG + AUDIO_LOG_INFO("get mixer(%s) = %d success", ctl_name, *val); +#endif + + pthread_mutex_unlock(&(ah->mixer.mutex)); + return AUDIO_RET_OK; + +close: + AUDIO_LOG_ERROR("Error\n"); + snd_ctl_close(handle); + pthread_mutex_unlock(&(ah->mixer.mutex)); + return AUDIO_ERR_UNDEFINED; +} + +audio_return_t _mixer_control_set_value(audio_hal_t *ah, const char *ctl_name, int val) +{ + snd_ctl_t *handle; + snd_ctl_elem_value_t *control; + snd_ctl_elem_id_t *id; + snd_ctl_elem_info_t *info; + snd_ctl_elem_type_t type; + int ret = 0, count = 0, i = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ctl_name, AUDIO_ERR_PARAMETER); + + pthread_mutex_lock(&(ah->mixer.mutex)); + + ret = snd_ctl_open(&handle, ALSA_DEFAULT_CARD, 0); + if (ret < 0) { + AUDIO_LOG_ERROR("snd_ctl_open error, card: %s: %s", ALSA_DEFAULT_CARD, snd_strerror(ret)); + pthread_mutex_unlock(&(ah->mixer.mutex)); + return AUDIO_ERR_IOCTL; + } + + // Get Element Info + + snd_ctl_elem_id_alloca(&id); + snd_ctl_elem_info_alloca(&info); + snd_ctl_elem_value_alloca(&control); + + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); + snd_ctl_elem_id_set_name(id, ctl_name); + + snd_ctl_elem_info_set_id(info, id); + if (snd_ctl_elem_info(handle, info) < 0) { + AUDIO_LOG_ERROR("Cannot find control element: %s", ctl_name); + goto close; + } + snd_ctl_elem_info_get_id(info, id); + + type = snd_ctl_elem_info_get_type(info); + count = snd_ctl_elem_info_get_count(info); + + snd_ctl_elem_value_set_id(control, id); + + snd_ctl_elem_read(handle, control); + + switch (type) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + for (i = 0; i < count; i++) + snd_ctl_elem_value_set_boolean(control, i, val); + break; + case SND_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < count; i++) + snd_ctl_elem_value_set_integer(control, i, val); + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < count; i++) + snd_ctl_elem_value_set_enumerated(control, i, val); + break; + + default: + AUDIO_LOG_WARN("unsupported control element type"); + goto close; + } + + snd_ctl_elem_write(handle, control); + + snd_ctl_close(handle); + + AUDIO_LOG_INFO("set mixer(%s) = %d success", ctl_name, val); + + pthread_mutex_unlock(&(ah->mixer.mutex)); + return AUDIO_RET_OK; + +close: + AUDIO_LOG_ERROR("Error"); + snd_ctl_close(handle); + pthread_mutex_unlock(&(ah->mixer.mutex)); + return AUDIO_ERR_UNDEFINED; +} + +audio_return_t _mixer_control_set_value_string(audio_hal_t *ah, const char* ctl_name, const char* value) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ctl_name, AUDIO_ERR_PARAMETER); + + /* TODO. */ + return AUDIO_RET_OK; +} + + +audio_return_t _mixer_control_get_element(audio_hal_t *ah, const char *ctl_name, snd_hctl_elem_t **elem) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ctl_name, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(elem, AUDIO_ERR_PARAMETER); + + /* TODO. */ + return AUDIO_RET_OK; +}
\ No newline at end of file diff --git a/tizen-audio-impl-pcm.c b/tizen-audio-impl-pcm.c new file mode 100644 index 0000000..a859a1e --- /dev/null +++ b/tizen-audio-impl-pcm.c @@ -0,0 +1,1042 @@ +/* + * audio-hal + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#include "tizen-audio-internal.h" +#include "tizen-audio-impl.h" + +#ifndef __USE_TINYALSA__ +#define DEVICE_NAME_MAX 32 +#endif + +#ifdef __USE_TINYALSA__ +/* Convert pcm format from pulse to alsa */ +static const uint32_t g_format_convert_table[] = { + [AUDIO_SAMPLE_U8] = PCM_FORMAT_S8, + [AUDIO_SAMPLE_S16LE] = PCM_FORMAT_S16_LE, + [AUDIO_SAMPLE_S32LE] = PCM_FORMAT_S32_LE, + [AUDIO_SAMPLE_S24_32LE] = PCM_FORMAT_S24_LE +}; +#else /* alsa-lib */ +/* FIXME : To avoid build warning... */ +int _snd_pcm_poll_descriptor(snd_pcm_t *pcm); +/* Convert pcm format from pulse to alsa */ +static const uint32_t g_format_convert_table[] = { + [AUDIO_SAMPLE_U8] = SND_PCM_FORMAT_U8, + [AUDIO_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW, + [AUDIO_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW, + [AUDIO_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE, + [AUDIO_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE, + [AUDIO_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE, + [AUDIO_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE, + [AUDIO_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE, + [AUDIO_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE, + [AUDIO_SAMPLE_S24LE] = SND_PCM_FORMAT_S24_3LE, + [AUDIO_SAMPLE_S24BE] = SND_PCM_FORMAT_S24_3BE, + [AUDIO_SAMPLE_S24_32LE] = SND_PCM_FORMAT_S24_LE, + [AUDIO_SAMPLE_S24_32BE] = SND_PCM_FORMAT_S24_BE +}; +#endif + +static uint32_t __convert_format(audio_sample_format_t format) +{ + return g_format_convert_table[format]; +} + +/* #define DEBUG_TIMING */ + +static int __pcm_device_set_params(audio_hal_t *ah, snd_pcm_t *pcm, uint32_t samplerate, uint32_t channels) +{ + snd_pcm_hw_params_t *params = NULL; + int err = 0; + unsigned int val = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER); + + /* Skip parameter setting to null device. */ + if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL) + return AUDIO_ERR_IOCTL; + + /* Allocate a hardware parameters object. */ + snd_pcm_hw_params_alloca(¶ms); + + /* Fill it in with default values. */ + if (snd_pcm_hw_params_any(pcm, params) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err)); + goto error; + } + + /* Set the desired hardware parameters. */ + /* Interleaved mode */ + err = snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() : failed! - %s\n", snd_strerror(err)); + goto error; + } + err = snd_pcm_hw_params_set_rate(pcm, params, samplerate, 0); + if (err < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err)); + goto error; + } + err = snd_pcm_hw_params_set_channels(pcm, params, channels); + if (err < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels() : failed! - %s\n", snd_strerror(err)); + goto error; + } + err = snd_pcm_hw_params(pcm, params); + if (err < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err)); + goto error; + } + + /* Dump current param */ + snd_pcm_hw_params_get_access(params, (snd_pcm_access_t *) &val); + AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val)); + + snd_pcm_hw_params_get_format(params, (snd_pcm_format_t*)&val); + AUDIO_LOG_DEBUG("format = '%s' (%s)\n", + snd_pcm_format_name((snd_pcm_format_t)val), + snd_pcm_format_description((snd_pcm_format_t)val)); + + snd_pcm_hw_params_get_subformat(params, (snd_pcm_subformat_t *)&val); + AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n", + snd_pcm_subformat_name((snd_pcm_subformat_t)val), + snd_pcm_subformat_description((snd_pcm_subformat_t)val)); + + snd_pcm_hw_params_get_channels(params, &val); + AUDIO_LOG_DEBUG("channels = %d\n", val); + + return 0; + +error: + return -1; +} + +#ifdef __USE_TINYALSA__ +static int __parse_card_device_number(const char *card, const char *device, unsigned int *card_u, unsigned int *device_u) { + AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(card_u, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(device_u, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_DEBUG("card : %s, device : %s", card, device); + + *card_u = (unsigned int) strtol(card, NULL, 10); + *device_u = (unsigned int) strtol(device, NULL, 10); + + return 0; +} + +static struct pcm *__tinyalsa_open_device(const char *card, const char *device, audio_pcm_sample_spec_t *ss, size_t period_size, size_t period_count, uint32_t direction) +{ + struct pcm *pcm = NULL; + struct pcm_config config; + unsigned int card_u, device_u; + + AUDIO_RETURN_NULL_IF_FAIL(device); + AUDIO_RETURN_NULL_IF_FAIL(ss); + + config.channels = ss->channels; + config.rate = ss->rate; + config.period_size = period_size; + config.period_count = period_count; + config.format = ss->format; + config.start_threshold = period_size; + config.stop_threshold = 0xFFFFFFFF; + config.silence_threshold = 0; + + AUDIO_LOG_INFO("card %s, device %s, direction %d, channels %d, rate %d, format %d, period_size %d, period_count %d", + card, device, direction, ss->channels, ss->rate, ss->format, period_size, period_count); + + if (__parse_card_device_number(card, device, &card_u, &device_u) < 0) { + AUDIO_LOG_ERROR("Failed to get card device number from %s", device); + return NULL; + } + + pcm = pcm_open(card_u, device_u, (direction == AUDIO_DIRECTION_OUT) ? PCM_OUT : PCM_IN, &config); + if (!pcm || !pcm_is_ready(pcm)) { + AUDIO_LOG_ERROR("Unable to open device (%s)", pcm_get_error(pcm)); + pcm_close(pcm); + return NULL; + } + + return pcm; +} + +static int __tinyalsa_pcm_recover(struct pcm *pcm, int err) +{ + if (err > 0) + err = -err; + if (err == -EINTR) /* nothing to do, continue */ + return 0; + if (err == -EPIPE) { + AUDIO_LOG_INFO("XRUN occurred"); + err = pcm_prepare(pcm); + if (err < 0) { + AUDIO_LOG_ERROR("Could not recover from XRUN occurred, prepare failed : %d", err); + return err; + } + return 0; + } + if (err == -ESTRPIPE) { + /* tinyalsa does not support pcm resume, dont't care suspend case */ + AUDIO_LOG_ERROR("Could not recover from suspend : %d", err); + return err; + } + return err; +} +#endif + +static audio_return_t __pcm_device_open(audio_hal_t *ah, audio_pcm_devices_t *pcm_devices, const char *device, uint32_t direction, uint32_t samplerate, uint32_t channels) +{ + int err, ret = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + +#ifdef __USE_TINYALSA__ + AUDIO_LOG_WARN("need implementation for tinyAlsa"); + return AUDIO_ERR_NOT_IMPLEMENTED; +#else + AUDIO_LOG_INFO("Setup DAI PCM"); + + if (direction & AUDIO_DEVICE_DIRECTION_IN) { + /* Capture PCM */ + if ((err = snd_pcm_open((snd_pcm_t **)&pcm_devices->in, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %s", device, snd_strerror(err)); + ret = AUDIO_ERR_IOCTL; + goto error; + } + ret = __pcm_device_set_params(ah, pcm_devices->in, samplerate, channels); + if (ret != 0) { + AUDIO_LOG_ERROR("capture DAI PCM(%s, %p) setparam failure", device, pcm_devices->in); + ret = AUDIO_ERR_INTERNAL; + goto error; + } + AUDIO_LOG_INFO("capture DAI PCM(%s, %p) open/setparam success", device, pcm_devices->in); + } + + if (direction & AUDIO_DEVICE_DIRECTION_OUT) { + /* Playback PCM */ + if ((err = snd_pcm_open((snd_pcm_t **)&pcm_devices->out, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %s", device, snd_strerror(err)); + ret = AUDIO_ERR_IOCTL; + goto error; + } + ret = __pcm_device_set_params(ah, pcm_devices->out, samplerate, channels); + if (ret != 0) { + AUDIO_LOG_ERROR("playback DAI PCM(%s, %p) setparam failure", device, pcm_devices->out); + ret = AUDIO_ERR_INTERNAL; + goto error; + } + AUDIO_LOG_INFO("playback DAI PCM(%s, %p) open/setparam success", device, pcm_devices->out); + } +#endif + + return AUDIO_RET_OK; + +error: + if ((direction & AUDIO_DEVICE_DIRECTION_OUT) && pcm_devices->out) { + snd_pcm_close(pcm_devices->out); + pcm_devices->out = NULL; + } + if ((direction & AUDIO_DEVICE_DIRECTION_IN) && pcm_devices->in) { + snd_pcm_close(pcm_devices->in); + pcm_devices->in = NULL; + } + return ret; +} + +audio_return_t _voice_pcm_open_all(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + return __pcm_device_open(ah, &ah->device.voice_pcm, VOICE_PCM_DEVICE, + AUDIO_DEVICE_DIRECTION_IN | AUDIO_DEVICE_DIRECTION_OUT, 48000, 2); +} + +audio_return_t _bt_pcm_open_all(audio_hal_t *ah) +{ + uint32_t samplerate; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + samplerate = (ah->device.bt_wideband) ? 16000 : 8000; + + return __pcm_device_open(ah, &ah->device.bt_pcm, BT_PCM_DEVICE, + AUDIO_DEVICE_DIRECTION_IN | AUDIO_DEVICE_DIRECTION_OUT, samplerate, 1); +} + +bool _is_voice_pcm_opened_all(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + if (ah->device.voice_pcm.in && ah->device.voice_pcm.out) + return true; + else + return false; +} + +bool _is_bt_pcm_opened_all(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + if (ah->device.bt_pcm.in && ah->device.bt_pcm.out) + return true; + else + return false; +} + +audio_return_t _voice_pcm_close_all(audio_hal_t *ah) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("close voice pcm handles"); + + if (ah->device.voice_pcm.out) { + if ((audio_ret = _pcm_close(ah->device.voice_pcm.out))) + AUDIO_LOG_ERROR("failed to _pcm_close() for voice pcm out, ret(0x%x)", audio_ret); + else { + ah->device.voice_pcm.out = NULL; + AUDIO_LOG_INFO("voice pcm out handle close success"); + } + } + if (ah->device.voice_pcm.in) { + if ((audio_ret = _pcm_close(ah->device.voice_pcm.in))) + AUDIO_LOG_ERROR("failed to _pcm_close() for voice pcm in, ret(0x%x)", audio_ret); + else { + ah->device.voice_pcm.in = NULL; + AUDIO_LOG_INFO("voice pcm in handle close success"); + } + } + + return audio_ret; +} + +audio_return_t _bt_pcm_close_all(audio_hal_t *ah) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("close bt pcm handles"); + + if (ah->device.bt_pcm.out) { + if ((audio_ret = _pcm_close(ah->device.bt_pcm.out))) + AUDIO_LOG_ERROR("failed to _pcm_close() for bt pcm out, ret(0x%x)", audio_ret); + else { + ah->device.bt_pcm.out = NULL; + AUDIO_LOG_INFO("bt pcm out handle close success"); + } + } + if (ah->device.bt_pcm.in) { + if ((audio_ret = _pcm_close(ah->device.bt_pcm.in))) + AUDIO_LOG_ERROR("failed to _pcm_close() for bt pcm in, ret(0x%x)", audio_ret); + else { + ah->device.bt_pcm.in = NULL; + AUDIO_LOG_INFO("bt pcm in handle close success"); + } + } + + return audio_ret; +} + +void _reset_pcm_devices(audio_hal_t *ah) +{ + AUDIO_RETURN_IF_FAIL(ah); + + _voice_pcm_close_all(ah); + _bt_pcm_close_all(ah); +} + +#ifndef __USE_TINYALSA__ +static int __make_alsa_device_name(const char *card, const char *device, char device_name[]) +{ + AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(device_name, AUDIO_ERR_PARAMETER); + + snprintf(device_name, DEVICE_NAME_MAX, "hw:%s,%s", card, device); + return 0; +} +#endif + +audio_return_t _pcm_open(const char *card, const char *device, uint32_t direction, void *sample_spec, + uint32_t period_size, uint32_t periods, void **pcm_handle) +{ + int err; + + AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL((direction == AUDIO_DIRECTION_OUT) || (direction == AUDIO_DIRECTION_IN), + AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("card(%s) device(%s) direction(%u) period_size(%u) periods(%u)", + card, device, direction, period_size, periods); +#ifdef __USE_TINYALSA__ + audio_pcm_sample_spec_t *ss; + + ss = (audio_pcm_sample_spec_t *)sample_spec; + ss->format = __convert_format((audio_sample_format_t)ss->format); + + *pcm_handle = __tinyalsa_open_device(card, device, ss, (size_t)period_size, (size_t)periods, direction); + if (*pcm_handle == NULL) { + AUDIO_LOG_ERROR("Error opening PCM device"); + return AUDIO_ERR_RESOURCE; + } + + if ((err = pcm_prepare((struct pcm *)*pcm_handle)) != 0) { + AUDIO_LOG_ERROR("Error prepare PCM device : %d", err); + } + +#else /* alsa-lib */ + int mode; + audio_return_t ret; + char device_name[DEVICE_NAME_MAX]; + + __make_alsa_device_name(card, device, device_name); + mode = SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT; + + if ((err = snd_pcm_open((snd_pcm_t **)pcm_handle, device_name, (direction == AUDIO_DIRECTION_OUT) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, mode)) < 0) { + AUDIO_LOG_ERROR("Error opening PCM device %s : %s", device_name, snd_strerror(err)); + return AUDIO_ERR_RESOURCE; + } + + if ((ret = _pcm_set_params(*pcm_handle, direction, sample_spec, period_size, periods)) != AUDIO_RET_OK) { + AUDIO_LOG_ERROR("Failed to set pcm parameters : %d", ret); + return ret; + } + + AUDIO_LOG_INFO("PCM device %s", device_name); +#endif + + return AUDIO_RET_OK; +} + +audio_return_t _pcm_start(void *pcm_handle) +{ + int err; + +#ifdef __USE_TINYALSA__ + if ((err = pcm_start(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error starting PCM handle : %d", err); + return AUDIO_ERR_RESOURCE; + } +#else /* alsa-lib */ + if ((err = snd_pcm_start(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error starting PCM handle : %s", snd_strerror(err)); + return AUDIO_ERR_RESOURCE; + } +#endif + + AUDIO_LOG_INFO("PCM handle %p start", pcm_handle); + return AUDIO_RET_OK; +} + +audio_return_t _pcm_stop(void *pcm_handle) +{ + int err; + +#ifdef __USE_TINYALSA__ + if ((err = pcm_stop(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error stopping PCM handle : %d", err); + return AUDIO_ERR_RESOURCE; + } +#else /* alsa-lib */ + if ((err = snd_pcm_drop(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error stopping PCM handle : %s", snd_strerror(err)); + return AUDIO_ERR_RESOURCE; + } +#endif + + AUDIO_LOG_INFO("PCM handle %p stop", pcm_handle); + return AUDIO_RET_OK; +} + +audio_return_t _pcm_close(void *pcm_handle) +{ + int err; + + AUDIO_LOG_INFO("Try to close PCM handle %p", pcm_handle); + +#ifdef __USE_TINYALSA__ + if ((err = pcm_close(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error closing PCM handle : %d", err); + return AUDIO_ERR_RESOURCE; + } +#else /* alsa-lib */ + if ((err = snd_pcm_close(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error closing PCM handle : %s", snd_strerror(err)); + return AUDIO_ERR_RESOURCE; + } +#endif + + return AUDIO_RET_OK; +} + +audio_return_t _pcm_avail(void *pcm_handle, uint32_t *avail) +{ +#ifdef __USE_TINYALSA__ + struct timespec tspec; + unsigned int frames_avail = 0; + int err; + + err = pcm_get_htimestamp(pcm_handle, &frames_avail, &tspec); + if (err < 0) { + AUDIO_LOG_ERROR("Could not get avail and timespec at PCM handle %p : %d", pcm_handle, err); + return AUDIO_ERR_IOCTL; + } + +#ifdef DEBUG_TIMING + AUDIO_LOG_DEBUG("avail = %d", frames_avail); +#endif + + *avail = (uint32_t)frames_avail; +#else /* alsa-lib */ + snd_pcm_sframes_t frames_avail; + + if ((frames_avail = snd_pcm_avail(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Could not get avail at PCM handle %p : %ld", pcm_handle, frames_avail); + return AUDIO_ERR_IOCTL; + } + +#ifdef DEBUG_TIMING + AUDIO_LOG_DEBUG("avail = %d", frames_avail); +#endif + + *avail = (uint32_t)frames_avail; +#endif + + return AUDIO_RET_OK; +} + +audio_return_t _pcm_write(void *pcm_handle, const void *buffer, uint32_t frames) +{ +#ifdef __USE_TINYALSA__ + int err; + + err = pcm_write(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames)); + if (err < 0) { + AUDIO_LOG_ERROR("Failed to write pcm : %d", err); + return AUDIO_ERR_IOCTL; + } + +#ifdef DEBUG_TIMING + AUDIO_LOG_DEBUG("_pcm_write = %d", frames); +#endif +#else /* alsa-lib */ + snd_pcm_sframes_t frames_written; + + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + frames_written = snd_pcm_writei(pcm_handle, buffer, (snd_pcm_uframes_t) frames); + if (frames_written < 0) { + AUDIO_LOG_ERROR("Failed to write pcm : %ld", frames_written); + return AUDIO_ERR_IOCTL; + } + +#ifdef DEBUG_TIMING + AUDIO_LOG_DEBUG("_pcm_write = (%d / %d)", frames_written, frames); +#endif +#endif + + return AUDIO_RET_OK; +} + +audio_return_t _pcm_read(void *pcm_handle, void *buffer, uint32_t frames) +{ +#ifdef __USE_TINYALSA__ + int err; + + err = pcm_read(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames)); + if (err < 0) { + AUDIO_LOG_ERROR("Failed to read pcm : %d", err); + return AUDIO_ERR_IOCTL; + } + +#ifdef DEBUG_TIMING + AUDIO_LOG_DEBUG("audio_pcm_read = %d", frames); +#endif +#else /* alsa-lib */ + snd_pcm_sframes_t frames_read; + + frames_read = snd_pcm_readi(pcm_handle, buffer, (snd_pcm_uframes_t)frames); + if (frames_read < 0) { + AUDIO_LOG_ERROR("Failed to read pcm : %ld", frames_read); + return AUDIO_ERR_IOCTL; + } + +#ifdef DEBUG_TIMING + AUDIO_LOG_DEBUG("_pcm_read = (%d / %d)", frames_read, frames); +#endif +#endif + + return AUDIO_RET_OK; +} + +audio_return_t _pcm_get_fd(void *pcm_handle, int *fd) +{ + /* we use an internal API of the (tiny)alsa library, so it causes warning message during compile */ +#ifdef __USE_TINYALSA__ + *fd = _pcm_poll_descriptor((struct pcm *)pcm_handle); +#else /* alsa-lib */ + *fd = _snd_pcm_poll_descriptor((snd_pcm_t *)pcm_handle); +#endif + return AUDIO_RET_OK; +} + +audio_return_t _pcm_recover(void *pcm_handle, int revents) +{ + int state, err; + + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + if (revents & POLLERR) + AUDIO_LOG_DEBUG("Got POLLERR from ALSA"); + if (revents & POLLNVAL) + AUDIO_LOG_DEBUG("Got POLLNVAL from ALSA"); + if (revents & POLLHUP) + AUDIO_LOG_DEBUG("Got POLLHUP from ALSA"); + if (revents & POLLPRI) + AUDIO_LOG_DEBUG("Got POLLPRI from ALSA"); + if (revents & POLLIN) + AUDIO_LOG_DEBUG("Got POLLIN from ALSA"); + if (revents & POLLOUT) + AUDIO_LOG_DEBUG("Got POLLOUT from ALSA"); + +#ifdef __USE_TINYALSA__ + state = pcm_state(pcm_handle); + AUDIO_LOG_DEBUG("PCM state is %d", state); + + switch (state) { + case PCM_STATE_XRUN: + if ((err = __tinyalsa_pcm_recover(pcm_handle, -EPIPE)) != 0) { + AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err); + return AUDIO_ERR_IOCTL; + } + break; + + case PCM_STATE_SUSPENDED: + if ((err = __tinyalsa_pcm_recover(pcm_handle, -ESTRPIPE)) != 0) { + AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err); + return AUDIO_ERR_IOCTL; + } + break; + + default: + pcm_stop(pcm_handle); + if ((err = pcm_prepare(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with pcm_prepare() : %d", err); + return AUDIO_ERR_IOCTL; + } + } +#else /* alsa-lib */ + state = snd_pcm_state(pcm_handle); + AUDIO_LOG_DEBUG("PCM state is %s", snd_pcm_state_name(state)); + + /* Try to recover from this error */ + + switch (state) { + case SND_PCM_STATE_XRUN: + if ((err = snd_pcm_recover(pcm_handle, -EPIPE, 1)) != 0) { + AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err); + return AUDIO_ERR_IOCTL; + } + break; + + case SND_PCM_STATE_SUSPENDED: + if ((err = snd_pcm_recover(pcm_handle, -ESTRPIPE, 1)) != 0) { + AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err); + return AUDIO_ERR_IOCTL; + } + break; + + default: + snd_pcm_drop(pcm_handle); + if ((err = snd_pcm_prepare(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare() : %d", err); + return AUDIO_ERR_IOCTL; + } + break; + } +#endif + + AUDIO_LOG_DEBUG("_pcm_recover"); + return AUDIO_RET_OK; +} + +audio_return_t _pcm_get_params(void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods) +{ +#ifdef __USE_TINYALSA__ + audio_pcm_sample_spec_t *ss; + unsigned int _period_size, _buffer_size, _periods, _format, _rate, _channels; + unsigned int _start_threshold, _stop_threshold, _silence_threshold; + struct pcm_config *config; + + ss = (audio_pcm_sample_spec_t *)*sample_spec; + + /* we use an internal API of the tiny alsa library, so it causes warning message during compile */ + _pcm_config(pcm_handle, &config); + + *period_size = config->period_size; + *periods = config->period_count; + _buffer_size = config->period_size * config->period_count; + ss->format = config->format; + ss->rate = config->rate; + ss->channels = config->channels; + _start_threshold = config->start_threshold; + _stop_threshold = config->stop_threshold; + _silence_threshold = config->silence_threshold; + + AUDIO_LOG_DEBUG("_pcm_get_params (handle %p, format %d, rate %u, channels %u, period_size %u, periods %u, buffer_size %u)", + pcm_handle, config->format, config->rate, config->channels, config->period_size, config->period_count, _buffer_size); +#else /* alsa-lib */ + int err; + audio_pcm_sample_spec_t *ss; + int dir; + snd_pcm_uframes_t _period_size, _buffer_size; + snd_pcm_format_t _format; + unsigned int _rate, _channels; + snd_pcm_uframes_t _start_threshold, _stop_threshold, _silence_threshold, _avail_min; + unsigned int _periods; + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + + ss = (audio_pcm_sample_spec_t *)*sample_spec; + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_sw_params_alloca(&swparams); + + if ((err = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_current() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 || + (err = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0 || + (err = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0 || + (err = snd_pcm_hw_params_get_format(hwparams, &_format)) < 0 || + (err = snd_pcm_hw_params_get_rate(hwparams, &_rate, &dir)) < 0 || + (err = snd_pcm_hw_params_get_channels(hwparams, &_channels)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_get_{period_size|buffer_size|periods|format|rate|channels}() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + *period_size = _period_size; + *periods = _periods; + ss->format = _format; + ss->rate = _rate; + ss->channels = _channels; + + if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_sw_params_current() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_sw_params_get_start_threshold(swparams, &_start_threshold)) < 0 || + (err = snd_pcm_sw_params_get_stop_threshold(swparams, &_stop_threshold)) < 0 || + (err = snd_pcm_sw_params_get_silence_threshold(swparams, &_silence_threshold)) < 0 || + (err = snd_pcm_sw_params_get_avail_min(swparams, &_avail_min)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_sw_params_get_{start_threshold|stop_threshold|silence_threshold|avail_min}() failed : %d", err); + } + + AUDIO_LOG_DEBUG("_pcm_get_params (handle %p, format %d, rate %u, channels %u, period_size %lu, periods %u, buffer_size %lu)", + pcm_handle, _format, _rate, _channels, _period_size, _periods, _buffer_size); +#endif + + return AUDIO_RET_OK; +} + +audio_return_t _pcm_set_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods) +{ +#ifdef __USE_TINYALSA__ + /* Parameters are only acceptable in pcm_open() function */ + AUDIO_LOG_DEBUG("_pcm_set_params"); +#else /* alsa-lib */ + int err; + audio_pcm_sample_spec_t ss; + snd_pcm_uframes_t _buffer_size; + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + + ss = *(audio_pcm_sample_spec_t *)sample_spec; + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_sw_params_alloca(&swparams); + + /* Set hw params */ + if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_any() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate_resample() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + ss.format = __convert_format((audio_sample_format_t)ss.format); + if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, ss.format)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_format() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, ss.rate, 0)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss.channels)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", ss.channels, err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, period_size, 0)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size(%u) failed : %d", period_size, err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods(%u) failed : %d", periods, err); + return AUDIO_ERR_PARAMETER; + } + + _buffer_size = period_size * periods; + if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, _buffer_size)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size(%lu) failed : %d", _buffer_size, err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err); + return AUDIO_ERR_IOCTL; + } + + /* Set sw params */ + if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) { + AUDIO_LOG_ERROR("Unable to determine current swparams : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) { + AUDIO_LOG_ERROR("Unable to enable time stamping : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xFFFFFFFF)) < 0) { + AUDIO_LOG_ERROR("Unable to set stop threshold : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, period_size / 2)) < 0) { + AUDIO_LOG_ERROR("Unable to set start threshold : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, 1024)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err); + return AUDIO_ERR_PARAMETER; + } + + if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0) { + AUDIO_LOG_ERROR("Unable to set sw params : %d", err); + return AUDIO_ERR_IOCTL; + } + + /* Prepare device */ + if ((err = snd_pcm_prepare(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_prepare() failed : %d", err); + return AUDIO_ERR_IOCTL; + } + + AUDIO_LOG_DEBUG("_pcm_set_params (handle %p, format %d, rate %u, channels %u, period_size %u, periods %u, buffer_size %lu)", + pcm_handle, ss.format, ss.rate, ss.channels, period_size, periods, _buffer_size); +#endif + + return AUDIO_RET_OK; +} + +/* Generic snd pcm interface APIs */ +audio_return_t _pcm_set_hw_params(snd_pcm_t *pcm, audio_pcm_sample_spec_t *sample_spec, uint8_t *use_mmap, snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *buffer_size) +{ + audio_return_t ret = AUDIO_RET_OK; + snd_pcm_hw_params_t *hwparams; + int err = 0; + int dir; + unsigned int val = 0; + snd_pcm_uframes_t _period_size = period_size ? *period_size : 0; + snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0; + uint8_t _use_mmap = use_mmap && *use_mmap; + uint32_t channels = 0; + + AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER); + + snd_pcm_hw_params_alloca(&hwparams); + + /* Skip parameter setting to null device. */ + if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL) + return AUDIO_ERR_IOCTL; + + /* Allocate a hardware parameters object. */ + snd_pcm_hw_params_alloca(&hwparams); + + /* Fill it in with default values. */ + if (snd_pcm_hw_params_any(pcm, hwparams) < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err)); + goto error; + } + + /* Set the desired hardware parameters. */ + + if (_use_mmap) { + + if (snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) { + + /* mmap() didn't work, fall back to interleaved */ + + if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret)); + goto error; + } + + _use_mmap = 0; + } + + } else if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret)); + goto error; + } + AUDIO_LOG_DEBUG("setting rate - %d", sample_spec->rate); + err = snd_pcm_hw_params_set_rate(pcm, hwparams, sample_spec->rate, 0); + if (err < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err)); + } + + err = snd_pcm_hw_params(pcm, hwparams); + if (err < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err)); + goto error; + } + + /* Dump current param */ + + if ((ret = snd_pcm_hw_params_current(pcm, hwparams)) < 0) { + AUDIO_LOG_INFO("snd_pcm_hw_params_current() failed: %s", snd_strerror(ret)); + goto error; + } + + if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 || + (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) { + AUDIO_LOG_INFO("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", snd_strerror(ret)); + goto error; + } + + snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val); + AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val)); + + snd_pcm_hw_params_get_format(hwparams, &sample_spec->format); + AUDIO_LOG_DEBUG("format = '%s' (%s)\n", + snd_pcm_format_name((snd_pcm_format_t)sample_spec->format), + snd_pcm_format_description((snd_pcm_format_t)sample_spec->format)); + + snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val); + AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n", + snd_pcm_subformat_name((snd_pcm_subformat_t)val), + snd_pcm_subformat_description((snd_pcm_subformat_t)val)); + + snd_pcm_hw_params_get_channels(hwparams, &channels); + sample_spec->channels = (uint8_t)channels; + AUDIO_LOG_DEBUG("channels = %d\n", sample_spec->channels); + + if (buffer_size) + *buffer_size = _buffer_size; + + if (period_size) + *period_size = _period_size; + + if (use_mmap) + *use_mmap = _use_mmap; + + return AUDIO_RET_OK; + +error: + return AUDIO_ERR_RESOURCE; +} + +audio_return_t _pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event) +{ + snd_pcm_sw_params_t *swparams; + snd_pcm_uframes_t boundary; + int err; + + AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER); + + snd_pcm_sw_params_alloca(&swparams); + + if ((err = snd_pcm_sw_params_current(pcm, swparams)) < 0) { + AUDIO_LOG_WARN("Unable to determine current swparams: %s\n", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, period_event)) < 0) { + AUDIO_LOG_WARN("Unable to disable period event: %s\n", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) { + AUDIO_LOG_WARN("Unable to enable time stamping: %s\n", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) { + AUDIO_LOG_WARN("Unable to get boundary: %s\n", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) { + AUDIO_LOG_WARN("Unable to set stop threshold: %s\n", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) avail_min)) < 0) { + AUDIO_LOG_WARN("Unable to set start threshold: %s\n", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) { + AUDIO_LOG_WARN("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err)); + goto error; + } + if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) { + AUDIO_LOG_WARN("Unable to set sw params: %s\n", snd_strerror(err)); + goto error; + } + return AUDIO_RET_OK; +error: + return err; +} diff --git a/tizen-audio-impl-ucm.c b/tizen-audio-impl-ucm.c new file mode 100644 index 0000000..7ff7cbc --- /dev/null +++ b/tizen-audio-impl-ucm.c @@ -0,0 +1,663 @@ +/* + * audio-hal + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef ALSA_UCM_DEBUG_TIME +#include <sys/time.h> +#include <time.h> +#endif + +#include "tizen-audio-internal.h" + +#ifdef ALSA_UCM_DEBUG_TIME +#define SND_USE_CASE_SET __set_use_case_with_time +#else +#define SND_USE_CASE_SET snd_use_case_set +#endif + +#define UCM_PREFIX_CURRENT ">>> UCM current" +#define UCM_PREFIX_REQUESTED "> UCM requested" +#define UCM_PREFIX_CHANGED "<<< UCM changed" + +#define DUMP_LEN 512 + +static void __dump_use_case(const char* prefix, const char *verb, const char *devices[], int dev_count, const char *modifiers[], int mod_count) +{ + int i; + dump_data_t* dump = NULL; + + if (!(dump = _audio_dump_new(DUMP_LEN))) { + AUDIO_LOG_ERROR("Failed to create dump string..."); + return; + } + + /* Verb */ + _audio_dump_add_str(dump, "Verb [ %s ] Devices [ ", verb ? verb : AUDIO_USE_CASE_VERB_INACTIVE); + + /* Devices */ + if (devices) { + for (i = 0; i < dev_count; i++) { + _audio_dump_add_str(dump, (i != dev_count - 1) ? "%s, " : "%s", devices[i]); + } + } + _audio_dump_add_str(dump, " ] Modifier [ "); + + /* Modifiers */ + if (modifiers) { + for (i = 0; i < mod_count; i++) { + _audio_dump_add_str(dump, (i != mod_count - 1) ? "%s, " : "%s", modifiers[i]); + } + } + _audio_dump_add_str(dump, " ]"); + + AUDIO_LOG_INFO("TEST %s : %s", prefix, _audio_dump_get_str(dump)); + + _audio_dump_free(dump); +} + +#ifdef ALSA_UCM_DEBUG_TIME +static inline int __set_use_case_with_time(snd_use_case_mgr_t *uc_mgr, const char *identifier, const char *value) +{ + int ret = 0; + struct timeval t_start, t_stop; + unsigned long long t_diff = 0; + + gettimeofday(&t_start, NULL); + ret = snd_use_case_set(uc_mgr, identifier, value); + gettimeofday(&t_stop, NULL); + if (t_start.tv_sec < t_stop.tv_sec) + t_diff = (t_stop.tv_sec - t_start.tv_sec) * 1000000; + t_diff += (t_stop.tv_usec - t_start.tv_usec); + AUDIO_LOG_DEBUG("identifier %s value %s takes %lluusec", identifier, value, t_diff); + + return ret; +} +#endif + +audio_return_t _ucm_init(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + snd_use_case_mgr_open(&ah->ucm.uc_mgr, ALSA_DEFAULT_CARD); + + if (!ah->ucm.uc_mgr) { + AUDIO_LOG_ERROR("uc_mgr open failed"); + return AUDIO_ERR_RESOURCE; + } + return AUDIO_RET_OK; +} + +audio_return_t _ucm_deinit(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, AUDIO_ERR_PARAMETER); + + if (ah->ucm.uc_mgr) { + snd_use_case_mgr_close(ah->ucm.uc_mgr); + ah->ucm.uc_mgr = NULL; + } + + return AUDIO_RET_OK; +} + +/* UCM sequence + 1) If verb is null or verb is not changed + 1-1) If device is changed + (If there is request for same device, it will be ignored) + -> Set "Inactive" verb, disable modifiers & devices, set current verb again, enable devices & modifiers + (playback/capture device will be enabled again if there is no request for playback/capture device) + 1-2) If device is not changed + 1-2-1) If modifier is changed + (If there is request for same modifier, it will be ignored) + -> Disable modifiers, enable modifiers + 2) If verb is changed + -> Reset, set new verb, enable devices & modifiers + */ +audio_return_t _ucm_set_use_case(audio_hal_t *ah, const char *verb, const char *devices[], const char *modifiers[]) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + int is_verb_changed = 0, is_dev_changed = 0, is_mod_changed = 0; + const char *old_verb = NULL, **old_dev_list = NULL, **old_mod_list = NULL; + int old_dev_count = 0, dev_count = 0; + int old_mod_count = 0, mod_count = 0; + const char **dis_dev_list = NULL, **ena_dev_list = NULL; + const char **dis_mod_list = NULL, **ena_mod_list = NULL; + int dis_dev_count = 0, ena_dev_count = 0; + int dis_mod_count = 0, ena_mod_count = 0; + int i = 0, j = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(verb, AUDIO_ERR_PARAMETER); + + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &old_verb); + old_dev_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enadevs", &old_dev_list); + old_mod_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enamods", &old_mod_list); + __dump_use_case(UCM_PREFIX_CURRENT, old_verb, old_dev_list, old_dev_count, old_mod_list, old_mod_count); + + if (devices) { + for (dev_count = 0; devices[dev_count]; dev_count++); + } + if (modifiers) { + for (mod_count = 0; modifiers[mod_count]; mod_count++); + } + + __dump_use_case(UCM_PREFIX_REQUESTED, verb, devices, dev_count, modifiers, mod_count); + + if (old_verb && streq(verb, old_verb)) { + AUDIO_LOG_DEBUG("current verb and new verb is same. No need to change verb, disable devices explicitely"); + + if (old_dev_count > 0) { + dis_dev_list = (const char **)malloc(sizeof(const char *) * old_dev_count); + for (i = 0; i < old_dev_count; i++) { + dis_dev_list[i] = NULL; + } + } + if (dev_count > 0) { + ena_dev_list = (const char **)malloc(sizeof(const char *) * dev_count); + for (i = 0; i < dev_count; i++) { + ena_dev_list[i] = NULL; + } + } + if (old_mod_count > 0) { + dis_mod_list = (const char **)malloc(sizeof(const char *) * old_mod_count); + for (i = 0; i < old_mod_count; i++) { + dis_mod_list[i] = NULL; + } + } + if (mod_count > 0) { + ena_mod_list = (const char **)malloc(sizeof(const char *) * mod_count); + for (i = 0; i < mod_count; i++) { + ena_mod_list[i] = NULL; + } + } + + /* update disable modifiers list which are not present in new modifier list */ + for (i = 0; i < old_mod_count; i++) { + int need_disable_mod = 1; + + for (j = 0; j < mod_count; j++) { + if (streq(old_mod_list[i], modifiers[j])) { + need_disable_mod = 0; + break; + } + } + if (need_disable_mod) { + if (is_mod_changed == 0) + is_mod_changed = 1; + dis_mod_list[dis_mod_count++] = old_mod_list[i]; + } + } + + /* update disable devices list which are not present in new device list */ + for (i = 0; i < old_dev_count; i++) { + int need_disable_dev = 1; + + for (j = 0; j < dev_count; j++) { + if (streq(old_dev_list[i], devices[j])) { + need_disable_dev = 0; + break; + } + } + if (need_disable_dev) { + if (is_dev_changed == 0) + is_dev_changed = 1; + dis_dev_list[dis_dev_count++] = old_dev_list[i]; + } + } + + /* update enable devices list which are not present in old device list */ + for (i = 0; i < dev_count; i++) { + int need_enable_dev = 1; + + for (j = 0; j < old_dev_count; j++) { + if (streq(devices[i], old_dev_list[j])) { + need_enable_dev = 0; + break; + } + } + if (need_enable_dev) { + if (is_dev_changed == 0) + is_dev_changed = 1; + ena_dev_list[ena_dev_count++] = devices[i]; + } + } + + /* update enable modifiers list which are not present in old modifier list */ + for (i = 0; i < mod_count; i++) { + int need_enable_mod = 1; + + for (j = 0; j < old_mod_count; j++) { + if (streq(modifiers[i], old_mod_list[j])) { + need_enable_mod = 0; + break; + } + } + if (need_enable_mod) { + if (is_mod_changed == 0) + is_mod_changed = 1; + ena_mod_list[ena_mod_count++] = modifiers[i]; + } + } + + /* disable modifiers */ + for (i = 0; i < dis_mod_count; i++) { + AUDIO_LOG_INFO("Disable modifier : %s", dis_mod_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_dismod", dis_mod_list[i]) < 0) + AUDIO_LOG_ERROR("disable %s modifier failed", dis_mod_list[i]); + } + + /* disable devices */ + for (i = 0; i < dis_dev_count; i++) { + AUDIO_LOG_INFO("Disable device : %s", dis_dev_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_disdev", dis_dev_list[i]) < 0) + AUDIO_LOG_ERROR("disable %s device failed", dis_dev_list[i]); + } + + /* enable devices */ + for (i = 0; i < ena_dev_count; i++) { + AUDIO_LOG_INFO("Enable device : %s", ena_dev_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enadev", ena_dev_list[i]) < 0) + AUDIO_LOG_ERROR("enable %s device failed", ena_dev_list[i]); + } + + /* enable modifiers */ + for (i = 0; i < ena_mod_count; i++) { + AUDIO_LOG_INFO("Enable modifier : %s", ena_mod_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enamod", ena_mod_list[i]) < 0) + AUDIO_LOG_ERROR("enable %s modifier failed", ena_mod_list[i]); + } + } else { + is_verb_changed = 1; + + AUDIO_LOG_DEBUG("Setting new verb: %s", verb); + /* set new verb */ + if (snd_use_case_set(ah->ucm.uc_mgr, "_verb", verb) < 0) { + AUDIO_LOG_ERROR("Setting verb %s failed", verb); + audio_ret = AUDIO_ERR_UNDEFINED; + goto exit; + } + /* enable devices */ + for (i = 0; i < dev_count; i++) { + AUDIO_LOG_DEBUG("Enable device : %s", devices[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enadev", devices[i]) < 0) + AUDIO_LOG_ERROR("Enable %s device failed", devices[i]); + } + /* enable modifiers */ + for (i = 0; i < mod_count; i++) { + AUDIO_LOG_DEBUG("Enable modifier : %s", modifiers[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enamod", modifiers[i]) < 0) + AUDIO_LOG_ERROR("Enable %s modifier failed", modifiers[i]); + } + } + +exit: + if (old_verb) + free((void *)old_verb); + if (old_dev_list) + snd_use_case_free_list(old_dev_list, old_dev_count); + if (old_mod_list) + snd_use_case_free_list(old_mod_list, old_mod_count); + if (dis_dev_list) + free((void *)dis_dev_list); + if (ena_dev_list) + free((void *)ena_dev_list); + if (dis_mod_list) + free((void *)dis_mod_list); + if (ena_mod_list) + free((void *)ena_mod_list); + + if (is_verb_changed == 1 || is_dev_changed == 1 || is_mod_changed == 1) { + const char *new_verb = NULL, **new_dev_list = NULL, **new_mod_list = NULL; + int new_dev_count = 0, new_mod_count = 0; + + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &new_verb); + new_dev_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enadevs", &new_dev_list); + new_mod_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enamods", &new_mod_list); + __dump_use_case(UCM_PREFIX_CHANGED, new_verb, new_dev_list, new_dev_count, new_mod_list, new_mod_count); + + if (new_verb) + free((void *)new_verb); + if (new_dev_list) + snd_use_case_free_list(new_dev_list, new_dev_count); + if (new_mod_list) + snd_use_case_free_list(new_mod_list, new_mod_count); + } + + return audio_ret; +} + +audio_return_t _ucm_set_devices(audio_hal_t *ah, const char *verb, const char *devices[]) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + int is_verb_changed = 0, is_dev_changed = 0; + const char *old_verb = NULL, **old_dev_list = NULL; + int old_dev_count = 0, dev_count = 0; + const char **dis_dev_list = NULL, **ena_dev_list = NULL; + int dis_dev_count = 0, ena_dev_count = 0; + int i = 0, j = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(verb, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER); + + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &old_verb); + old_dev_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enadevs", &old_dev_list); + __dump_use_case(UCM_PREFIX_CURRENT, old_verb, old_dev_list, old_dev_count, NULL, 0); + + if (devices) { + if (streq(verb, "Voice")) { + /* In case of Voice verb with Bluetooth device, make this device alone */ + for (dev_count = 0; devices[dev_count]; dev_count++) { + if (streq(devices[dev_count], "Bluetooth")) { + devices = &devices[dev_count]; + dev_count = 1; + AUDIO_LOG_DEBUG("Voice verb with Bluetooth device only"); + break; + } + } + } else { + for (dev_count = 0; devices[dev_count]; dev_count++); + } + } + + __dump_use_case(UCM_PREFIX_REQUESTED, verb, devices, dev_count, NULL, 0); + + if (old_verb && streq(verb, old_verb)) { + AUDIO_LOG_DEBUG("current verb and new verb is same. No need to change verb, disable devices explicitely"); + + if (old_dev_count > 0) { + dis_dev_list = (const char **)malloc(sizeof(const char *) * old_dev_count); + for (i = 0; i < old_dev_count; i++) { + dis_dev_list[i] = NULL; + } + } + if (dev_count > 0) { + ena_dev_list = (const char **)malloc(sizeof(const char *) * dev_count); + for (i = 0; i < dev_count; i++) { + ena_dev_list[i] = NULL; + } + } + + /* update disable devices list which are not present in new device list */ + for (i = 0; i < old_dev_count; i++) { + int need_disable_dev = 1; + + for (j = 0; j < dev_count; j++) { + if (streq(old_dev_list[i], devices[j])) { + need_disable_dev = 0; + break; + } + } + if (need_disable_dev) { + if (is_dev_changed == 0) + is_dev_changed = 1; + dis_dev_list[dis_dev_count++] = old_dev_list[i]; + } + } + + /* update enable devices list which are not present in old device list */ + for (i = 0; i < dev_count; i++) { + int need_enable_dev = 1; + + for (j = 0; j < old_dev_count; j++) { + if (streq(devices[i], old_dev_list[j])) { + need_enable_dev = 0; + break; + } + } + if (need_enable_dev) { + if (is_dev_changed == 0) + is_dev_changed = 1; + ena_dev_list[ena_dev_count++] = devices[i]; + } + } + + /* disable devices */ + for (i = 0; i < dis_dev_count; i++) { + AUDIO_LOG_INFO("Disable device : %s", dis_dev_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_disdev", dis_dev_list[i]) < 0) + AUDIO_LOG_ERROR("disable %s device failed", dis_dev_list[i]); + } + + /* enable devices */ + for (i = 0; i < ena_dev_count; i++) { + AUDIO_LOG_INFO("Enable device : %s", ena_dev_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enadev", ena_dev_list[i]) < 0) + AUDIO_LOG_ERROR("enable %s device failed", ena_dev_list[i]); + } + + } else { + is_verb_changed = 1; + + AUDIO_LOG_INFO("Setting new verb: %s", verb); + /* set new verb */ + if (snd_use_case_set(ah->ucm.uc_mgr, "_verb", verb) < 0) { + AUDIO_LOG_ERROR("Setting verb %s failed", verb); + audio_ret = AUDIO_ERR_UNDEFINED; + goto exit; + } + /* enable devices */ + for (i = 0; i < dev_count; i++) { + AUDIO_LOG_INFO("Enable device : %s", devices[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enadev", devices[i]) < 0) + AUDIO_LOG_ERROR("Enable %s device failed", devices[i]); + } + } + +exit: + if (old_verb) + free((void *)old_verb); + if (old_dev_list) + snd_use_case_free_list(old_dev_list, old_dev_count); + if (dis_dev_list) + free((void *)dis_dev_list); + if (ena_dev_list) + free((void *)ena_dev_list); + + if (is_verb_changed == 1 || is_dev_changed == 1) { + const char *new_verb = NULL, **new_dev_list = NULL; + int new_dev_count = 0; + + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &new_verb); + new_dev_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enadevs", &new_dev_list); + __dump_use_case(UCM_PREFIX_CHANGED, new_verb, new_dev_list, new_dev_count, NULL, 0); + + if (new_verb) + free((void *)new_verb); + if (new_dev_list) + snd_use_case_free_list(new_dev_list, new_dev_count); + } + + return audio_ret; + +} + +audio_return_t _ucm_set_modifiers(audio_hal_t *ah, const char *verb, const char *modifiers[]) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + int is_verb_changed = 0, is_mod_changed = 0; + const char *old_verb = NULL, **old_mod_list = NULL; + int old_mod_count = 0, mod_count = 0; + const char **dis_mod_list = NULL, **ena_mod_list = NULL; + int dis_mod_count = 0, ena_mod_count = 0; + int i = 0, j = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(verb, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(modifiers, AUDIO_ERR_PARAMETER); + + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &old_verb); + old_mod_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enamods", &old_mod_list); + __dump_use_case(UCM_PREFIX_CURRENT, old_verb, NULL, 0, old_mod_list, old_mod_count); + + if (modifiers) { + for (mod_count = 0; modifiers[mod_count]; mod_count++); + } + + __dump_use_case(UCM_PREFIX_REQUESTED, verb, NULL, 0, modifiers, mod_count); + + if (old_verb && streq(verb, old_verb)) { + AUDIO_LOG_DEBUG("current verb and new verb is same. No need to change verb, disable devices explicitely"); + + if (old_mod_count > 0) { + dis_mod_list = (const char **)malloc(sizeof(const char *) * old_mod_count); + for (i = 0; i < old_mod_count; i++) { + dis_mod_list[i] = NULL; + } + } + if (mod_count > 0) { + ena_mod_list = (const char **)malloc(sizeof(const char *) * mod_count); + for (i = 0; i < mod_count; i++) { + ena_mod_list[i] = NULL; + } + } + + /* update disable modifiers list which are not present in new modifier list */ + for (i = 0; i < old_mod_count; i++) { + int need_disable_mod = 1; + + for (j = 0; j < mod_count; j++) { + if (streq(old_mod_list[i], modifiers[j])) { + need_disable_mod = 0; + break; + } + } + if (need_disable_mod) { + if (is_mod_changed == 0) + is_mod_changed = 1; + dis_mod_list[dis_mod_count++] = old_mod_list[i]; + } + } + + /* update enable modifiers list which are not present in old modifier list */ + for (i = 0; i < mod_count; i++) { + int need_enable_mod = 1; + + for (j = 0; j < old_mod_count; j++) { + if (streq(modifiers[i], old_mod_list[j])) { + need_enable_mod = 0; + break; + } + } + if (need_enable_mod) { + if (is_mod_changed == 0) + is_mod_changed = 1; + ena_mod_list[ena_mod_count++] = modifiers[i]; + } + } + + /* disable modifiers */ + for (i = 0; i < dis_mod_count; i++) { + AUDIO_LOG_INFO("Disable modifier : %s", dis_mod_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_dismod", dis_mod_list[i]) < 0) + AUDIO_LOG_ERROR("disable %s modifier failed", dis_mod_list[i]); + } + + /* enable modifiers */ + for (i = 0; i < ena_mod_count; i++) { + AUDIO_LOG_INFO("Enable modifier : %s", ena_mod_list[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enamod", ena_mod_list[i]) < 0) + AUDIO_LOG_ERROR("enable %s modifier failed", ena_mod_list[i]); + } + } else { + is_verb_changed = 1; + + AUDIO_LOG_DEBUG("Setting new verb: %s", verb); + /* set new verb */ + if (snd_use_case_set(ah->ucm.uc_mgr, "_verb", verb) < 0) { + AUDIO_LOG_ERROR("Setting verb %s failed", verb); + audio_ret = AUDIO_ERR_UNDEFINED; + goto exit; + } + /* enable modifiers */ + for (i = 0; i < mod_count; i++) { + AUDIO_LOG_DEBUG("Enable modifier : %s", modifiers[i]); + if (snd_use_case_set(ah->ucm.uc_mgr, "_enamod", modifiers[i]) < 0) + AUDIO_LOG_ERROR("Enable %s modifier failed", modifiers[i]); + } + } + +exit: + if (old_verb) + free((void *)old_verb); + if (old_mod_list) + snd_use_case_free_list(old_mod_list, old_mod_count); + if (dis_mod_list) + free((void *)dis_mod_list); + if (ena_mod_list) + free((void *)ena_mod_list); + + if (is_verb_changed == 1 || is_mod_changed == 1) { + const char *new_verb = NULL, **new_mod_list = NULL; + int new_mod_count = 0; + + snd_use_case_get(ah->ucm.uc_mgr, "_verb", &new_verb); + new_mod_count = snd_use_case_get_list(ah->ucm.uc_mgr, "_enamods", &new_mod_list); + __dump_use_case(UCM_PREFIX_CHANGED, new_verb, NULL, 0, new_mod_list, new_mod_count); + + if (new_verb) + free((void *)new_verb); + if (new_mod_list) + snd_use_case_free_list(new_mod_list, new_mod_count); + } + + return audio_ret; +} + +audio_return_t _ucm_get_verb(audio_hal_t *ah, const char **value) +{ + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(value, AUDIO_ERR_PARAMETER); + + if ((ret = snd_use_case_get(ah->ucm.uc_mgr, "_verb", value)) < 0) { + AUDIO_LOG_ERROR("Getting current verb failed: Reason %d", ret); + ret = AUDIO_ERR_UNDEFINED; + } + + return ret; +} + +audio_return_t _ucm_reset_use_case(audio_hal_t *ah) +{ + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->ucm.uc_mgr, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO(">>> UCM reset Verb [ %s ]", AUDIO_USE_CASE_VERB_INACTIVE); + + if ((ret = snd_use_case_set(ah->ucm.uc_mgr, "_verb", AUDIO_USE_CASE_VERB_INACTIVE)) < 0) { + AUDIO_LOG_ERROR("Reset use case failed: Reason %d", ret); + ret = AUDIO_ERR_UNDEFINED; + } + + return ret; +} diff --git a/tizen-audio-impl.h b/tizen-audio-impl.h new file mode 100644 index 0000000..8218032 --- /dev/null +++ b/tizen-audio-impl.h @@ -0,0 +1,66 @@ +#ifndef footizenaudioimplfoo +#define footizenaudioimplfoo + +/* + * audio-hal + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <stdbool.h> + +/* PCM */ +audio_return_t _voice_pcm_open_all(audio_hal_t *ah); +audio_return_t _voice_pcm_close_all(audio_hal_t *ah); +audio_return_t _bt_pcm_open_all(audio_hal_t *ah); +audio_return_t _bt_pcm_close_all(audio_hal_t *ah); +bool _is_voice_pcm_opened_all(audio_hal_t *ah); +bool _is_bt_pcm_opened_all(audio_hal_t *ah); +void _reset_pcm_devices(audio_hal_t *ah); +audio_return_t _pcm_open(const char *card, const char *device, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods, void **pcm_handle); +audio_return_t _pcm_start(void *pcm_handle); +audio_return_t _pcm_stop(void *pcm_handle); +audio_return_t _pcm_close(void *pcm_handle); +audio_return_t _pcm_avail(void *pcm_handle, uint32_t *avail); +audio_return_t _pcm_write(void *pcm_handle, const void *buffer, uint32_t frames); +audio_return_t _pcm_read(void *pcm_handle, void *buffer, uint32_t frames); +audio_return_t _pcm_get_fd(void *pcm_handle, int *fd); +audio_return_t _pcm_recover(void *pcm_handle, int revents); +audio_return_t _pcm_get_params(void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods); +audio_return_t _pcm_set_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods); +audio_return_t _pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event); +audio_return_t _pcm_set_hw_params(snd_pcm_t *pcm, audio_pcm_sample_spec_t *sample_spec, uint8_t *use_mmap, snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *buffer_size); + +/* Control */ +audio_return_t _mixer_control_init(audio_hal_t *ah); +audio_return_t _mixer_control_deinit(audio_hal_t *ah); +audio_return_t _mixer_control_set_param(audio_hal_t *ah, const char* ctl_name, snd_ctl_elem_value_t* value, int size); +audio_return_t _mixer_control_set_value(audio_hal_t *ah, const char *ctl_name, int val); +audio_return_t _mixer_control_set_value_string(audio_hal_t *ah, const char* ctl_name, const char* value); +audio_return_t _mixer_control_get_value(audio_hal_t *ah, const char *ctl_name, int *val); +audio_return_t _mixer_control_get_element(audio_hal_t *ah, const char *ctl_name, snd_hctl_elem_t **elem); + +/* UCM */ +audio_return_t _ucm_init(audio_hal_t *ah); +audio_return_t _ucm_deinit(audio_hal_t *ah); +#define _ucm_update_use_case _ucm_set_use_case +audio_return_t _ucm_set_use_case(audio_hal_t *ah, const char *verb, const char *devices[], const char *modifiers[]); +audio_return_t _ucm_set_devices(audio_hal_t *ah, const char *verb, const char *devices[]); +audio_return_t _ucm_set_modifiers(audio_hal_t *ah, const char *verb, const char *modifiers[]); +audio_return_t _ucm_get_verb(audio_hal_t *ah, const char **value); +audio_return_t _ucm_reset_use_case(audio_hal_t *ah); + +#endif diff --git a/tizen-audio-internal.h b/tizen-audio-internal.h new file mode 100644 index 0000000..5d3ab1d --- /dev/null +++ b/tizen-audio-internal.h @@ -0,0 +1,327 @@ +#ifndef footizenaudiointernalfoo +#define footizenaudiointernalfoo + +/* + * audio-hal + * + * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <stdbool.h> +#include <dlog.h> +#include <time.h> +#include <sys/types.h> +#include <asoundlib.h> +#ifdef __USE_TINYALSA__ +#include <tinyalsa/asoundlib.h> +#endif +#include <pthread.h> +#include <use-case.h> +#include "tizen-audio.h" + +/* Debug */ + +//#define AUDIO_DEBUG +#define PROPERTY_VALUE_MAX 92 +#define BUF_SIZE 1024 +#define AUDIO_DUMP_STR_LEN 256 +#define AUDIO_DEVICE_INFO_LIST_MAX 16 +#ifdef USE_DLOG +#ifdef DLOG_TAG +#undef DLOG_TAG +#endif +#define DLOG_TAG "AUDIO_HAL" +#define AUDIO_LOG_ERROR(...) SLOG(LOG_ERROR, DLOG_TAG, __VA_ARGS__) +#define AUDIO_LOG_WARN(...) SLOG(LOG_WARN, DLOG_TAG, __VA_ARGS__) +#define AUDIO_LOG_INFO(...) SLOG(LOG_INFO, DLOG_TAG, __VA_ARGS__) +#define AUDIO_LOG_DEBUG(...) SLOG(LOG_DEBUG, DLOG_TAG, __VA_ARGS__) +#define AUDIO_LOG_VERBOSE(...) SLOG(LOG_DEBUG, DLOG_TAG, __VA_ARGS__) +#else +#define AUDIO_LOG_ERROR(...) fprintf(stderr, __VA_ARGS__) +#define AUDIO_LOG_WARN(...) fprintf(stderr, __VA_ARGS__) +#define AUDIO_LOG_INFO(...) fprintf(stdout, __VA_ARGS__) +#define AUDIO_LOG_DEBUG(...) fprintf(stdout, __VA_ARGS__) +#define AUDIO_LOG_VERBOSE(...) fprintf(stdout, __VA_ARGS__) +#endif + +#define AUDIO_RETURN_IF_FAIL(expr) do { \ + if (!(expr)) { \ + AUDIO_LOG_ERROR("%s failed", #expr); \ + return; \ + } \ +} while (0) +#define AUDIO_RETURN_VAL_IF_FAIL(expr, val) do { \ + if (!(expr)) { \ + AUDIO_LOG_ERROR("%s failed", #expr); \ + return val; \ + } \ +} while (0) +#define AUDIO_RETURN_NULL_IF_FAIL(expr) do { \ + if (!(expr)) { \ + AUDIO_LOG_ERROR("%s failed", #expr); \ + return NULL; \ + } \ +} while (0) +/* +static device_type_t outDeviceTypes[] = { + { AUDIO_DEVICE_OUT_SPEAKER, "speaker" }, + { AUDIO_DEVICE_OUT_BT_SCO, "bt-sco-headset" }, + { AUDIO_DEVICE_OUT_BT_SCO_8K_NB, "bt-8k-sco-nb-headset" }, + { AUDIO_DEVICE_OUT_BT_SCO_16K_NB, "bt-16k-sco-nb-headset" }, + { AUDIO_DEVICE_OUT_BT_SCO_8K_WB, "bt-8k-sco-wb-headset" }, + { AUDIO_DEVICE_OUT_BT_SCO_16K_WB, "bt-16k-sco-wb-headset" }, + { 0, 0 }, +}; + +static device_type_t inDeviceTypes[] = { + { AUDIO_DEVICE_IN_MAIN_MIC, "main-mic" }, + { AUDIO_DEVICE_IN_BT_SCO, "bt-sco-mic" }, + { AUDIO_DEVICE_IN_BT_SCO_8K_NB, "bt-8k-sco-nb-mic" }, + { AUDIO_DEVICE_IN_BT_SCO_16K_NB, "bt-16k-sco-nb-mic" }, + { AUDIO_DEVICE_IN_BT_SCO_8K_WB, "bt-8k-sco-wb-mic" }, + { AUDIO_DEVICE_IN_BT_SCO_16K_WB, "bt-16k-sco-wb-mic" }, + { 0, 0 }, +}; + + +*/ + +/* Devices : Normal */ +#define AUDIO_DEVICE_OUT 0x00000000 +#define AUDIO_DEVICE_IN 0x80000000 +enum audio_device_type { + AUDIO_DEVICE_NONE = 0, + + /* output devices */ + AUDIO_DEVICE_OUT_SPEAKER = AUDIO_DEVICE_OUT | 0x00000001, + AUDIO_DEVICE_OUT_RECEIVER = AUDIO_DEVICE_OUT | 0x00000002, + AUDIO_DEVICE_OUT_JACK = AUDIO_DEVICE_OUT | 0x00000004, + AUDIO_DEVICE_OUT_BT_SCO = AUDIO_DEVICE_OUT | 0x00000008, + AUDIO_DEVICE_OUT_BT_SCO_8K_NB = AUDIO_DEVICE_OUT | 0x00000010, + AUDIO_DEVICE_OUT_BT_SCO_16K_NB = AUDIO_DEVICE_OUT | 0x00000020, + AUDIO_DEVICE_OUT_BT_SCO_8K_WB = AUDIO_DEVICE_OUT | 0x00000040, + AUDIO_DEVICE_OUT_BT_SCO_16K_WB = AUDIO_DEVICE_OUT | 0x00000080, + AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_SPEAKER | + AUDIO_DEVICE_OUT_RECEIVER | + AUDIO_DEVICE_OUT_JACK | + AUDIO_DEVICE_OUT_BT_SCO | + AUDIO_DEVICE_OUT_BT_SCO_8K_NB | + AUDIO_DEVICE_OUT_BT_SCO_16K_NB | + AUDIO_DEVICE_OUT_BT_SCO_8K_WB | + AUDIO_DEVICE_OUT_BT_SCO_16K_WB), + /* input devices */ + AUDIO_DEVICE_IN_MAIN_MIC = AUDIO_DEVICE_IN | 0x00000001, + AUDIO_DEVICE_IN_SUB_MIC = AUDIO_DEVICE_IN | 0x00000002, + AUDIO_DEVICE_IN_JACK = AUDIO_DEVICE_IN | 0x00000004, + AUDIO_DEVICE_IN_BT_SCO = AUDIO_DEVICE_IN | 0x00000008, + AUDIO_DEVICE_IN_BT_SCO_8K_NB = AUDIO_DEVICE_IN | 0x00000010, + AUDIO_DEVICE_IN_BT_SCO_16K_NB = AUDIO_DEVICE_IN | 0x00000020, + AUDIO_DEVICE_IN_BT_SCO_8K_WB = AUDIO_DEVICE_IN | 0x00000040, + AUDIO_DEVICE_IN_BT_SCO_16K_WB = AUDIO_DEVICE_IN | 0x00000080, + AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_MAIN_MIC | + AUDIO_DEVICE_IN_SUB_MIC | + AUDIO_DEVICE_IN_JACK | + AUDIO_DEVICE_IN_BT_SCO | + AUDIO_DEVICE_IN_BT_SCO_8K_NB | + AUDIO_DEVICE_IN_BT_SCO_16K_NB | + AUDIO_DEVICE_IN_BT_SCO_8K_WB | + AUDIO_DEVICE_IN_BT_SCO_16K_WB), +}; + +typedef struct device_type { + uint32_t type; + const char *name; +} device_type_t; + +/* Verbs */ +#define AUDIO_USE_CASE_VERB_INACTIVE "Inactive" +#define AUDIO_USE_CASE_VERB_HIFI "HiFi" +#define AUDIO_USE_CASE_VERB_VOICECALL "Voice" +#define AUDIO_USE_CASE_VERB_VOIP "VoIP" + +#define streq !strcmp +#define strneq strcmp + +#define ALSA_DEFAULT_CARD "Exynos9110Sound" + +/* DAI PCM DEVICE */ +#define VOICE_PCM_DEVICE "hw:0,1" +#define BT_PCM_DEVICE "hw:0,2" + +#define MAX_DEVICES 5 +#define MAX_NAME_LEN 32 + +/* type definitions */ +typedef signed char int8_t; + +/* PCM */ +typedef struct { + snd_pcm_format_t format; + uint32_t rate; + uint8_t channels; +} audio_pcm_sample_spec_t; + +/* Routing */ +typedef enum audio_route_mode { + VERB_NORMAL, + VERB_VOICECALL, + VERB_VOIP, +} audio_route_mode_t; + +typedef struct { + snd_pcm_t *in; + snd_pcm_t *out; +} audio_pcm_devices_t; + +typedef struct audio_hal_device { + uint32_t active_in; + uint32_t active_out; + audio_pcm_devices_t voice_pcm; + audio_pcm_devices_t bt_pcm; + audio_route_mode_t mode; + bool bt_wideband; + bool bt_sco_ready; +} audio_hal_device_t; + +/* Volume */ +#define AUDIO_VOLUME_LEVEL_MAX 16 + +typedef enum audio_volume { + AUDIO_VOLUME_TYPE_SYSTEM, /**< System volume type */ + AUDIO_VOLUME_TYPE_NOTIFICATION, /**< Notification volume type */ + AUDIO_VOLUME_TYPE_ALARM, /**< Alarm volume type */ + AUDIO_VOLUME_TYPE_RINGTONE, /**< Ringtone volume type */ + AUDIO_VOLUME_TYPE_MEDIA, /**< Media volume type */ + AUDIO_VOLUME_TYPE_CALL, /**< Call volume type */ + AUDIO_VOLUME_TYPE_VOIP, /**< VOIP volume type */ + AUDIO_VOLUME_TYPE_VOICE, /**< Voice volume type */ + AUDIO_VOLUME_TYPE_MAX, /**< Volume type count */ +} audio_volume_t; + +typedef enum audio_gain { + AUDIO_GAIN_TYPE_DEFAULT, + AUDIO_GAIN_TYPE_DIALER, + AUDIO_GAIN_TYPE_TOUCH, + AUDIO_GAIN_TYPE_AF, + AUDIO_GAIN_TYPE_SHUTTER1, + AUDIO_GAIN_TYPE_SHUTTER2, + AUDIO_GAIN_TYPE_CAMCODING, + AUDIO_GAIN_TYPE_MIDI, + AUDIO_GAIN_TYPE_BOOTING, + AUDIO_GAIN_TYPE_VIDEO, + AUDIO_GAIN_TYPE_TTS, + AUDIO_GAIN_TYPE_MAX, +} audio_gain_t; + +typedef struct audio_volume_value_table { + double volume[AUDIO_VOLUME_TYPE_MAX][AUDIO_VOLUME_LEVEL_MAX]; + double gain[AUDIO_GAIN_TYPE_MAX]; +} audio_volume_value_table_t; + +enum { + AUDIO_DEVICE_DIRECTION_IN = 0x01, + AUDIO_DEVICE_DIRECTION_OUT = 0x02 +}; + +enum { + AUDIO_VOLUME_DEVICE_DEFAULT, + AUDIO_VOLUME_DEVICE_MAX, +}; + +typedef struct audio_hal_volume { + uint32_t volume_level_max[AUDIO_VOLUME_LEVEL_MAX]; + uint32_t volume_level[AUDIO_VOLUME_TYPE_MAX]; + audio_volume_value_table_t *volume_value_table; +} audio_hal_volume_t; + +/* UCM */ +typedef struct audio_hal_ucm { + snd_use_case_mgr_t* uc_mgr; +} audio_hal_ucm_t; + +/* Mixer */ +typedef struct audio_hal_mixer { + snd_mixer_t *mixer; + pthread_mutex_t mutex; + struct { + snd_ctl_elem_value_t *value; + snd_ctl_elem_id_t *id; + snd_ctl_elem_info_t *info; + } control; +} audio_hal_mixer_t; + +/* Audio format */ +typedef enum audio_sample_format { + AUDIO_SAMPLE_U8, + AUDIO_SAMPLE_ALAW, + AUDIO_SAMPLE_ULAW, + AUDIO_SAMPLE_S16LE, + AUDIO_SAMPLE_S16BE, + AUDIO_SAMPLE_FLOAT32LE, + AUDIO_SAMPLE_FLOAT32BE, + AUDIO_SAMPLE_S32LE, + AUDIO_SAMPLE_S32BE, + AUDIO_SAMPLE_S24LE, + AUDIO_SAMPLE_S24BE, + AUDIO_SAMPLE_S24_32LE, + AUDIO_SAMPLE_S24_32BE, + AUDIO_SAMPLE_MAX, + AUDIO_SAMPLE_INVALID = -1 +} audio_sample_format_t; + +typedef struct audio_hal_comm { + message_cb msg_cb; + void *user_data; +} audio_hal_comm_t; + +/* Overall */ +#define MAX_DIRECTION 2 +typedef struct audio_hal { + audio_hal_device_t device; + audio_hal_volume_t volume; + audio_hal_ucm_t ucm; + audio_hal_mixer_t mixer; + audio_hal_comm_t comm; + audio_route_info_t *saved_route_infos[MAX_DIRECTION]; + bool call_wideband; +} audio_hal_t; + +audio_return_t _audio_ctrl_init(audio_hal_t *ah); +audio_return_t _audio_ctrl_deinit(audio_hal_t *ah); +audio_return_t _audio_volume_init(audio_hal_t *ah); +audio_return_t _audio_volume_deinit(audio_hal_t *ah); +audio_return_t _audio_routing_init(audio_hal_t *ah); +audio_return_t _audio_routing_deinit(audio_hal_t *ah); +audio_return_t _audio_stream_init(audio_hal_t *ah); +audio_return_t _audio_stream_deinit(audio_hal_t *ah); +audio_return_t _audio_pcm_init(audio_hal_t *ah); +audio_return_t _audio_pcm_deinit(audio_hal_t *ah); +audio_return_t _audio_comm_init(audio_hal_t *ah); +audio_return_t _audio_comm_deinit(audio_hal_t *ah); +audio_return_t _audio_comm_send_message(audio_hal_t *ah, const char *name, int value); + +typedef struct _dump_data { + char *strbuf; + int left; + char *p; +} dump_data_t; + +dump_data_t* _audio_dump_new(int length); +void _audio_dump_add_str(dump_data_t *dump, const char *fmt, ...); +char* _audio_dump_get_str(dump_data_t *dump); +void _audio_dump_free(dump_data_t *dump); + +#endif diff --git a/tizen-audio-pcm.c b/tizen-audio-pcm.c new file mode 100644 index 0000000..556be06 --- /dev/null +++ b/tizen-audio-pcm.c @@ -0,0 +1,196 @@ +/* + * audio-hal + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "tizen-audio-internal.h" +#include "tizen-audio-impl.h" + +audio_return_t _audio_pcm_init(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + ah->device.voice_pcm.in = NULL; + ah->device.voice_pcm.out = NULL; + ah->device.bt_pcm.in = NULL; + ah->device.bt_pcm.out = NULL; + + return AUDIO_RET_OK; +} + +audio_return_t _audio_pcm_deinit(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + return AUDIO_RET_OK; +} + +audio_return_t audio_pcm_open(void *audio_handle, const char *card, const char *device, uint32_t direction, void *sample_spec, + uint32_t period_size, uint32_t periods, void **pcm_handle) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL((period_size > 0), AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL((periods > 0), AUDIO_ERR_PARAMETER); + + if ((audio_ret = _pcm_open(card, device, direction, sample_spec, period_size, periods, pcm_handle))) + return audio_ret; + + AUDIO_LOG_INFO("Opening PCM handle %p", *pcm_handle); + + return AUDIO_RET_OK; +} + +audio_return_t audio_pcm_start(void *audio_handle, void *pcm_handle) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + audio_ret = _pcm_start(pcm_handle); + + return audio_ret; +} + +audio_return_t audio_pcm_stop(void *audio_handle, void *pcm_handle) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + audio_ret = _pcm_stop(pcm_handle); + + return audio_ret; +} + +audio_return_t audio_pcm_close(void *audio_handle, void *pcm_handle) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + if ((audio_ret = _pcm_close(pcm_handle))) + return audio_ret; + + pcm_handle = NULL; + + AUDIO_LOG_INFO("PCM handle close success"); + + return audio_ret; +} + +audio_return_t audio_pcm_avail(void *audio_handle, void *pcm_handle, uint32_t *avail) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(avail, AUDIO_ERR_PARAMETER); + + audio_ret = _pcm_avail(pcm_handle, avail); + + return audio_ret; +} + +audio_return_t audio_pcm_write(void *audio_handle, void *pcm_handle, const void *buffer, uint32_t frames) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + audio_ret = _pcm_write(pcm_handle, buffer, frames); + + return audio_ret; +} + +audio_return_t audio_pcm_read(void *audio_handle, void *pcm_handle, void *buffer, uint32_t frames) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + audio_ret = _pcm_read(pcm_handle, buffer, frames); + + return audio_ret; +} + +audio_return_t audio_pcm_get_fd(void *audio_handle, void *pcm_handle, int *fd) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(fd, AUDIO_ERR_PARAMETER); + + audio_ret = _pcm_get_fd(pcm_handle, fd); + + return audio_ret; +} + +audio_return_t audio_pcm_recover(void *audio_handle, void *pcm_handle, int revents) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + + audio_ret = _pcm_recover(pcm_handle, revents); + + return audio_ret; +} + +audio_return_t audio_pcm_get_params(void *audio_handle, void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(period_size, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(periods, AUDIO_ERR_PARAMETER); + + audio_ret = _pcm_get_params(pcm_handle, direction, sample_spec, period_size, periods); + + return audio_ret; +} + +audio_return_t audio_pcm_set_params(void *audio_handle, void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(period_size, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(periods, AUDIO_ERR_PARAMETER); + + audio_ret = _pcm_set_params(pcm_handle, direction, sample_spec, period_size, periods); + + return audio_ret; +} diff --git a/tizen-audio-routing.c b/tizen-audio-routing.c new file mode 100644 index 0000000..96528f6 --- /dev/null +++ b/tizen-audio-routing.c @@ -0,0 +1,594 @@ +/* + * audio-hal + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "tizen-audio-internal.h" +#include "tizen-audio-impl.h" + +/* #define DEBUG_TIMING */ + +static device_type_t outDeviceTypes[] = { + { AUDIO_DEVICE_OUT_SPEAKER, "speaker" }, + { AUDIO_DEVICE_OUT_BT_SCO, "bt-sco-headset" }, + { AUDIO_DEVICE_OUT_BT_SCO_8K_NB, "bt-8k-sco-nb-headset" }, + { AUDIO_DEVICE_OUT_BT_SCO_16K_NB, "bt-16k-sco-nb-headset" }, + { AUDIO_DEVICE_OUT_BT_SCO_8K_WB, "bt-8k-sco-wb-headset" }, + { AUDIO_DEVICE_OUT_BT_SCO_16K_WB, "bt-16k-sco-wb-headset" }, + { 0, 0 }, +}; + +static device_type_t inDeviceTypes[] = { + { AUDIO_DEVICE_IN_MAIN_MIC, "main-mic" }, + { AUDIO_DEVICE_IN_BT_SCO, "bt-sco-mic" }, + { AUDIO_DEVICE_IN_BT_SCO_8K_NB, "bt-8k-sco-nb-mic" }, + { AUDIO_DEVICE_IN_BT_SCO_16K_NB, "bt-16k-sco-nb-mic" }, + { AUDIO_DEVICE_IN_BT_SCO_8K_WB, "bt-8k-sco-wb-mic" }, + { AUDIO_DEVICE_IN_BT_SCO_16K_WB, "bt-16k-sco-wb-mic" }, + { 0, 0 }, +}; + +static const char* mode_to_verb_str[] = { + AUDIO_USE_CASE_VERB_HIFI, + AUDIO_USE_CASE_VERB_VOICECALL, + AUDIO_USE_CASE_VERB_VOIP, +}; + +static bool __is_acive_device_bt_sco_for_call(audio_hal_t *ah) +{ + if (!ah) { + AUDIO_LOG_WARN("ah is null"); + return false; + } + + if (ah->device.active_in & AUDIO_DEVICE_IN_BT_SCO_8K_NB && + ah->device.active_out & AUDIO_DEVICE_OUT_BT_SCO_8K_NB) + return true; + if (ah->device.active_in & AUDIO_DEVICE_IN_BT_SCO_16K_NB && + ah->device.active_out & AUDIO_DEVICE_OUT_BT_SCO_16K_NB) + return true; + if (ah->device.active_in & AUDIO_DEVICE_IN_BT_SCO_8K_WB && + ah->device.active_out & AUDIO_DEVICE_OUT_BT_SCO_8K_WB) + return true; + if (ah->device.active_in & AUDIO_DEVICE_IN_BT_SCO_16K_WB && + ah->device.active_out & AUDIO_DEVICE_OUT_BT_SCO_16K_WB) + return true; + + return false; +} + +static uint32_t __convert_device_string_to_enum(audio_hal_t *ah, const char *verb, const char* device_str, uint32_t direction) +{ + uint32_t device = 0; + + AUDIO_LOG_INFO("verb(%s), device string(%s), direction(%d)", verb, device_str, direction); + + if (!strncmp(device_str, "builtin-speaker", MAX_NAME_LEN)) { + device = AUDIO_DEVICE_OUT_SPEAKER; + + } else if ((!strncmp(device_str, "bt-sco", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_OUT)) { + if (!strncmp(verb, AUDIO_USE_CASE_VERB_HIFI, MAX_NAME_LEN)) { + device = AUDIO_DEVICE_OUT_BT_SCO; + } else if (!strncmp(verb, AUDIO_USE_CASE_VERB_VOICECALL, MAX_NAME_LEN)) { + AUDIO_LOG_INFO("device.bt_widband(%d), call_wideband(%d)", ah->device.bt_wideband, ah->call_wideband); + if (ah->device.bt_wideband) { + if (ah->call_wideband) + device = AUDIO_DEVICE_OUT_BT_SCO_16K_WB; + else + device = AUDIO_DEVICE_OUT_BT_SCO_16K_NB; + } else { + if (ah->call_wideband) + device = AUDIO_DEVICE_OUT_BT_SCO_8K_WB; + else + device = AUDIO_DEVICE_OUT_BT_SCO_8K_NB; + } + } else { + device = AUDIO_DEVICE_OUT_BT_SCO; + AUDIO_LOG_WARN("invalid verb, set device(%d) forcedly", device); + } + + } else if ((!strncmp(device_str, "builtin-mic", MAX_NAME_LEN))) { + device = AUDIO_DEVICE_IN_MAIN_MIC; + + } else if ((!strncmp(device_str, "bt-sco", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_IN)) { + if (!strncmp(verb, AUDIO_USE_CASE_VERB_HIFI, MAX_NAME_LEN)) { + device = AUDIO_DEVICE_IN_BT_SCO; + } else if (!strncmp(verb, AUDIO_USE_CASE_VERB_VOICECALL, MAX_NAME_LEN)) { + AUDIO_LOG_INFO("device.bt_widband(%d), call_wideband(%d)", ah->device.bt_wideband, ah->call_wideband); + if (ah->device.bt_wideband) { + if (ah->call_wideband) + device = AUDIO_DEVICE_IN_BT_SCO_16K_WB; + else + device = AUDIO_DEVICE_IN_BT_SCO_16K_NB; + } else { + if (ah->call_wideband) + device = AUDIO_DEVICE_IN_BT_SCO_8K_WB; + else + device = AUDIO_DEVICE_IN_BT_SCO_8K_NB; + } + } else { + device = AUDIO_DEVICE_IN_BT_SCO; + AUDIO_LOG_WARN("invalid verb, set device(%d) forcedly", device); + } + + } else { + device = AUDIO_DEVICE_NONE; + } + + AUDIO_LOG_INFO("device type(%s), enum(0x%x)", device_str, device); + return device; +} + +static audio_return_t __set_devices(audio_hal_t *ah, const char *verb, device_info_t *devices, uint32_t num_of_devices) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + uint32_t new_device = 0; + const char *active_devices[MAX_DEVICES] = {NULL,}; + int i = 0, j = 0, dev_idx = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(num_of_devices, AUDIO_ERR_PARAMETER); + + if (num_of_devices > MAX_DEVICES) { + num_of_devices = MAX_DEVICES; + AUDIO_LOG_ERROR("error: num_of_devices"); + return AUDIO_ERR_PARAMETER; + } + + if (devices[0].direction == AUDIO_DIRECTION_OUT) { + ah->device.active_out &= 0x0; + if (ah->device.active_in) { + /* check the active in devices */ + for (j = 0; j < inDeviceTypes[j].type; j++) { + if (((ah->device.active_in & (~AUDIO_DEVICE_IN)) & inDeviceTypes[j].type)) + active_devices[dev_idx++] = inDeviceTypes[j].name; + } + } + } else if (devices[0].direction == AUDIO_DIRECTION_IN) { + ah->device.active_in &= 0x0; + if (ah->device.active_out) { + /* check the active out devices */ + for (j = 0; j < outDeviceTypes[j].type; j++) { + if (ah->device.active_out & outDeviceTypes[j].type) + active_devices[dev_idx++] = outDeviceTypes[j].name; + } + } + } + + for (i = 0; i < num_of_devices; i++) { + new_device = __convert_device_string_to_enum(ah, verb, devices[i].type, devices[i].direction); + if (new_device & AUDIO_DEVICE_IN) { + for (j = 0; j < inDeviceTypes[j].type; j++) { + if (new_device == inDeviceTypes[j].type) { + active_devices[dev_idx++] = inDeviceTypes[j].name; + ah->device.active_in |= new_device; + } + } + } else { + for (j = 0; j < outDeviceTypes[j].type; j++) { + if (new_device == outDeviceTypes[j].type) { + active_devices[dev_idx++] = outDeviceTypes[j].name; + ah->device.active_out |= new_device; + } + } + } + } + + if (active_devices[0] == NULL) { + AUDIO_LOG_ERROR("Failed to set device: active device is NULL"); + return AUDIO_ERR_PARAMETER; + } + + audio_ret = _ucm_set_devices(ah, verb, active_devices); + if (audio_ret) + AUDIO_LOG_ERROR("Failed to set device: error = %d", audio_ret); + + return audio_ret; +} + +static audio_return_t __save_route_infos(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices) +{ + int i = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER); + + for (i = 0; i < MAX_DIRECTION; i++) { + if (!ah->saved_route_infos[i]) { + ah->saved_route_infos[i] = malloc(sizeof(audio_route_info_t)); + memset(ah->saved_route_infos[i], 0, sizeof(audio_route_info_t)); + + ah->saved_route_infos[i]->device_infos = malloc(sizeof(device_info_t)); + memcpy(ah->saved_route_infos[i]->device_infos, devices, sizeof(device_info_t)); + + ah->saved_route_infos[i]->num_of_devices = num_of_devices; + + AUDIO_LOG_INFO("SAVE route infos[%d]: device_infos->type[%s],id[%u]", + i, ah->saved_route_infos[i]->device_infos->type, ah->saved_route_infos[i]->device_infos->id); + return AUDIO_RET_OK; + } + } + + AUDIO_LOG_ERROR("could not find empty slot to save route infos"); + return AUDIO_ERR_INTERNAL; +} + +static void __reset_saved_route_infos(void *audio_handle) +{ + audio_hal_t *ah = (audio_hal_t *)audio_handle; + int i = 0; + + AUDIO_RETURN_IF_FAIL(ah); + + for (i = 0; i < MAX_DIRECTION; i++) { + if (ah->saved_route_infos[i]) { + if (ah->saved_route_infos[i]->device_infos) + free(ah->saved_route_infos[i]->device_infos); + free(ah->saved_route_infos[i]); + ah->saved_route_infos[i] = NULL; + AUDIO_LOG_INFO("reset saved route infos[%d] well", i); + } + } +} + +static audio_return_t __update_route_ap_playback_capture(audio_hal_t *ah, audio_route_info_t *route_info) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + device_info_t *devices = NULL; + const char *verb = mode_to_verb_str[VERB_NORMAL]; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(route_info, AUDIO_ERR_PARAMETER); + + if (ah->device.mode != VERB_NORMAL) { + _reset_pcm_devices(ah); + ah->device.mode = VERB_NORMAL; + } + + devices = route_info->device_infos; + + AUDIO_LOG_INFO("update_route_ap_playback_capture++ "); + + audio_ret = __set_devices(ah, verb, devices, route_info->num_of_devices); + if (audio_ret) { + AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret); + return audio_ret; + } + + return audio_ret; +} + +static audio_return_t __update_route_voicecall(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + const char *verb = mode_to_verb_str[VERB_VOICECALL]; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("update_route_voicecall++"); + + if (!strncmp(devices[0].type, "bt-sco", MAX_NAME_LEN)) + if (!ah->device.bt_sco_ready) + return __save_route_infos(ah, devices, num_of_devices); + + if ((audio_ret = __set_devices(ah, verb, devices, num_of_devices))) { + AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret); + return audio_ret; + } + + if (ah->device.mode != VERB_VOICECALL) { + ah->device.mode = VERB_VOICECALL; + _reset_pcm_devices(ah); + } else { + /* if this request is for BT SCO device */ + if (__is_acive_device_bt_sco_for_call(ah)) { + if (_is_bt_pcm_opened_all(ah)) { + AUDIO_LOG_INFO("bt pcm device is already opened, skip it"); + return audio_ret; + } + if ((audio_ret = _bt_pcm_open_all(ah))) { + AUDIO_LOG_ERROR("Failed to open bt pcm device: error = 0x%x", audio_ret); + return audio_ret; + } + } + + if (_is_voice_pcm_opened_all(ah)) { + AUDIO_LOG_INFO("voice pcm device is already opened, skip it"); + return audio_ret; + } + if ((audio_ret = _voice_pcm_open_all(ah))) { + AUDIO_LOG_ERROR("Failed to open voice pcm device: error = 0x%x", audio_ret); + return audio_ret; + } + } + + return audio_ret; +} + +static audio_return_t __update_route_voip(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + const char *verb = mode_to_verb_str[VERB_NORMAL]; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("update_route_voip++"); + + if ((audio_ret = __set_devices(ah, verb, devices, num_of_devices))) { + AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret); + return audio_ret; + } + /* FIXME. If necessary, set VERB_VOIP */ + ah->device.mode = VERB_NORMAL; + + /* TO DO: Set modifiers */ + return audio_ret; +} + +static audio_return_t __update_route_voice_recognition(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + const char *verb = mode_to_verb_str[VERB_NORMAL]; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("update_route_voice_recognition++"); + + if ((audio_ret = __set_devices(ah, verb, devices, num_of_devices))) { + AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret); + return audio_ret; + } + + if (ah->device.mode != VERB_NORMAL) { + ah->device.mode = VERB_NORMAL; + _reset_pcm_devices(ah); + } + + /* if this request is for BT SCO device */ + if (ah->device.active_in & AUDIO_DEVICE_IN_BT_SCO) { + if (_is_bt_pcm_opened_all(ah)) { + AUDIO_LOG_INFO("bt pcm device is already opened, skip it"); + return audio_ret; + } + + if ((audio_ret = _bt_pcm_open_all(ah))) { + AUDIO_LOG_ERROR("Failed to open bt pcm device: error = 0x%x", audio_ret); + return audio_ret; + } + } + + return audio_ret; +} + +static audio_return_t __update_route_voice_information(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + const char *verb = mode_to_verb_str[VERB_NORMAL]; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("update_route_voice_information++"); + + audio_ret = __set_devices(ah, verb, devices, num_of_devices); + if (audio_ret) { + AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret); + return audio_ret; + } + + if (ah->device.mode != VERB_NORMAL) { + ah->device.mode = VERB_NORMAL; + _reset_pcm_devices(ah); + } + /* if this request is for BT SCO device */ + if (ah->device.active_out & AUDIO_DEVICE_OUT_BT_SCO) { + if (_is_bt_pcm_opened_all(ah)) { + AUDIO_LOG_INFO("bt pcm device is already opened, skip it"); + return audio_ret; + } + + if ((audio_ret = _bt_pcm_open_all(ah))) { + AUDIO_LOG_ERROR("Failed to open bt pcm device: error = 0x%x", audio_ret); + return audio_ret; + } + } + + return audio_ret; +} + +static audio_return_t __update_route_reset(audio_hal_t *ah, uint32_t direction) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + const char *active_devices[MAX_DEVICES] = {NULL,}; + int i = 0, dev_idx = 0; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("update_route_reset++, direction(0x%x)", direction); + + if (direction == AUDIO_DIRECTION_OUT) { + ah->device.active_out &= 0x0; + if (ah->device.active_in) { + /* check the active in devices */ + for (i = 0; i < inDeviceTypes[i].type; i++) { + if (((ah->device.active_in & (~AUDIO_DEVICE_IN)) & inDeviceTypes[i].type)) { + active_devices[dev_idx++] = inDeviceTypes[i].name; + AUDIO_LOG_INFO("added for in : %s", inDeviceTypes[i].name); + } + } + } + } else { + ah->device.active_in &= 0x0; + if (ah->device.active_out) { + /* check the active out devices */ + for (i = 0; i < outDeviceTypes[i].type; i++) { + if (ah->device.active_out & outDeviceTypes[i].type) { + active_devices[dev_idx++] = outDeviceTypes[i].name; + AUDIO_LOG_INFO("added for out : %s", outDeviceTypes[i].name); + } + } + } + } + + if (ah->device.mode == VERB_VOICECALL) { + if ((audio_ret = _bt_pcm_close_all(ah))) + AUDIO_LOG_ERROR("failed to _bt_pcm_close(), ret(0x%x)", audio_ret); + if ((audio_ret = _voice_pcm_close_all(ah))) + AUDIO_LOG_ERROR("failed to _voice_pcm_close(), ret(0x%x)", audio_ret); + if (!ah->device.active_in && !ah->device.active_out) + ah->device.mode = VERB_NORMAL; + } else if (ah->device.mode == VERB_NORMAL) { + if (direction == AUDIO_DIRECTION_IN) { + /* voice-recognition case */ + if ((audio_ret = _bt_pcm_close_all(ah))) + AUDIO_LOG_ERROR("failed to _bt_pcm_close(), ret(0x%x)", audio_ret); + } + } + + __reset_saved_route_infos(ah); + + if (active_devices[0] == NULL) { + AUDIO_LOG_DEBUG("active device is NULL, no need to update."); + return AUDIO_RET_OK; + } + + if ((audio_ret = _ucm_set_devices(ah, mode_to_verb_str[ah->device.mode], active_devices))) + AUDIO_LOG_ERROR("failed to _ucm_set_devices(), ret(0x%x)", audio_ret); + + /* reset bandwidth information */ + ah->device.bt_wideband = false; + ah->call_wideband = false; + ah->device.bt_sco_ready = false; + + return audio_ret; +} + +audio_return_t _audio_routing_init(audio_hal_t *ah) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + ah->device.active_in = 0x0; + ah->device.active_out = 0x0; + ah->device.mode = VERB_NORMAL; + + if ((audio_ret = _ucm_init(ah))) + AUDIO_LOG_ERROR("failed to _ucm_init(), ret(0x%x)", audio_ret); + + return audio_ret; +} + +audio_return_t _audio_routing_deinit(audio_hal_t *ah) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + if ((audio_ret = _ucm_deinit(ah))) + AUDIO_LOG_ERROR("failed to _ucm_deinit(), ret(0x%x)", audio_ret); + + return audio_ret; +} + +audio_return_t audio_update_route(void *audio_handle, audio_route_info_t *info) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_hal_t *ah = (audio_hal_t *)audio_handle; + device_info_t *devices = NULL; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info->role, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("role:%s", info->role); + + devices = info->device_infos; + + if (!strncmp("call-voice", info->role, MAX_NAME_LEN)) { + if ((audio_ret = __update_route_voicecall(ah, devices, info->num_of_devices))) + AUDIO_LOG_WARN("update voicecall route return 0x%x", audio_ret); + + } else if (!strncmp("voip", info->role, MAX_NAME_LEN)) { + if ((audio_ret = __update_route_voip(ah, devices, info->num_of_devices))) + AUDIO_LOG_WARN("update voip route return 0x%x", audio_ret); + + } else if (!strncmp("voice-recognition", info->role, MAX_NAME_LEN) || + !strncmp("voice-recognition-service", info->role, MAX_NAME_LEN)) { + if ((audio_ret = __update_route_voice_recognition(ah, devices, info->num_of_devices))) + AUDIO_LOG_WARN("update voice-recognition route return 0x%x", audio_ret); + + } else if (!strncmp("voice-information", info->role, MAX_NAME_LEN)) { + if ((audio_ret = __update_route_voice_information(ah, devices, info->num_of_devices))) + AUDIO_LOG_WARN("update voice-information route return 0x%x", audio_ret); + + } else if (!strncmp("reset", info->role, MAX_NAME_LEN)) { + if ((audio_ret = __update_route_reset(ah, devices->direction))) + AUDIO_LOG_WARN("update reset return 0x%x", audio_ret); + + } else { + /* need to prepare for "alarm","notification","emergency","ringtone" */ + if ((audio_ret = __update_route_ap_playback_capture(ah, info))) + AUDIO_LOG_WARN("update playback route return 0x%x", audio_ret); + } + return audio_ret; +} + +audio_return_t audio_update_route_option(void *audio_handle, audio_route_option_t *option) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(option, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("role:%s, name:%s, value:%d", option->role, option->name, option->value); + + if (!strncmp("bt-wideband", option->name, MAX_NAME_LEN)) { + ah->device.bt_wideband = (option->value > 0) ? true : false; + } else if (!strncmp("call-wideband", option->name, MAX_NAME_LEN)) { + ah->call_wideband = (option->value > 0) ? true : false; + } else if (!strncmp("bt-sco-ready", option->name, MAX_NAME_LEN)) { + ah->device.bt_sco_ready = (option->value > 0) ? true : false; + if (ah->device.bt_sco_ready) { + int i = 0; + for (i = 0; i < MAX_DIRECTION; i++) { + if (ah->saved_route_infos[i]) { + if ((audio_ret = __update_route_voicecall(ah, ah->saved_route_infos[i]->device_infos, ah->saved_route_infos[i]->num_of_devices))) + AUDIO_LOG_WARN("update voicecall route from SAVED return 0x%x", audio_ret); + } + } + } + __reset_saved_route_infos(ah); + } else { + AUDIO_LOG_WARN("undefined route option"); + } + + return audio_ret; +} diff --git a/tizen-audio-stream.c b/tizen-audio-stream.c new file mode 100644 index 0000000..c60a443 --- /dev/null +++ b/tizen-audio-stream.c @@ -0,0 +1,54 @@ +/* + * audio-hal + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "tizen-audio-internal.h" + +audio_return_t _audio_stream_init(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + return AUDIO_RET_OK; +} + +audio_return_t _audio_stream_deinit(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + return AUDIO_RET_OK; +} + +audio_return_t audio_notify_stream_connection_changed(void *audio_handle, audio_stream_info_t *info, uint32_t is_connected) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info->role, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL((info->direction <= AUDIO_DIRECTION_OUT), AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("role:%s, direction:%u, idx:%u, is_connected:%d", info->role, info->direction, info->idx, is_connected); + + return audio_ret; +} + diff --git a/tizen-audio-util.c b/tizen-audio-util.c new file mode 100644 index 0000000..6ab4aa0 --- /dev/null +++ b/tizen-audio-util.c @@ -0,0 +1,80 @@ +/* + * audio-hal + * + * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "tizen-audio-internal.h" + +/* ------ dump helper -------- */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +dump_data_t* _audio_dump_new(int length) +{ + dump_data_t* dump = NULL; + + if ((dump = malloc(sizeof(dump_data_t)))) { + memset(dump, 0, sizeof(dump_data_t)); + if ((dump->strbuf = malloc(length))) { + dump->p = &dump->strbuf[0]; + dump->left = length; + } else { + free(dump); + dump = NULL; + } + } + + return dump; +} + +void _audio_dump_add_str(dump_data_t *dump, const char *fmt, ...) +{ + int len; + va_list ap; + + if (!dump) + return; + + va_start(ap, fmt); + len = vsnprintf(dump->p, dump->left, fmt, ap); + va_end(ap); + + dump->p += MAX(0, len); + dump->left -= MAX(0, len); +} + +char* _audio_dump_get_str(dump_data_t *dump) +{ + return (dump) ? dump->strbuf : NULL; +} + +void _audio_dump_free(dump_data_t *dump) +{ + if (dump) { + if (dump->strbuf) + free(dump->strbuf); + free(dump); + } +} +/* ------ dump helper -------- */ diff --git a/tizen-audio-volume.c b/tizen-audio-volume.c new file mode 100644 index 0000000..87c5523 --- /dev/null +++ b/tizen-audio-volume.c @@ -0,0 +1,415 @@ +/* + * audio-hal + * + * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <math.h> +#include <vconf.h> +#include <iniparser.h> + +#include "tizen-audio-internal.h" + +#define VOLUME_INI_DEFAULT_PATH SYSCONFDIR"/multimedia/mmfw_audio_volume.ini" /* SYSCONFDIR is defined at .spec */ +#define VOLUME_INI_TEMP_PATH "/opt/system/mmfw_audio_volume.ini" +#define VOLUME_VALUE_MAX (1.0f) +#define GAIN_VALUE_MAX (1.0f) + +uint32_t g_master_volume_level = 100; + +static const char *g_volume_vconf[AUDIO_VOLUME_TYPE_MAX] = { + "file/private/sound/volume/system", /* AUDIO_VOLUME_TYPE_SYSTEM */ + "file/private/sound/volume/notification", /* AUDIO_VOLUME_TYPE_NOTIFICATION */ + "file/private/sound/volume/alarm", /* AUDIO_VOLUME_TYPE_ALARM */ + "file/private/sound/volume/ringtone", /* AUDIO_VOLUME_TYPE_RINGTONE */ + "file/private/sound/volume/media", /* AUDIO_VOLUME_TYPE_MEDIA */ + "file/private/sound/volume/call", /* AUDIO_VOLUME_TYPE_CALL */ + "file/private/sound/volume/voip", /* AUDIO_VOLUME_TYPE_VOIP */ + "file/private/sound/volume/voice", /* AUDIO_VOLUME_TYPE_VOICE */ +}; + +static const char *__get_volume_type_string_by_idx(uint32_t vol_type_idx) +{ + switch (vol_type_idx) { + case AUDIO_VOLUME_TYPE_SYSTEM: return "system"; + case AUDIO_VOLUME_TYPE_NOTIFICATION: return "notification"; + case AUDIO_VOLUME_TYPE_ALARM: return "alarm"; + case AUDIO_VOLUME_TYPE_RINGTONE: return "ringtone"; + case AUDIO_VOLUME_TYPE_MEDIA: return "media"; + case AUDIO_VOLUME_TYPE_CALL: return "call"; + case AUDIO_VOLUME_TYPE_VOIP: return "voip"; + case AUDIO_VOLUME_TYPE_VOICE: return "voice"; + default: return "invalid"; + } +} + +static uint32_t __get_volume_idx_by_string_type(const char *vol_type) +{ + if (!strncmp(vol_type, "system", strlen(vol_type)) || !strncmp(vol_type, "0", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_SYSTEM; + else if (!strncmp(vol_type, "notification", strlen(vol_type)) || !strncmp(vol_type, "1", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_NOTIFICATION; + else if (!strncmp(vol_type, "alarm", strlen(vol_type)) || !strncmp(vol_type, "2", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_ALARM; + else if (!strncmp(vol_type, "ringtone", strlen(vol_type)) || !strncmp(vol_type, "3", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_RINGTONE; + else if (!strncmp(vol_type, "media", strlen(vol_type)) || !strncmp(vol_type, "4", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_MEDIA; + else if (!strncmp(vol_type, "call", strlen(vol_type)) || !strncmp(vol_type, "5", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_CALL; + else if (!strncmp(vol_type, "voip", strlen(vol_type)) || !strncmp(vol_type, "6", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_VOIP; + else if (!strncmp(vol_type, "voice", strlen(vol_type)) || !strncmp(vol_type, "7", strlen(vol_type))) + return AUDIO_VOLUME_TYPE_VOICE; + else + return AUDIO_VOLUME_TYPE_MEDIA; +} + +static const char *__get_gain_type_string_by_idx(uint32_t gain_type_idx) +{ + switch (gain_type_idx) { + case AUDIO_GAIN_TYPE_DEFAULT: return "default"; + case AUDIO_GAIN_TYPE_DIALER: return "dialer"; + case AUDIO_GAIN_TYPE_TOUCH: return "touch"; + case AUDIO_GAIN_TYPE_AF: return "af"; + case AUDIO_GAIN_TYPE_SHUTTER1: return "shutter1"; + case AUDIO_GAIN_TYPE_SHUTTER2: return "shutter2"; + case AUDIO_GAIN_TYPE_CAMCODING: return "camcording"; + case AUDIO_GAIN_TYPE_MIDI: return "midi"; + case AUDIO_GAIN_TYPE_BOOTING: return "booting"; + case AUDIO_GAIN_TYPE_VIDEO: return "video"; + case AUDIO_GAIN_TYPE_TTS: return "tts"; + default: return "invalid"; + } +} + +static void __dump_tb(audio_hal_t *ah) +{ + audio_volume_value_table_t *volume_value_table = ah->volume.volume_value_table; + uint32_t vol_type_idx, vol_level_idx, gain_type_idx; + const char *gain_type_str[] = { + "def", /* AUDIO_GAIN_TYPE_DEFAULT */ + "dial", /* AUDIO_GAIN_TYPE_DIALER */ + "touch", /* AUDIO_GAIN_TYPE_TOUCH */ + "af", /* AUDIO_GAIN_TYPE_AF */ + "shut1", /* AUDIO_GAIN_TYPE_SHUTTER1 */ + "shut2", /* AUDIO_GAIN_TYPE_SHUTTER2 */ + "cam", /* AUDIO_GAIN_TYPE_CAMCODING */ + "midi", /* AUDIO_GAIN_TYPE_MIDI */ + "boot", /* AUDIO_GAIN_TYPE_BOOTING */ + "video", /* AUDIO_GAIN_TYPE_VIDEO */ + "tts", /* AUDIO_GAIN_TYPE_TTS */ + }; + char dump_str[AUDIO_DUMP_STR_LEN], *dump_str_ptr; + + /* Dump volume table */ + AUDIO_LOG_INFO("<<<<< volume table >>>>>"); + + const char *table_str = "volumes"; + + AUDIO_LOG_INFO("<< %s >>", table_str); + + for (vol_type_idx = 0; vol_type_idx < AUDIO_VOLUME_TYPE_MAX; vol_type_idx++) { + const char *vol_type_str = __get_volume_type_string_by_idx(vol_type_idx); + + dump_str_ptr = &dump_str[0]; + memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str)); + snprintf(dump_str_ptr, 8, "%6s:", vol_type_str); + dump_str_ptr += strlen(dump_str_ptr); + + for (vol_level_idx = 0; vol_level_idx < ah->volume.volume_level_max[vol_type_idx]; vol_level_idx++) { + snprintf(dump_str_ptr, 6, "%01.2f ", volume_value_table->volume[vol_type_idx][vol_level_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + } + + volume_value_table = ah->volume.volume_value_table; + + /* Dump gain table */ + AUDIO_LOG_INFO("<<<<< gain table >>>>>"); + + dump_str_ptr = &dump_str[0]; + memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str)); + + snprintf(dump_str_ptr, 11, "%10s", " "); + dump_str_ptr += strlen(dump_str_ptr); + + for (gain_type_idx = 0; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) { + snprintf(dump_str_ptr, 7, "%5s ", gain_type_str[gain_type_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + + dump_str_ptr = &dump_str[0]; + memset(dump_str, 0x00, sizeof(char) * sizeof(dump_str)); + + snprintf(dump_str_ptr, 11, "%9s:", table_str); + dump_str_ptr += strlen(dump_str_ptr); + + for (gain_type_idx = 0; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) { + snprintf(dump_str_ptr, 7, "%01.3f ", volume_value_table->gain[gain_type_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + +} + +static audio_return_t __load_volume_value_table_from_ini(audio_hal_t *ah) +{ + dictionary * dict = NULL; + uint32_t vol_type_idx, vol_level_idx, gain_type_idx; + audio_volume_value_table_t *volume_value_table = ah->volume.volume_value_table; + int size = 0; + + if (access(VOLUME_INI_TEMP_PATH, F_OK) == 0) + dict = iniparser_load(VOLUME_INI_TEMP_PATH); + if (!dict) { + AUDIO_LOG_DEBUG("Use default volume&gain ini file"); + dict = iniparser_load(VOLUME_INI_DEFAULT_PATH); + if (!dict) { + AUDIO_LOG_WARN("Loading volume&gain table from ini file failed"); + return AUDIO_ERR_UNDEFINED; + } + } + + const char delimiter[] = ", "; + char *key, *list_str, *token, *ptr = NULL; + const char *table_str = "volumes"; + + /* Load volume table */ + for (vol_type_idx = 0; vol_type_idx < AUDIO_VOLUME_TYPE_MAX; vol_type_idx++) { + const char *vol_type_str = __get_volume_type_string_by_idx(vol_type_idx); + + ah->volume.volume_level_max[vol_type_idx] = 0; + size = strlen(table_str) + strlen(vol_type_str) + 2; + key = malloc(size); + if (key) { + snprintf(key, size, "%s:%s", table_str, vol_type_str); + list_str = iniparser_getstring(dict, key, NULL); + if (list_str) { + token = strtok_r(list_str, delimiter, &ptr); + while (token) { + /* convert dB volume to linear volume */ + double vol_value = 0.0f; + if (strncmp(token, "0", strlen(token))) + vol_value = pow(10.0, (atof(token) - 100) / 20.0); + volume_value_table->volume[vol_type_idx][ah->volume.volume_level_max[vol_type_idx]++] = vol_value; + token = strtok_r(NULL, delimiter, &ptr); + } + } else { + ah->volume.volume_level_max[vol_type_idx] = 1; + for (vol_level_idx = 0; vol_level_idx < AUDIO_VOLUME_LEVEL_MAX; vol_level_idx++) { + volume_value_table->volume[vol_type_idx][vol_level_idx] = VOLUME_VALUE_MAX; + } + } + free(key); + } + } + + /* Load gain table */ + volume_value_table->gain[AUDIO_GAIN_TYPE_DEFAULT] = GAIN_VALUE_MAX; + for (gain_type_idx = AUDIO_GAIN_TYPE_DEFAULT + 1; gain_type_idx < AUDIO_GAIN_TYPE_MAX; gain_type_idx++) { + const char *gain_type_str = __get_gain_type_string_by_idx(gain_type_idx); + + size = strlen(table_str) + strlen("gain") + strlen(gain_type_str) + 3; + key = malloc(size); + if (key) { + snprintf(key, size, "%s:gain_%s", table_str, gain_type_str); + token = iniparser_getstring(dict, key, NULL); + if (token) { + volume_value_table->gain[gain_type_idx] = atof(token); + } else { + volume_value_table->gain[gain_type_idx] = GAIN_VALUE_MAX; + } + free(key); + } else { + volume_value_table->gain[gain_type_idx] = GAIN_VALUE_MAX; + } + } + + iniparser_freedict(dict); + + __dump_tb(ah); + + return AUDIO_RET_OK; +} + +audio_return_t _audio_volume_init(audio_hal_t *ah) +{ + int i; + int val = 0; + audio_return_t audio_ret = AUDIO_RET_OK; + int init_value[AUDIO_VOLUME_TYPE_MAX] = { 9, 11, 7, 11, 7, 4, 4, 7 }; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + for (i = 0; i < AUDIO_VOLUME_TYPE_MAX; i++) { + ah->volume.volume_level[i] = init_value[i]; + } + + for (i = 0; i < AUDIO_VOLUME_TYPE_MAX; i++) { + /* Get volume value string from VCONF */ + if (vconf_get_int(g_volume_vconf[i], &val) < 0) { + AUDIO_LOG_ERROR("vconf_get_int(%s) failed", g_volume_vconf[i]); + continue; + } + + AUDIO_LOG_INFO("read vconf. %s = %d", g_volume_vconf[i], val); + ah->volume.volume_level[i] = val; + } + + if (!(ah->volume.volume_value_table = malloc(AUDIO_VOLUME_DEVICE_MAX * sizeof(audio_volume_value_table_t)))) { + AUDIO_LOG_ERROR("volume_value_table malloc failed"); + return AUDIO_ERR_RESOURCE; + } + + audio_ret = __load_volume_value_table_from_ini(ah); + if (audio_ret != AUDIO_RET_OK) { + AUDIO_LOG_ERROR("gain table load error"); + return AUDIO_ERR_UNDEFINED; + } + + return audio_ret; +} + +audio_return_t _audio_volume_deinit(audio_hal_t *ah) +{ + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + if (ah->volume.volume_value_table) { + free(ah->volume.volume_value_table); + ah->volume.volume_value_table = NULL; + } + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_volume_level_max(void *audio_handle, audio_volume_info_t *info, uint32_t *level) +{ + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(level, AUDIO_ERR_PARAMETER); + + /* Get max volume level by device & type */ + *level = ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)]; + + AUDIO_LOG_DEBUG("get_[%s] volume_level_max: %d", info->type, *level); + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t *level) +{ + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(level, AUDIO_ERR_PARAMETER); + + if (!strncmp(info->type, "master", strlen("master"))) { + *level = g_master_volume_level; + return AUDIO_RET_OK; + } + + *level = ah->volume.volume_level[__get_volume_idx_by_string_type(info->type)]; + + AUDIO_LOG_INFO("get [%s] volume_level: %d, direction(%d)", info->type, *level, info->direction); + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_volume_value(void *audio_handle, audio_volume_info_t *info, uint32_t level, double *value) +{ + audio_hal_t *ah = (audio_hal_t *)audio_handle; + audio_volume_value_table_t *volume_value_table; + char dump_str[AUDIO_DUMP_STR_LEN] = {0,}; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(value, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(ah->volume.volume_value_table, AUDIO_ERR_PARAMETER); + + /* Get basic volume by device & type & level */ + volume_value_table = ah->volume.volume_value_table; + if (ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)] < level) + *value = VOLUME_VALUE_MAX; + else + *value = volume_value_table->volume[__get_volume_idx_by_string_type(info->type)][level]; + *value *= volume_value_table->gain[AUDIO_GAIN_TYPE_DEFAULT]; /* need to fix getting gain via audio_info_t */ + + AUDIO_LOG_DEBUG("get_volume_value:%d(%s)=>%f %s", level, info->type, *value, &dump_str[0]); + + return AUDIO_RET_OK; +} + +audio_return_t audio_set_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t level) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + if (!strncmp(info->type, "master", strlen("master"))) { + g_master_volume_level = level; + return AUDIO_RET_OK; + } + AUDIO_RETURN_VAL_IF_FAIL((ah->volume.volume_level_max[__get_volume_idx_by_string_type(info->type)] >= level), AUDIO_ERR_PARAMETER); + + /* Update volume level */ + ah->volume.volume_level[__get_volume_idx_by_string_type(info->type)] = level; + AUDIO_LOG_INFO("set [%s] volume_level: %d, direction(%d)", info->type, level, info->direction); + + /* set mixer related to H/W volume if needed */ + + return audio_ret; +} + +audio_return_t audio_get_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t *mute) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(mute, AUDIO_ERR_PARAMETER); + + /* TODO. Not implemented */ + + return audio_ret; +} + +audio_return_t audio_set_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t mute) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER); + + /* TODO. Not implemented */ + + return audio_ret; +} diff --git a/tizen-audio.c b/tizen-audio.c new file mode 100644 index 0000000..c587ee0 --- /dev/null +++ b/tizen-audio.c @@ -0,0 +1,90 @@ +/* + * audio-hal + * + * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "tizen-audio-internal.h" + +audio_return_t audio_init(void **audio_handle) +{ + audio_hal_t *ah; + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER); + + if (!(ah = malloc(sizeof(audio_hal_t)))) { + AUDIO_LOG_ERROR("failed to malloc()"); + return AUDIO_ERR_RESOURCE; + } + + if ((ret = _audio_ctrl_init(ah))) { + AUDIO_LOG_ERROR("failed to _audio_ctrl_init(), ret(0x%x)", ret); + goto error_exit; + } + if ((ret = _audio_volume_init(ah))) { + AUDIO_LOG_ERROR("failed to _audio_volume_init(), ret(0x%x)", ret); + goto error_exit; + } + if ((ret = _audio_routing_init(ah))) { + AUDIO_LOG_ERROR("failed to _audio_routing_init(), ret(0x%x)", ret); + goto error_exit; + } + if ((ret = _audio_stream_init(ah))) { + AUDIO_LOG_ERROR("failed to _audio_stream_init(), ret(0x%x)", ret); + goto error_exit; + } + if ((ret = _audio_pcm_init(ah))) { + AUDIO_LOG_ERROR("failed to _audio_pcm_init(), ret(0x%x)", ret); + goto error_exit; + } + if ((ret = _audio_comm_init(ah))) { + AUDIO_LOG_ERROR("failed to _audio_comm_init(), ret(0x%x)", ret); + goto error_exit; + } + + *audio_handle = (void *)ah; + return AUDIO_RET_OK; + +error_exit: + if (ah) + free(ah); + + return ret; +} + +audio_return_t audio_deinit(void *audio_handle) +{ + audio_hal_t *ah = (audio_hal_t *)audio_handle; + + AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER); + + _audio_volume_deinit(ah); + _audio_routing_deinit(ah); + _audio_stream_deinit(ah); + _audio_pcm_deinit(ah); + _audio_comm_deinit(ah); + _audio_ctrl_deinit(ah); + + free(ah); + ah = NULL; + + return AUDIO_RET_OK; +}
\ No newline at end of file diff --git a/tizen-audio.h b/tizen-audio.h new file mode 100644 index 0000000..94efa03 --- /dev/null +++ b/tizen-audio.h @@ -0,0 +1,523 @@ +/* + * audio-hal + * + * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef footizenaudiofoo +#define footizenaudiofoo + +#include <stdint.h> + +/** + * @file tizen-audio.h + * @brief This file contains the Audio Hardware Abstraction Layer Interfaces. + */ + +/** + * @addtogroup TIZEN_AUDIO_HAL_MODULE + * @{ + */ + +/** + * @brief Enumeration for return codes. + * @since_tizen 3.0 + */ +typedef enum audio_return { + AUDIO_RET_OK = 0, + AUDIO_ERR_UNDEFINED = (int32_t)0x80001000, + AUDIO_ERR_RESOURCE = (int32_t)0x80001001, + AUDIO_ERR_PARAMETER = (int32_t)0x80001002, + AUDIO_ERR_IOCTL = (int32_t)0x80001003, + AUDIO_ERR_INVALID_STATE = (int32_t)0x80001004, + AUDIO_ERR_INTERNAL = (int32_t)0x80001005, + /* add new enemerator here */ + AUDIO_ERR_NOT_IMPLEMENTED = (int32_t)0x80001100, +} audio_return_t ; + +/** + * @brief Enumeration for audio direction. + * @since_tizen 3.0 + */ +typedef enum audio_direction { + AUDIO_DIRECTION_IN, /**< Capture */ + AUDIO_DIRECTION_OUT, /**< Playback */ +} audio_direction_t; + +/** + * @brief Device information including type, direction and id. + * @since_tizen 3.0 + */ +typedef struct device_info { + const char *type; + uint32_t direction; + uint32_t id; +} device_info_t; + +/** + * @brief Volume information including type, gain and direction. + * @since_tizen 3.0 + */ +typedef struct audio_volume_info { + const char *type; + const char *gain; + uint32_t direction; +} audio_volume_info_t ; + +/** + * @brief Route information including role and device. + * @since_tizen 3.0 + */ +typedef struct audio_route_info { + const char *role; + device_info_t *device_infos; + uint32_t num_of_devices; +} audio_route_info_t; + +/** + * @brief Route option including role, name and value. + * @since_tizen 3.0 + */ +typedef struct audio_route_option { + const char *role; + const char *name; + int32_t value; +} audio_route_option_t; + +/** + * @brief Stream information including role, direction and index. + * @since_tizen 3.0 + */ +typedef struct audio_stream_info { + const char *role; + uint32_t direction; + uint32_t idx; +} audio_stream_info_t ; + +/** + * @brief Called when audio hal implementation needs to send a message. + * @since_tizen 3.0 + * @param[in] name The message name + * @param[in] value The message value + * @param[in] user_data The user data passed from the callback registration function + * + * @see audio_add_message_cb() + * @see audio_remove_message_cb() + */ +typedef void (*message_cb)(const char *name, int value, void *user_data); + +/* Overall */ +typedef struct audio_interface { + /* Initialization & de-initialization */ + audio_return_t (*init)(void **audio_handle); + audio_return_t (*deinit)(void *audio_handle); + /* Volume */ + audio_return_t (*get_volume_level_max)(void *audio_handle, audio_volume_info_t *info, uint32_t *level); + audio_return_t (*get_volume_level)(void *audio_handle, audio_volume_info_t *info, uint32_t *level); + audio_return_t (*set_volume_level)(void *audio_handle, audio_volume_info_t *info, uint32_t level); + audio_return_t (*get_volume_value)(void *audio_handle, audio_volume_info_t *info, uint32_t level, double *value); + audio_return_t (*get_volume_mute)(void *audio_handle, audio_volume_info_t *info, uint32_t *mute); + audio_return_t (*set_volume_mute)(void *audio_handle, audio_volume_info_t *info, uint32_t mute); + /* Routing */ + audio_return_t (*update_route)(void *audio_handle, audio_route_info_t *info); + audio_return_t (*update_route_option)(void *audio_handle, audio_route_option_t *option); + /* Stream */ + audio_return_t (*notify_stream_connection_changed)(void *audio_handle, audio_stream_info_t *info, uint32_t is_connected); + /* PCM */ + audio_return_t (*pcm_open)(void *audio_handle, const char *card, const char *device, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods, void **pcm_handle); + audio_return_t (*pcm_start)(void *audio_handle, void *pcm_handle); + audio_return_t (*pcm_stop)(void *audio_handle, void *pcm_handle); + audio_return_t (*pcm_close)(void *audio_handle, void *pcm_handle); + audio_return_t (*pcm_avail)(void *audio_handle, void *pcm_handle, uint32_t *avail); + audio_return_t (*pcm_write)(void *audio_handle, void *pcm_handle, const void *buffer, uint32_t frames); + audio_return_t (*pcm_read)(void *audio_handle, void *pcm_handle, void *buffer, uint32_t frames); + audio_return_t (*pcm_get_fd)(void *audio_handle, void *pcm_handle, int *fd); + audio_return_t (*pcm_recover)(void *audio_handle, void *pcm_handle, int revents); + audio_return_t (*pcm_get_params)(void *audio_handle, void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods); + audio_return_t (*pcm_set_params)(void *audio_handle, void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods); + /* Message callback */ + audio_return_t (*add_message_cb)(void *audio_handle, message_cb callback, void *user_data); + audio_return_t (*remove_message_cb)(void *audio_handle, message_cb callback); +} audio_interface_t; + +/** + * @brief Initializes audio hal. + * @since_tizen 3.0 + * @param[out] audio_handle The audio hal handle + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_deinit() + */ +audio_return_t audio_init(void **audio_handle); + +/** + * @brief De-initializes audio hal. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_init() + */ +audio_return_t audio_deinit(void *audio_handle); + +/** + * @brief Gets the maximum volume level supported for a particular volume information. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] info The audio volume information + * @param[out] level The maximum volume level + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_set_volume_level() + * @see audio_get_volume_level() + * @see audio_get_volume_value() + */ +audio_return_t audio_get_volume_level_max(void *audio_handle, audio_volume_info_t *info, uint32_t *level); + +/** + * @brief Gets the volume level specified for a particular volume information. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] info The audio volume information + * @param[out] level The current volume level + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_set_volume_level() + * @see audio_get_volume_level_max() + * @see audio_get_volume_value() + */ +audio_return_t audio_get_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t *level); + +/** + * @brief Sets the volume level specified for a particular volume information. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] info The audio volume information + * @param[in] level The volume level to be set + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_get_volume_level() + * @see audio_get_volume_level_max() + * @see audio_get_volume_value() + */ +audio_return_t audio_set_volume_level(void *audio_handle, audio_volume_info_t *info, uint32_t level); + +/** + * @brief Gets the volume value specified for a particular volume information and level. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] info The audio volume information + * @param[in] level The volume level + * @param[out] value The volume value (range is from 0.0 to 1.0 inclusive, 1.0 = 100%) + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_set_volume_level() + * @see audio_get_volume_level() + * @see audio_get_volume_level_max() + */ +audio_return_t audio_get_volume_value(void *audio_handle, audio_volume_info_t *info, uint32_t level, double *value); + +/** + * @brief Gets the volume mute specified for a particular volume information. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] info The audio volume information + * @param[out] mute The volume mute state : (@c 0 = unmute, @c 1 = mute) + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_set_volume_mute() + */ +audio_return_t audio_get_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t *mute); + +/** + * @brief Sets the volume mute specified for a particular volume information. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] info The audio volume information + * @param[in] mute The volume mute state to be set : (@c 0 = unmute, @c 1 = mute) + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_get_volume_mute() + */ +audio_return_t audio_set_volume_mute(void *audio_handle, audio_volume_info_t *info, uint32_t mute); + +/** + * @brief Updates the audio routing according to audio route information. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] info The audio route information including role and devices + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_update_route_option() + */ +audio_return_t audio_update_route(void *audio_handle, audio_route_info_t *info); + +/** + * @brief Updates audio routing option according to audio route option. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] option The option that can be used for audio routing including role, name and value + * + * @remarks This option can be used for audio routing.\n + * It is recommended to apply this option for routing per each role. + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_update_route() + */ +audio_return_t audio_update_route_option(void *audio_handle, audio_route_option_t *option); + +/** + * @brief Gets notified when a stream is connected and disconnected. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] info The stream information including role, direction, index + * @param[in] is_connected The connection state of this stream (@c true = connected, @c false = disconnected) + * + * @remarks This information can be used for audio routing, volume controls and so on. + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + */ +audio_return_t audio_notify_stream_connection_changed(void *audio_handle, audio_stream_info_t *info, uint32_t is_connected); + +/** + * @brief Opens a PCM device. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] card The card of PCM + * @param[in] device The device of PCM + * @param[in] direction The direction of PCM + * @param[in] sample_spec The sample specification + * @param[in] period_size The period size + * @param[in] periods The periods + * @param[out] pcm_handle The PCM handle + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_pcm_close() + */ +audio_return_t audio_pcm_open(void *audio_handle, const char *card, const char *device, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods, void **pcm_handle); + +/** + * @brief Starts a PCM device. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] pcm_handle The PCM handle to be started + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_pcm_avail() + * @see audio_pcm_write() + * @see audio_pcm_read() + * @see audio_pcm_stop() + * @see audio_pcm_recover() + */ +audio_return_t audio_pcm_start(void *audio_handle, void *pcm_handle); + +/** + * @brief Stops a PCM device. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] pcm_handle The PCM handle to be stopped + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_pcm_start() + */ +audio_return_t audio_pcm_stop(void *audio_handle, void *pcm_handle); + +/** + * @brief Closes a PCM device. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] pcm_handle The PCM handle to be closed + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_pcm_open() + */ +audio_return_t audio_pcm_close(void *audio_handle, void *pcm_handle); + +/** + * @brief Gets available number of frames. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] pcm_handle The PCM handle + * @param[out] avail The available number of frames + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_pcm_write() + * @see audio_pcm_read() + */ +audio_return_t audio_pcm_avail(void *audio_handle, void *pcm_handle, uint32_t *avail); + +/** + * @brief Writes frames to a PCM device. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] pcm_handle The PCM handle + * @param[in] buffer The buffer containing frames + * @param[in] frames The number of frames to be written + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_pcm_avail() + * @see audio_pcm_recover() + */ +audio_return_t audio_pcm_write(void *audio_handle, void *pcm_handle, const void *buffer, uint32_t frames); + +/** + * @brief Reads frames from a PCM device. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] pcm_handle The PCM handle + * @param[out] buffer The buffer containing frames + * @param[in] frames The number of frames to be read + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_pcm_avail() + * @see audio_pcm_recover() + */ +audio_return_t audio_pcm_read(void *audio_handle, void *pcm_handle, void *buffer, uint32_t frames); + +/** + * @brief Gets poll descriptor for a PCM handle. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] pcm_handle The PCM handle + * @param[out] fd The poll descriptor + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_pcm_open() + * @see audio_pcm_recover() + */ +audio_return_t audio_pcm_get_fd(void *audio_handle, void *pcm_handle, int *fd); + +/** + * @brief Recovers the PCM state. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] pcm_handle The PCM handle + * @param[in] revents The returned event from pollfd + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_pcm_start() + * @see audio_pcm_write() + * @see audio_pcm_read() + * @see audio_pcm_get_fd() + */ +audio_return_t audio_pcm_recover(void *audio_handle, void *pcm_handle, int revents); + +/** + * @brief Gets parameters of a PCM device. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] pcm_handle The PCM handle + * @param[in] direction The direction of PCM + * @param[out] sample_spec The sample specification + * @param[out] period_size The period size + * @param[out] periods The periods + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_pcm_set_params() + */ +audio_return_t audio_pcm_get_params(void *audio_handle, void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods); + +/** + * @brief Sets hardware and software parameters of a PCM device. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] pcm_handle The PCM handle + * @param[in] direction The direction of PCM + * @param[in] sample_spec The sample specification + * @param[in] period_size The period size + * @param[in] periods The periods + * + * @return @c 0 on success, + * otherwise a negative error value + * @retval #AUDIO_RET_OK Success + * @see audio_pcm_set_params() + */ +audio_return_t audio_pcm_set_params(void *audio_handle, void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods); + +/** + * @brief Adds the message callback function. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] message_cb The message callback function + * @param[in] user_data The user data passed to the callback function + * + * @see message_cb() + * @see audio_remove_message_cb() + */ +audio_return_t audio_add_message_cb(void *audio_handle, message_cb callback, void *user_data); + +/** + * @brief Removes the message callback function. + * @since_tizen 3.0 + * @param[in] audio_handle The audio hal handle + * @param[in] message_cb The message callback function to be removed + * + * @see message_cb() + * @see audio_add_message_cb() + */ +audio_return_t audio_remove_message_cb(void *audio_handle, message_cb callback); + +/** +* @} +*/ + +/** +* @} +*/ + +#endif |