diff options
-rw-r--r-- | LICENSE.Apache-2.0 | 202 | ||||
-rw-r--r-- | Makefile.am | 14 | ||||
-rw-r--r-- | NOTICE | 3 | ||||
-rwxr-xr-x | autogen.sh | 10 | ||||
-rw-r--r-- | configure.ac | 55 | ||||
-rw-r--r-- | packaging/audio-hal-sc7727.spec | 57 | ||||
-rw-r--r-- | tizen-audio-device.c | 698 | ||||
-rw-r--r-- | tizen-audio-internal.h | 380 | ||||
-rw-r--r-- | tizen-audio-modem.c | 1073 | ||||
-rw-r--r-- | tizen-audio-session.c | 245 | ||||
-rw-r--r-- | tizen-audio-stream.c | 638 | ||||
-rw-r--r-- | tizen-audio-ucm.c | 466 | ||||
-rw-r--r-- | tizen-audio-util.c | 357 | ||||
-rw-r--r-- | tizen-audio.c | 131 | ||||
-rw-r--r-- | tizen-audio.h | 279 | ||||
-rw-r--r-- | vb_control_parameters.h | 116 |
16 files changed, 4724 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..46b22e4 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,14 @@ +lib_LTLIBRARIES = libtizen-audio.la + +libtizen_audio_la_SOURCES = tizen-audio.c \ + tizen-audio-session.c \ + tizen-audio-device.c \ + tizen-audio-stream.c \ + tizen-audio-ucm.c \ + tizen-audio-modem.c \ + tizen-audio-util.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) $(TAPI_LIBS) -lexpat +libtizen_audio_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) $(VCONF_CFLAGS) $(DLOG_CFLAGS) $(INIPARSER_CFLAGS) $(TAPI_CFLAGS) +libtizen_audio_la_CFLAGS += -DUSE_DLOG + @@ -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/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..e6257d8 --- /dev/null +++ b/configure.ac @@ -0,0 +1,55 @@ +AC_PREREQ([2.67]) + +AC_INIT([audio-hal-sc7727], [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) + +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) + +PKG_CHECK_MODULES(TAPI, tapi) +AC_SUBST(TAPI_CFLAGS) +AC_SUBST(TAPI_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-sc7727.spec b/packaging/audio-hal-sc7727.spec new file mode 100644 index 0000000..cc0073c --- /dev/null +++ b/packaging/audio-hal-sc7727.spec @@ -0,0 +1,57 @@ +Name: audio-hal-sc7727 +Summary: TIZEN Audio HAL for SC7727 +Version: 0.3.17 +Release: 0 +Group: System/Libraries +License: Apache-2.0 +URL: http://tizen.org +Source0: audio-hal-sc7727-%{version}.tar.gz +ExclusiveArch: %arm +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(iniparser) +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(alsa) +BuildRequires: pkgconfig(tapi) +BuildRequires: expat-devel +Provides: libtizen-audio.so + +%if ("%{tizen_target_name}" != "Z130H" && "%{tizen_target_name}" != "Z300H") +ExcludeArch: %{arm} +%endif + +%description +TIZEN Audio HAL for SC7727 + +%prep +%setup -q -n %{name}-%{version} + +%build +export CFLAGS="$CFLAGS -DTIZEN_DEBUG_ENABLE" +export CXXFLAGS="$CXXFLAGS ?“DTIZEN_DEBUG_ENABLE" +export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE" + +%if "%{tizen_target_name}" == "Z300H" +export CFLAGS+=" -DUSE_FMRADIO_V4L2_SPRD" +%endif + +%autogen +%configure + +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}/usr/share/license +cp LICENSE.Apache-2.0 %{buildroot}/usr/share/license/%{name} +%make_install + +%post +/sbin/ldconfig + +%postun +/sbin/ldconfig + +%files +%defattr(-,root,root,-) +/usr/lib/libtizen-audio.so +/usr/share/license/%{name} diff --git a/tizen-audio-device.c b/tizen-audio-device.c new file mode 100644 index 0000000..ef0a29c --- /dev/null +++ b/tizen-audio-device.c @@ -0,0 +1,698 @@ +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 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" + +audio_return_t _audio_device_init (audio_mgr_t *am) +{ + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + am->device.active_in = AUDIO_DEVICE_IN_NONE; + am->device.active_out = AUDIO_DEVICE_OUT_NONE; + am->device.route_flag = AUDIO_ROUTE_FLAG_NONE; + am->device.pcm_in = NULL; + am->device.pcm_out = NULL; + pthread_mutex_init(&am->device.pcm_lock, NULL); + am->device.pcm_count = 0; +#ifdef USE_FMRADIO_V4L2_SPRD + am->device.fmradio_pcm_out = NULL; +#endif + + return AUDIO_RET_OK; +} + +audio_return_t _audio_device_deinit (audio_mgr_t *am) +{ + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + return AUDIO_RET_OK; +} + +#ifdef USE_FMRADIO_V4L2_SPRD +audio_return_t _fmradio_pcm_open (audio_mgr_t *am, uint32_t device_in, uint32_t device_out, uint32_t route_flag) +{ + audio_return_t ret = AUDIO_RET_OK; + const char *device_name = NULL; + audio_pcm_sample_spec_t sample_spec; + uint8_t use_mmap = 0; + + sample_spec.rate = 44100; + sample_spec.channels = 2; + sample_spec.format = SND_PCM_FORMAT_S16_LE; + + /* open playback device */ + _audio_ucm_get_device_name(am, AUDIO_USE_CASE_VERB_FMRADIO, AUDIO_DIRECTION_OUT, &device_name); + + if (device_name) { + if((ret = audio_alsa_pcm_open((void *)am, (void **)&am->device.fmradio_pcm_out, (char *)device_name, AUDIO_DIRECTION_OUT, 0)) != AUDIO_RET_OK) { + AUDIO_LOG_ERROR("[%s] out pcm_open failed", device_name); + goto error_exit; + } + AUDIO_LOG_INFO("[%s] out pcm_open success:%p", device_name, am->device.fmradio_pcm_out); + + if ((ret = _audio_pcm_set_hw_params(am->device.fmradio_pcm_out, &sample_spec, &use_mmap, NULL, NULL)) != AUDIO_RET_OK) { + AUDIO_LOG_ERROR("[%s] out __set_pcm_hw_params failed", device_name); + audio_alsa_pcm_close((void *)am, am->device.fmradio_pcm_out); + am->device.fmradio_pcm_out = NULL; + goto error_exit; + } + free((void *)device_name); + } + + return AUDIO_RET_OK; + +error_exit: + if (device_name) + free((void *)device_name); + + return AUDIO_ERR_RESOURCE; +} + +audio_return_t _fmradio_pcm_close (audio_mgr_t *am) +{ + /* close playback device */ + if (am->device.fmradio_pcm_out) { + audio_alsa_pcm_close((void *)am, am->device.fmradio_pcm_out); + am->device.fmradio_pcm_out = NULL; + } + return AUDIO_RET_OK; +} +#endif + +static void __free_device_info (audio_device_info_t *device_info) +{ + if (device_info->name) { + free(device_info->name); + device_info->name = NULL; + } + if (device_info->api == AUDIO_DEVICE_API_ALSA) { + if (device_info->alsa.card_name) { + free(device_info->alsa.card_name); + device_info->alsa.card_name = NULL; + } + } +} + +static void __load_n_open_device_with_params (audio_mgr_t *am, audio_device_info_t *device_info, int load_only) +{ + audio_device_param_info_t params[AUDIO_DEVICE_PARAM_MAX]; + int dev_param_count = 0; + + AUDIO_RETURN_IF_FAIL(am); + AUDIO_RETURN_IF_FAIL(device_info); + AUDIO_RETURN_IF_FAIL(am->cb_intf.load_device); + AUDIO_RETURN_IF_FAIL(am->cb_intf.open_device); + + memset(¶ms[0], 0, sizeof(audio_device_param_info_t) * AUDIO_DEVICE_PARAM_MAX); + + if (device_info->api == AUDIO_DEVICE_API_ALSA) { + /* 6 = "hw: ,idx */ + if ((device_info->name = malloc(strlen(device_info->alsa.card_name) + 6)) != NULL){ + snprintf(device_info->name, strlen(device_info->alsa.card_name) + 6, + "hw:%s,%d", device_info->alsa.card_name, device_info->alsa.device_idx); + } + if (device_info->direction == AUDIO_DIRECTION_OUT) { + /* ALSA playback */ + if (!strncmp(device_info->alsa.card_name, ALSA_DEFAULT_CARD, strlen(ALSA_DEFAULT_CARD))) { + /* default card */ + switch (device_info->alsa.device_idx) { + /* default device */ + case 0: + device_info->is_default_device = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_SUSPEND_TIMEOUT; + params[dev_param_count++].u32_v = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_TSCHED_BUF_SIZE; + params[dev_param_count++].u32_v = 35280; + AUDIO_LOG_INFO("HiFi Device"); + break; + case 1: + /* VOICE PCM. */ + break; +#ifdef USE_FMRADIO_V4L2_SPRD + /* FM device */ + case 4: + AUDIO_LOG_INFO("Digital FM device"); + break; +#endif + default: + AUDIO_LOG_INFO("Unknown Playback Device"); + break; + } + } else if (!strncmp(device_info->alsa.card_name, ALSA_SAUDIOVOIP_CARD, strlen(ALSA_SAUDIOVOIP_CARD))) { + device_info->is_default_device = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_CHANNELS; + params[dev_param_count++].u32_v = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_FRAGMENT_SIZE; + params[dev_param_count++].u32_v = 640; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_FRAGMENT_NB; + params[dev_param_count++].u32_v = 2; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_SAMPLERATE; + params[dev_param_count++].u32_v = 8000; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_ALTERNATE_RATE; + params[dev_param_count++].u32_v = 8000; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_USE_TSCHED; + params[dev_param_count++].u32_v = 0; + } else if (!strncmp(device_info->alsa.card_name, ALSA_VIRTUAL_CARD, strlen(ALSA_VIRTUAL_CARD))) { + /* virtual card */ + device_info->is_default_device = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_SAMPLERATE; + params[dev_param_count++].u32_v = 16000; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_CHANNELS; + params[dev_param_count++].u32_v = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_USE_TSCHED; + params[dev_param_count++].u32_v = 0; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_SUSPEND_TIMEOUT; + params[dev_param_count++].u32_v = 0; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_ALTERNATE_RATE; + params[dev_param_count++].u32_v = 16000; + } + } else if (device_info->direction == AUDIO_DIRECTION_IN) { + /* ALSA capture */ + if (!strncmp(device_info->alsa.card_name, ALSA_DEFAULT_CARD, strlen(ALSA_DEFAULT_CARD))) { + /* default card */ + switch (device_info->alsa.device_idx) { + /* default device */ + case 0: + device_info->is_default_device = 1; + /* use mmap */ + params[dev_param_count].param = AUDIO_DEVICE_PARAM_USE_MMAP; + params[dev_param_count++].u32_v = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_SAMPLERATE; + params[dev_param_count++].u32_v = (am->session.is_radio_on) ? 44100 : 48000; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_ALTERNATE_RATE; + params[dev_param_count++].u32_v = (am->session.is_radio_on) ? 44100 : 48000; + break; + default: + AUDIO_LOG_INFO("Unknown Capture Device"); + break; + } + } else if (!strncmp(device_info->alsa.card_name, ALSA_SAUDIOVOIP_CARD, strlen(ALSA_SAUDIOVOIP_CARD))) { + device_info->is_default_device = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_SAMPLERATE; + params[dev_param_count++].u32_v = 8000; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_CHANNELS; + params[dev_param_count++].u32_v = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_FRAGMENT_SIZE; + params[dev_param_count++].u32_v = 640; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_FRAGMENT_NB; + params[dev_param_count++].u32_v = 8; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_USE_TSCHED; + params[dev_param_count++].u32_v = 0; + } else if (!strncmp(device_info->alsa.card_name, ALSA_VIRTUAL_CARD, strlen(ALSA_VIRTUAL_CARD))) { + /* virtual card */ + device_info->is_default_device = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_SAMPLERATE; + params[dev_param_count++].u32_v = 16000; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_CHANNELS; + params[dev_param_count++].u32_v = 1; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_USE_TSCHED; + params[dev_param_count++].u32_v = 0; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_SUSPEND_TIMEOUT; + params[dev_param_count++].u32_v = 0; + params[dev_param_count].param = AUDIO_DEVICE_PARAM_ALTERNATE_RATE; + params[dev_param_count++].u32_v = 16000; + } + } + + AUDIO_LOG_INFO("open alsa %s device hw:%s,%d", (device_info->direction == AUDIO_DIRECTION_IN) ? "capture" : "playback", + device_info->alsa.card_name, device_info->alsa.device_idx); + } + + if (load_only) { + am->cb_intf.load_device(am->platform_data, device_info, ¶ms[0]); + } else { + am->cb_intf.open_device(am->platform_data, device_info, ¶ms[0]); + } +} + +void _load_n_open_device_from_ucm (audio_mgr_t *am, const char *verb, int load_only) +{ + audio_device_info_t device_info_list[AUDIO_DEVICE_INFO_LIST_MAX]; + int i, dev_info_count = 0; + + AUDIO_RETURN_IF_FAIL(am); + + memset((void *)&device_info_list[0], 0, sizeof(audio_device_info_t) * AUDIO_DEVICE_INFO_LIST_MAX); + /* fill device params & open device */ + dev_info_count = _audio_ucm_fill_device_info_list(am, &device_info_list[0], verb); + for (i = 0; i < dev_info_count; i++) { + __load_n_open_device_with_params(am, &device_info_list[i], load_only); + __free_device_info(&device_info_list[i]); + } +} + +void _close_n_unload_device_from_ucm (audio_mgr_t *am, const char *verb, int force_unload) +{ + audio_device_info_t device_info_list[AUDIO_DEVICE_INFO_LIST_MAX]; + int i, dev_info_count = 0; + + AUDIO_RETURN_IF_FAIL(am); + AUDIO_RETURN_IF_FAIL(am->cb_intf.close_device); + AUDIO_RETURN_IF_FAIL(am->cb_intf.unload_device); + + memset((void *)&device_info_list[0], 0, sizeof(audio_device_info_t) * AUDIO_DEVICE_INFO_LIST_MAX); + /* fill device params & open device */ + dev_info_count = _audio_ucm_fill_device_info_list(am, &device_info_list[0], verb); + for (i = 0; i < dev_info_count; i++) { + if (!force_unload) { + am->cb_intf.close_device(am->platform_data, &device_info_list[i]); + } else { + am->cb_intf.unload_device(am->platform_data, &device_info_list[i]); + } + __free_device_info(&device_info_list[i]); + } +} + +void _open_virtual_device (audio_mgr_t *am) +{ + audio_device_info_t device_info; + + AUDIO_RETURN_IF_FAIL(am); + + if (am->cb_intf.open_device) { + /* open Rx device */ + memset(&device_info, 0x00, sizeof(audio_device_info_t)); + device_info.api = AUDIO_DEVICE_API_ALSA; + device_info.alsa.device_idx = 0; + device_info.alsa.card_name = strdup(ALSA_VIRTUAL_CARD); + device_info.alsa.card_idx = snd_card_get_index(device_info.alsa.card_name); + device_info.direction = AUDIO_DIRECTION_OUT; + __load_n_open_device_with_params(am, &device_info, 0); + __free_device_info(&device_info); + /* open Tx device */ + memset(&device_info, 0x00, sizeof(audio_device_info_t)); + device_info.api = AUDIO_DEVICE_API_ALSA; + device_info.alsa.device_idx = 0; + device_info.alsa.card_name = strdup(ALSA_VIRTUAL_CARD); + device_info.alsa.card_idx = snd_card_get_index(device_info.alsa.card_name); + device_info.direction = AUDIO_DIRECTION_IN; + __load_n_open_device_with_params(am, &device_info, 0); + __free_device_info(&device_info); + } +} + +static audio_return_t __set_route_ap_playback_capture (audio_mgr_t *am, uint32_t device_in, uint32_t device_out, uint32_t route_flag) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + int dev_idx = 0; + int mod_idx = 0; + const char *verb = NULL; + const char *devices[MAX_DEVICES] = {NULL,}; + const char *modifiers[MAX_MODIFIERS] = {NULL,}; + + verb = AUDIO_USE_CASE_VERB_HIFI; + + if (route_flag & AUDIO_ROUTE_FLAG_MUTE_POLICY) { + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET; + } else if (route_flag & AUDIO_ROUTE_FLAG_DUAL_OUT) { + devices[dev_idx++] = AUDIO_USE_CASE_DEV_SPEAKER; + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET; + if (device_out == AUDIO_DEVICE_OUT_MIRRORING) { + AUDIO_LOG_INFO("Skip WFD enable during DUAL path"); + } + } else { + switch (device_out) { + case AUDIO_DEVICE_OUT_SPEAKER: + if (am->session.is_radio_on == 1 && am->session.is_recording != 1) { + modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_FM_SPEAKER; + } else { + devices[dev_idx++] = AUDIO_USE_CASE_DEV_SPEAKER; + } + break; + case AUDIO_DEVICE_OUT_RECEIVER: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HANDSET; + break; + case AUDIO_DEVICE_OUT_WIRED_ACCESSORY: + if (am->session.is_radio_on == 1) { + modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_FM_HEADSET; + } else { + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET; + } + break; + /* even BT SCO is opened by call app, we cannot use BT SCO on HiFi verb */ + case AUDIO_DEVICE_OUT_BT_SCO: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_SPEAKER; + break; + default: + break; + } + } + + if (am->session.is_radio_on == 0 || am->session.is_recording == 1) { + switch (device_in) { + case AUDIO_DEVICE_IN_MIC: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_MAIN_MIC; + break; + case AUDIO_DEVICE_IN_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET_MIC; + break; + /* even BT SCO is opened by call app, we cannot use BT SCO on HiFi verb */ + case AUDIO_DEVICE_IN_BT_SCO: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_MAIN_MIC; + break; + default: + break; + } + } + + if (am->session.is_radio_on == 1) { + /* FM radio don't want to route BT headset */ + if(device_out != AUDIO_DEVICE_OUT_SPEAKER && device_out != AUDIO_DEVICE_OUT_WIRED_ACCESSORY) { + AUDIO_LOG_INFO("not supported devices. device_out(%d)", device_out); + audio_ret = AUDIO_ERR_PARAMETER; + return audio_ret; + } + devices[dev_idx++] = AUDIO_USE_CASE_DEV_FMRADIO_CORE; + } + + /* TODO. Handle voice recognition when seperate devices are available */ + audio_ret = _audio_ucm_update_use_case(am, verb, devices, modifiers); + if (AUDIO_IS_ERROR(audio_ret)) { + return audio_ret; + } + return AUDIO_RET_OK; +} + +audio_return_t _set_route_voicecall (audio_mgr_t *am, uint32_t device_in, uint32_t device_out, uint32_t route_flag) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + int dev_idx = 0; + const char *verb = NULL; + const char *devices[MAX_DEVICES] = {NULL,}; + + verb = AUDIO_USE_CASE_VERB_VOICECALL; + + switch (device_out) { + case AUDIO_DEVICE_OUT_SPEAKER: + /* FIXME: WB handling is needed */ + devices[dev_idx++] = AUDIO_USE_CASE_DEV_SPEAKER; + break; + case AUDIO_DEVICE_OUT_RECEIVER: + /* FIXME: WB handling is needed */ + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HANDSET; + break; + case AUDIO_DEVICE_OUT_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET; + break; + case AUDIO_DEVICE_OUT_BT_SCO: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_BT_HEADSET; + break; + default: + break; + } + + switch (device_in) { + case AUDIO_DEVICE_IN_MIC: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_MAIN_MIC; + break; + case AUDIO_DEVICE_IN_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET_MIC; + break; + default: + break; + } + + /* FIXME. Get network info and configure rate in pcm device */ + audio_ret = _audio_ucm_update_use_case(am, verb, devices, NULL); + + return audio_ret; +} + +static audio_return_t __set_route_voip (audio_mgr_t *am, uint32_t device_in, uint32_t device_out, uint32_t route_flag) +{ + int dev_idx = 0; + const char *verb = NULL; + const char *devices[MAX_DEVICES] = {NULL,}; + + verb = AUDIO_USE_CASE_VERB_HIFI; /* Modify later to use VIRTUALAUDIO to enable echo cancellation */ + + switch (device_out) { + case AUDIO_DEVICE_OUT_SPEAKER: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_SPEAKER; + break; + case AUDIO_DEVICE_OUT_RECEIVER: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HANDSET; + break; + case AUDIO_DEVICE_OUT_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET; + break; + case AUDIO_DEVICE_OUT_BT_SCO: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_BT_HEADSET; + break; + default: + break; + } + + switch (device_in) { + case AUDIO_DEVICE_IN_MIC: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_MAIN_MIC; + break; + case AUDIO_DEVICE_IN_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET_MIC; + break; + case AUDIO_DEVICE_IN_BT_SCO: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_BT_MIC; + break; + default: + break; + } + + return _audio_ucm_update_use_case(am, verb, devices, NULL); +} + +audio_return_t _set_route_videocall (audio_mgr_t *am, uint32_t device_in, uint32_t device_out, uint32_t route_flag) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + int dev_idx = 0; + const char *devices[MAX_DEVICES] = {NULL,}; + + switch (device_out) { + case AUDIO_DEVICE_OUT_SPEAKER: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_SPEAKER; + break; + case AUDIO_DEVICE_OUT_RECEIVER: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HANDSET; + break; + case AUDIO_DEVICE_OUT_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET; + break; + case AUDIO_DEVICE_OUT_BT_SCO: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_BT_HEADSET; + break; + default: + break; + } + + switch (device_in) { + case AUDIO_DEVICE_IN_MIC: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_MAIN_MIC; + break; + case AUDIO_DEVICE_IN_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET_MIC; + break; + default: + break; + } + audio_ret = _audio_ucm_update_use_case(am, AUDIO_USE_CASE_VERB_VIDEOCALL, devices, NULL); + + return audio_ret; +} + +#ifdef USE_FMRADIO_V4L2_SPRD +audio_return_t __set_route_fmradio (audio_mgr_t *am, uint32_t device_in, uint32_t device_out, uint32_t route_flag) +{ + int dev_idx = 0; + const char *verb = NULL; + const char *devices[MAX_DEVICES] = {NULL,}; + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + verb = AUDIO_USE_CASE_VERB_FMRADIO; + switch (device_out) { + case AUDIO_DEVICE_OUT_SPEAKER: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_SPEAKER; + break; + case AUDIO_DEVICE_OUT_WIRED_ACCESSORY: + devices[dev_idx++] = AUDIO_USE_CASE_DEV_HEADSET; + _set_volume_level_fmradio(am,0); + break; + default: + AUDIO_LOG_INFO("not supported devices. device_out(%d)", device_out); + return AUDIO_ERR_NOT_IMPLEMENTED; + break; + } + + audio_ret = _audio_ucm_update_use_case(am, verb, devices, NULL); + if (AUDIO_IS_ERROR(audio_ret)) { + return audio_ret; + } + + return audio_ret; +} +#endif + +audio_return_t _reset_route (audio_mgr_t *am, int need_inactive) +{ + const char *devices[MAX_DEVICES] = {NULL,}; + const char *modifiers[MAX_MODIFIERS] = {NULL,}; + + if(need_inactive) { + _audio_ucm_update_use_case(am, AUDIO_USE_CASE_VERB_INACTIVE, devices, modifiers); + } + _audio_ucm_update_use_case(am, AUDIO_USE_CASE_VERB_HIFI, devices, modifiers); + __set_route_ap_playback_capture(am, am->device.active_in, am->device.active_out, 0); + + return AUDIO_RET_OK; +} + +audio_return_t audio_set_route (void *userdata, uint32_t session, uint32_t subsession, uint32_t device_in, uint32_t device_out, uint32_t route_flag) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + int i, dev_info_count = 0; + audio_device_info_t device_info_list[AUDIO_DEVICE_INFO_LIST_MAX]; + + am->device.active_in = device_in; + am->device.active_out = device_out; + am->device.route_flag = route_flag; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("session:%d,%d in:%d out:%d flag:0x%x", session, subsession, device_in, device_out, route_flag); + + if ((session == AUDIO_SESSION_VOICECALL) && (subsession == AUDIO_SUBSESSION_VOICE)) { + if (_audio_modem_is_call_connected(am)) { + audio_ret = _set_route_voicecall(am, device_in, device_out, route_flag); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set voicecall route return 0x%x", audio_ret); + } + } else { + AUDIO_LOG_DEBUG("skip route before call connected"); + return AUDIO_RET_OK; + } + } else if ((session == AUDIO_SESSION_VOIP) && (subsession == AUDIO_SUBSESSION_VOICE)) { + audio_ret = __set_route_voip(am, device_in, device_out, route_flag); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set voip route return 0x%x", audio_ret); + } + } else if ((session == AUDIO_SESSION_VIDEOCALL) && (subsession == AUDIO_SUBSESSION_VOICE) && _audio_modem_is_call_connected(am)) { + audio_ret = _set_route_videocall(am, device_in, device_out, route_flag); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set videocall route return 0x%x", audio_ret); + } + } +#ifdef USE_FMRADIO_V4L2_SPRD + else if(session == AUDIO_SESSION_FMRADIO) { + audio_ret = __set_route_fmradio(am, device_in, device_out, route_flag); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set fm route return 0x%x", audio_ret); + } + } +#endif + else { + audio_ret = __set_route_ap_playback_capture(am, device_in, device_out, route_flag); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set playback route return 0x%x", audio_ret); + } + } + + if (!AUDIO_IS_ERROR(audio_ret)) { + memset((void *)&device_info_list[0], 0, sizeof(audio_device_info_t) * AUDIO_DEVICE_INFO_LIST_MAX); + /* fill device params & open device */ + dev_info_count = _audio_ucm_fill_device_info_list(am, &device_info_list[0], NULL); + for (i = 0; i < dev_info_count; i++) { + __load_n_open_device_with_params(am, &device_info_list[i], 0); + } + } + return AUDIO_RET_OK; +} + +audio_return_t audio_set_route_info(void *userdata, const char* key, const char* value) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + int mixer_value = 0; + + if(key == NULL || value == NULL) + return AUDIO_ERR_PARAMETER; + +#ifdef USE_FMRADIO_V4L2_SPRD + if(!strncmp(key, "fm_mute", strlen(key))) { + mixer_value = atoi(value); + audio_ret = _audio_mixer_control_set_value(am, MIXER_FMRADIO_MUTE, mixer_value); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_ERROR("set mixer(%s) failed", MIXER_FMRADIO_MUTE); + } + } +#endif + + return audio_ret; +} + +audio_return_t audio_alsa_pcm_open (void *userdata, void **pcm_handle, char *device_name, uint32_t direction, int mode) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + int err; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + +// pthread_mutex_lock(&am->device.pcm_lock); + 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)); + pthread_mutex_unlock(&am->device.pcm_lock); + return AUDIO_ERR_RESOURCE; + } + am->device.pcm_count++; + AUDIO_LOG_INFO("PCM handle 0x%x(%s,%s) opened(count:%d)", *pcm_handle, device_name, (direction == AUDIO_DIRECTION_OUT) ? "playback" : "capture", am->device.pcm_count); +// pthread_mutex_unlock(&am->device.pcm_lock); + + return audio_ret; +} + +audio_return_t audio_alsa_pcm_close (void *userdata, void *pcm_handle) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + int err; + + AUDIO_LOG_INFO("Try to close PCM handle 0x%x", pcm_handle); +// pthread_mutex_lock(&am->device.pcm_lock); + if ((err = snd_pcm_close(pcm_handle)) < 0) { + AUDIO_LOG_ERROR("Error closing PCM handle : %s", snd_strerror(err)); + pthread_mutex_unlock(&am->device.pcm_lock); + return AUDIO_ERR_RESOURCE; + } + + am->device.pcm_count--; + AUDIO_LOG_INFO("PCM handle close success (count:%d)", am->device.pcm_count); +// pthread_mutex_unlock(&am->device.pcm_lock); + + return audio_ret; +} diff --git a/tizen-audio-internal.h b/tizen-audio-internal.h new file mode 100644 index 0000000..2232d26 --- /dev/null +++ b/tizen-audio-internal.h @@ -0,0 +1,380 @@ +#ifndef footizenaudiointernalfoo +#define footizenaudiointernalfoo + +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 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 <dlog.h> +#include <time.h> +#include <sys/types.h> +#include <asoundlib.h> +#include <pthread.h> +#include <use-case.h> +#include "tizen-audio.h" +#include "vb_control_parameters.h" +#include "tapi_common.h" + +/* Debug */ + +//#define AUDIO_DEBUG +#define PROPERTY_VALUE_MAX 92 +#define BUF_SIZE 1024 +#define AUDIO_XML_PATH "/usr/etc/audio_hw.xml" +#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) + +/* 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_LOOPBACK "Loopback" +#ifdef USE_FMRADIO_V4L2_SPRD +#define AUDIO_USE_CASE_VERB_FMRADIO "DigitalFM" +#else +#define AUDIO_USE_CASE_VERB_FMRADIO "FM" +#endif +#define AUDIO_USE_CASE_VERB_VIDEOCALL "Video" + +/* Devices : Normal */ +#define AUDIO_USE_CASE_DEV_SPEAKER "Speaker" +#define AUDIO_USE_CASE_DEV_HANDSET "Earpiece" +#define AUDIO_USE_CASE_DEV_HEADSET "Headphones" +#define AUDIO_USE_CASE_DEV_HEADSET_3POLE "Headphones" +#define AUDIO_USE_CASE_DEV_BT_HEADSET "Bluetooth" +#define AUDIO_USE_CASE_DEV_LINEOUT "Line" +#define AUDIO_USE_CASE_DEV_FMRADIO "FM" +#define AUDIO_USE_CASE_DEV_FMRADIO_CORE "FM_Core" + +#define AUDIO_USE_CASE_DEV_MAIN_MIC "MainMic" +#define AUDIO_USE_CASE_DEV_SUB_MIC "SubMic" +#define AUDIO_USE_CASE_DEV_HEADSET_MIC "HeadsetMic" +#define AUDIO_USE_CASE_DEV_BT_MIC "BT Mic" + +/* Modifiers */ +#define AUDIO_USE_CASE_MODIFIER_VOICE "VoiceSearch" +#define AUDIO_USE_CASE_MODIFIER_CAMCORDING "Camcording" +#define AUDIO_USE_CASE_MODIFIER_RINGTONE "Ringtone" + +#define AUDIO_USE_CASE_MODIFIER_FMRADIO "PlayFM" +#define AUDIO_USE_CASE_MODIFIER_FM_SPEAKER "FM_Speaker" +#define AUDIO_USE_CASE_MODIFIER_FM_HEADSET "FM_Headphone" +#define MIXER_VBC_SWITCH "VBC Switch" +#ifdef USE_FMRADIO_V4L2_SPRD +#define MIXER_FMRADIO_L_VOLUME "VBC STR DG Set" +#define MIXER_FMRADIO_R_VOLUME "VBC STL DG Set" +#define MIXER_FMRADIO_MUTE "Digital FM Function" +#endif +#define FM_IIS 0x10 +#define VBC_TD_CHANNELID 0 +#define VBC_ARM_CHANNELID 2 +#define vbc_thread_new pthread_create +#define streq !strcmp +#define strneq strcmp + +/* pin_switch */ +#define PIN_SWITCH_IIS0_SYS_SEL "IIS0 pin select" +#define PIN_SWITCH_IIS0_AP_ID 0 +#define PIN_SWITCH_IIS0_CP0_ID 1 +#define PIN_SWITCH_IIS0_CP1_ID 2 +#define PIN_SWITCH_IIS0_CP2_ID 3 +#define PIN_SWITCH_IIS0_VBC_ID 4 + +#define PIN_SWITCH_BT_IIS_SYS_SEL "BT IIS pin select" +#define PIN_SWITCH_BT_IIS_CP0_IIS0_ID 0 +#define PIN_SWITCH_BT_IIS_CP1_IIS0_ID 4 +#define PIN_SWITCH_BT_IIS_AP_IIS0_ID 8 + +#define PIN_SWITCH_BT_IIS_CON_SWITCH "BT IIS con switch" + +#define ALSA_DEFAULT_CARD "sprdphone" +#define ALSA_VIRTUAL_CARD "VIRTUALAUDIOW" +#define VBPIPE_DEVICE "/dev/spipe_w6" +#define VBPIPE_VOIP_DEVICE "/dev/spipe_w4" +#define MUX_DEVICE "/dev/stty_w0" +#define ALSA_SAUDIOVOIP_CARD "saudiovoip" +#define VOICE_PCM_DEVICE "hw:sprdphone,1" +#define VBC_CMD_TAG "VBC" +#define MESSAGE_OK "OK" +#define MAX_DEVICES 5 +#define MAX_MODIFIERS 5 + +/* type definitions */ +typedef signed char int8_t; + +/* pcm */ +struct config_parse_state { + audio_modem_t *modem_info; + /* To do : pga control setting*/ + /* struct audio_pga *pga; */ + /* struct pga_profile *profile; */ + /* struct pga_attribute_item *attribute_item; */ +}; + +typedef struct { + snd_pcm_format_t format; + uint32_t rate; + uint8_t channels; +} audio_pcm_sample_spec_t; + +/* Session */ +typedef struct audio_session_mgr { + audio_session_t session; + audio_subsession_t subsession; + uint32_t is_recording; + uint32_t is_radio_on; + uint32_t is_call_session; +} audio_session_mgr_t; + +/* Device */ + +typedef struct audio_device_mgr { + audio_device_in_t active_in; + audio_device_out_t active_out; + uint32_t route_flag; + snd_pcm_t *pcm_in; + snd_pcm_t *pcm_out; + pthread_mutex_t pcm_lock; + uint32_t pcm_count; +#ifdef USE_FMRADIO_V4L2_SPRD + snd_pcm_t *fmradio_pcm_out; +#endif +} audio_device_mgr_t; + +/* Stream */ + +#define AUDIO_VOLUME_LEVEL_MAX 16 + +typedef struct audio_volume_gain_table { + double volume[AUDIO_VOLUME_TYPE_MAX][AUDIO_VOLUME_LEVEL_MAX]; + uint32_t volume_level_max[AUDIO_VOLUME_LEVEL_MAX]; + double gain[AUDIO_GAIN_TYPE_MAX]; +} audio_volume_gain_table_t; + +enum { + AUDIO_VOLUME_DEVICE_SPEAKER, + AUDIO_VOLUME_DEVICE_RECEIVER, + AUDIO_VOLUME_DEVICE_EARJACK, + AUDIO_VOLUME_DEVICE_BT_SCO, + AUDIO_VOLUME_DEVICE_BT_A2DP, + AUDIO_VOLUME_DEVICE_DOCK, + AUDIO_VOLUME_DEVICE_HDMI, + AUDIO_VOLUME_DEVICE_MIRRORING, + AUDIO_VOLUME_DEVICE_USB, + AUDIO_VOLUME_DEVICE_MULTIMEDIA_DOCK, + AUDIO_VOLUME_DEVICE_MAX, +}; + +#ifdef USE_FMRADIO_V4L2_SPRD +#define FM_VOLUME_MAX 16 +#endif + +typedef struct audio_stream_mgr { + uint32_t volume_level[AUDIO_VOLUME_TYPE_MAX]; + audio_volume_gain_table_t *volume_gain_table; +#ifdef USE_FMRADIO_V4L2_SPRD + int fmradio_volume_table[FM_VOLUME_MAX]; +#endif +} audio_stream_mgr_t; + +typedef struct audio_ucm_mgr { + snd_use_case_mgr_t* uc_mgr; + pthread_mutex_t mutex; +} audio_ucm_mgr_t; + +typedef struct audio_modem_mgr { + + struct { + pthread_t thread_handle; + pthread_t voip_thread_handle; + snd_pcm_t *voice_pcm_handle_p; + snd_pcm_t *voice_pcm_handle_c; + int exit_vbc_thread; + int vbpipe_fd; + int vbpipe_voip_fd; + unsigned short vbpipe_count; + } vbc; + + struct { + int fd; + } at_cmd; + + int samplerate; + int sim_id; +} audio_modem_mgr_t; + +typedef struct audio_mixer_mgr { + 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_mixer_mgr_t; + +/* Overall */ + +typedef struct audio_mgr { + void *platform_data; + audio_cb_interface_t cb_intf; + audio_session_mgr_t session; + audio_device_mgr_t device; + audio_stream_mgr_t stream; + audio_ucm_mgr_t ucm; + audio_mixer_mgr_t mixer; + audio_modem_mgr_t modem; + audio_modem_t *cp; + cp_type_t cp_type; +} audio_mgr_t; + +/* Voice */ +typedef enum { + VBC_CMD_NONE = 0, + /* current mode and volume gain parameters.*/ + VBC_CMD_SET_MODE = 1, + VBC_CMD_RESP_MODE = 2, + + VBC_CMD_SET_GAIN = 3, + VBC_CMD_RESP_GAIN = 4, + + /* whether switch vb control to dsp parameters.*/ + VBC_CMD_SWITCH_CTRL = 5, + VBC_CMD_RESP_SWITCH = 6, + + /* whether mute or not.*/ + VBC_CMD_SET_MUTE = 7, + VBC_CMD_RESP_MUTE = 8, + + /* open/close device parameters.*/ + VBC_CMD_DEVICE_CTRL = 9, + VBC_CMD_RESP_DEVICE = 10, + + VBC_CMD_PCM_OPEN = 11, + VBC_CMD_RESP_OPEN =12, + + VBC_CMD_PCM_CLOSE = 13, + VBC_CMD_RESP_CLOSE = 14, + + VBC_CMD_SET_SAMPLERATE = 15, + VBC_CMD_RESP_SAMPLERATE = 16, + + VBC_CMD_MAX +}vbc_command; + +typedef struct { + unsigned int sim_card; /*sim card number*/ +} open_pcm_t; + +typedef struct _vbc_parameters_head { + char tag[4]; + unsigned int cmd_type; + unsigned int paras_size; +} vbc_parameters_head; + +typedef struct { + unsigned int is_switch; /* switch vbc contrl to dsp.*/ +} switch_ctrl_t; + +typedef struct vbc_control_params { + int vbchannel_id; + audio_mgr_t *am; +} vbc_control_params_t; + +typedef struct samplerate_ctrl { + unsigned int samplerate; /* change samplerate.*/ +} set_samplerate_t; + +audio_return_t _audio_stream_init (audio_mgr_t *am); +audio_return_t _audio_stream_deinit (audio_mgr_t *am); + +audio_return_t _audio_device_init (audio_mgr_t *am); +audio_return_t _audio_device_deinit (audio_mgr_t * am); +#ifdef USE_FMRADIO_V4L2_SPRD +audio_return_t _set_volume_level_fmradio(audio_mgr_t *am, uint32_t level); +audio_return_t _fmradio_pcm_open (audio_mgr_t *am, uint32_t device_in, uint32_t device_out, uint32_t route_flag); +audio_return_t _fmradio_pcm_close (audio_mgr_t *am); +#endif +audio_return_t _set_route_voicecall (audio_mgr_t *am, uint32_t device_in, uint32_t device_out, uint32_t route_flag); +void _load_n_open_device_from_ucm (audio_mgr_t *am, const char *verb, int load_only); +#define _load_device_from_ucm(am, verb) _load_n_open_device_from_ucm(am, verb, 1) +#define _open_device_from_ucm(am, verb) _load_n_open_device_from_ucm(am, verb, 0) +void _close_n_unload_device_from_ucm (audio_mgr_t *am, const char *verb, int force_unload); +#define _close_device_from_ucm(am, verb) _close_n_unload_device_from_ucm(am, verb, 0) +#define _unload_device_from_ucm(am, verb) _close_n_unload_device_from_ucm(am, verb, 1) +void _open_virtual_device (audio_mgr_t *am); +audio_return_t _reset_route (audio_mgr_t *am, int need_inactive); + +audio_return_t _audio_session_init (audio_mgr_t *am); +audio_return_t _audio_session_deinit (audio_mgr_t *am); + +audio_return_t _audio_ucm_init (audio_mgr_t *am); +audio_return_t _audio_ucm_deinit (audio_mgr_t *am); +void _audio_ucm_get_device_name (audio_mgr_t *am, const char *use_case, audio_direction_t direction, const char **value); +#define _audio_ucm_update_use_case _audio_ucm_set_use_case +audio_return_t _audio_ucm_set_use_case (audio_mgr_t *am, const char *verb, const char *devices[], const char *modifiers[]); +int _audio_ucm_fill_device_info_list (audio_mgr_t *am, audio_device_info_t *device_info_list, const char *verb); +audio_return_t _audio_ucm_get_verb (audio_mgr_t *am, const char **value); +audio_return_t _audio_ucm_reset_use_case (audio_mgr_t *am); +audio_return_t _audio_modem_init (audio_mgr_t *am); +audio_return_t _audio_modem_deinit (audio_mgr_t *am); +int i2s_pin_mux_sel (audio_mgr_t *am, int type); +int _audio_modem_is_call_connected (audio_mgr_t *am); +int _voice_pcm_open(audio_mgr_t *am); +int _voice_pcm_close(audio_mgr_t *am, int reset); +audio_return_t _audio_util_init (audio_mgr_t *am); +audio_return_t _audio_util_deinit (audio_mgr_t *am); +audio_return_t _audio_mixer_control_set_param(audio_mgr_t *am, const char* ctl_name, snd_ctl_elem_value_t* value, int size); +audio_return_t _audio_mixer_control_set_value(audio_mgr_t *am, const char *ctl_name, int val); +audio_return_t _audio_mixer_control_set_value_string(audio_mgr_t *am, const char* ctl_name, const char* value); +audio_return_t _audio_mixer_control_get_value(audio_mgr_t *am, const char *ctl_name, int *val); +audio_return_t _audio_mixer_control_get_element(audio_mgr_t *am, const char *ctl_name, snd_hctl_elem_t **elem); + +audio_return_t _audio_pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event, uint32_t start_threshold, uint32_t rate); +audio_return_t _audio_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); +#endif diff --git a/tizen-audio-modem.c b/tizen-audio-modem.c new file mode 100644 index 0000000..4d03257 --- /dev/null +++ b/tizen-audio-modem.c @@ -0,0 +1,1073 @@ +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * + * 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 <expat.h> +#include <stdbool.h> +#include <vconf.h> + +#include "tizen-audio-internal.h" + +static int __read_nonblock (int fd, void *buf, int bytes) +{ + int ret = 0; + int bytes_to_read = bytes; + + if ((fd > 0) && (buf != NULL)) { + do { + ret = read(fd, buf, bytes); + if ( ret > 0) { + if (ret <= bytes) { + bytes -= ret; + } + } else if ((!((errno == EAGAIN) || (errno == EINTR))) || (0 == ret)) { + break; + } + } while(bytes); + } + + if (bytes == bytes_to_read) + return ret ; + else + return (bytes_to_read - bytes); +} + +static int __write_nonblock (int fd, void *buf, int bytes) +{ + int ret = -1; + int bytes_to_write = bytes; + + if ((fd > 0) && (buf != NULL)) { + do { + ret = write(fd, buf, bytes); + if ( ret > 0) { + if (ret <= bytes) { + bytes -= ret; + } + } else if ((!((errno == EAGAIN) || (errno == EINTR))) || (0 == ret)) { + break; + } + } while(bytes); + } + + if (bytes == bytes_to_write) + return ret ; + else + return (bytes_to_write - bytes); + +} + +static int __voice_pcm_set_params (audio_mgr_t *am, snd_pcm_t *pcm) +{ + snd_pcm_hw_params_t *params = NULL; + int err = 0; + unsigned int val = 0; + + /* 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; + } + if(am->modem.samplerate > 0) { + err = snd_pcm_hw_params_set_rate(pcm, params, am->modem.samplerate, 0); + if (err < 0) { + AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err)); + } + } else { + err = snd_pcm_hw_params_set_rate(pcm, params, (am->device.route_flag & AUDIO_ROUTE_FLAG_NETWORK_WB) ? 16000 : 8000, 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, 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; +} + +int _audio_modem_is_call_connected (audio_mgr_t *am) +{ + int val = -1; /* Mixer values 0 - cp [3g] ,1 - cp [2g] ,2 - ap */ + + _audio_mixer_control_get_value(am, MIXER_VBC_SWITCH, &val); + + return (val == VBC_TD_CHANNELID) ? 1 : 0; +} + +int _voice_pcm_open (audio_mgr_t *am) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + int ret = 0; + + AUDIO_LOG_INFO("open voice pcm handles"); + + /* Get playback voice-pcm from ucm conf. Open and set-params */ + if ((audio_ret = audio_alsa_pcm_open((void *)am, (void **)&am->modem.vbc.voice_pcm_handle_p, VOICE_PCM_DEVICE, AUDIO_DIRECTION_OUT, 0)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %x", VOICE_PCM_DEVICE, audio_ret); + return AUDIO_ERR_IOCTL; + } + ret = __voice_pcm_set_params(am, am->modem.vbc.voice_pcm_handle_p); + + AUDIO_LOG_INFO("pcm playback device open success device(%s)", VOICE_PCM_DEVICE); + + /* Get capture voice-pcm from ucm conf. Open and set-params */ + if ((audio_ret = audio_alsa_pcm_open((void *)am, (void **)&am->modem.vbc.voice_pcm_handle_c, VOICE_PCM_DEVICE, AUDIO_DIRECTION_IN, 0)) < 0) { + AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %x", VOICE_PCM_DEVICE, audio_ret); + return AUDIO_ERR_IOCTL; + } + ret = __voice_pcm_set_params(am, am->modem.vbc.voice_pcm_handle_c); + + AUDIO_LOG_INFO("pcm captures device open success device(%s)", VOICE_PCM_DEVICE); + + return ret; +} + +int _voice_pcm_close (audio_mgr_t *am,int reset) +{ + AUDIO_LOG_INFO("close voice pcm handles"); + + if (am->modem.vbc.voice_pcm_handle_p) { + audio_alsa_pcm_close((void *)am, am->modem.vbc.voice_pcm_handle_p); + am->modem.vbc.voice_pcm_handle_p = NULL; + AUDIO_LOG_INFO("pcm playback device close"); + } + + if (am->modem.vbc.voice_pcm_handle_c) { + audio_alsa_pcm_close((void *)am, am->modem.vbc.voice_pcm_handle_c); + am->modem.vbc.voice_pcm_handle_c = NULL; + AUDIO_LOG_INFO("pcm capture device close"); + } + if (reset) + _reset_route(am, 1); + + return 0; +} +static int __voice_read_samplerate (int fd, set_samplerate_t *paras_ptr) +{ + int ret = 0; + if (fd > 0 && paras_ptr != NULL) { + ret = __read_nonblock(fd, paras_ptr, sizeof(set_samplerate_t)); + if (ret != sizeof(set_samplerate_t)) + ret = -1; + } + AUDIO_LOG_INFO("Return value of read sample rate = %d", ret); + return ret; + +} + +static int __voice_get_samplerate (audio_mgr_t *am,int fd) +{ + set_samplerate_t samplerate_paras; + + memset(&samplerate_paras, 0, sizeof(set_samplerate_t)); + __voice_read_samplerate(fd, &samplerate_paras); + + if (samplerate_paras.samplerate <= 0){ + am->modem.samplerate = 8000; + } else { + am->modem.samplerate = samplerate_paras.samplerate; + } + + return 0; +} + +static int __vbc_write_response (int fd, unsigned int cmd, uint32_t paras_size) +{ + int ret = 0; + vbc_parameters_head write_head; + + memset(&write_head, 0, sizeof(vbc_parameters_head)); + memcpy(&write_head.tag[0], VBC_CMD_TAG, 3); + write_head.cmd_type = cmd + 1; + write_head.paras_size = paras_size; + + ret = __write_nonblock(fd, (void*)&write_head, sizeof(vbc_parameters_head)); + if (ret < 0) + AUDIO_LOG_ERROR("write failed"); + + return 0; +} + +int i2s_pin_mux_sel (audio_mgr_t *am, int type) +{ + int count = 0, ret = 0; + audio_modem_t *modem; + + if (!am) { + AUDIO_LOG_INFO("i2s_pin_mux_sel am is null"); + return 0; + } + + AUDIO_LOG_INFO("i2s_pin_mux_sel in type is %d",type); + modem = am->cp; + + if (type == FM_IIS) { + ret = _audio_mixer_control_set_value(am, + PIN_SWITCH_IIS0_SYS_SEL, PIN_SWITCH_IIS0_VBC_ID); + if (ret == AUDIO_RET_USE_HW_CONTROL) + count = 1; + return 0; + } + if (type == 0) { + if(am->device.active_out & AUDIO_DEVICE_OUT_BT_SCO) { + if(modem->i2s_bt.is_ext) { + if(modem->i2s_bt.is_switch) { + ret = _audio_mixer_control_set_value(am, + PIN_SWITCH_IIS0_SYS_SEL, PIN_SWITCH_IIS0_CP0_ID); + if (ret == AUDIO_RET_USE_HW_CONTROL) + count = 1; + } + } else { + if(modem->i2s_bt.is_switch) { + int value = 0; + audio_mixer_control_get_value (am, PIN_SWITCH_IIS0_SYS_SEL, &value); + if(value == PIN_SWITCH_IIS0_CP0_ID) { + ret = _audio_mixer_control_set_value(am, + PIN_SWITCH_IIS0_SYS_SEL, PIN_SWITCH_IIS0_AP_ID); + if (ret == AUDIO_RET_USE_HW_CONTROL) + count = 1; + } + } + if(am->device.active_out & AUDIO_DEVICE_OUT_BT_SCO) { + if(modem->i2s_bt.is_switch) { + ret = _audio_mixer_control_set_value(am, + PIN_SWITCH_BT_IIS_SYS_SEL, PIN_SWITCH_BT_IIS_CP0_IIS0_ID); + if (ret == AUDIO_RET_USE_HW_CONTROL) + count = 1; + } + } + } + } + } else if (type == 1) { + if(am->device.active_out & AUDIO_DEVICE_OUT_BT_SCO) { + if(modem->i2s_bt.is_ext) { + if(modem->i2s_bt.is_switch) { + ret = _audio_mixer_control_set_value(am, + PIN_SWITCH_IIS0_SYS_SEL, PIN_SWITCH_IIS0_CP1_ID); + if (ret == AUDIO_RET_USE_HW_CONTROL) + count = 1; + } + } else { + if(modem->i2s_bt.is_switch) { + int value = 0; + audio_mixer_control_get_value (am, PIN_SWITCH_IIS0_SYS_SEL, &value); + if(value == PIN_SWITCH_IIS0_CP1_ID) { + ret = _audio_mixer_control_set_value(am, + PIN_SWITCH_IIS0_SYS_SEL, PIN_SWITCH_IIS0_CP2_ID); + if (ret == AUDIO_RET_USE_HW_CONTROL) + count = 1; + } + } + if(am->device.active_out & AUDIO_DEVICE_OUT_BT_SCO) { + if(modem->i2s_bt.is_switch) { + ret = _audio_mixer_control_set_value(am, + PIN_SWITCH_BT_IIS_SYS_SEL, PIN_SWITCH_BT_IIS_CP1_IIS0_ID); + if (ret == AUDIO_RET_USE_HW_CONTROL) + count = 1; + } + } + } + } + } else { + return 0; + } + return (count==1); +} +static void *__vbc_control_thread_run (void *args) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + vbc_parameters_head read_head; + vbc_parameters_head write_head; + int exit_thread = 0; /* make exit variable global if required to gracefully exit */ + int vbpipe_fd; + vbc_control_params_t *params = (vbc_control_params_t*)args; + if (params == NULL) { + return (void*)AUDIO_ERR_PARAMETER; + } + audio_mgr_t *am = params->am; + fd_set fds_read; + struct timeval timeout = {5,0}; + + memset(&read_head, 0, sizeof(vbc_parameters_head)); + memset(&write_head, 0, sizeof(vbc_parameters_head)); + + memcpy(&write_head.tag[0], VBC_CMD_TAG, 3); + write_head.cmd_type = VBC_CMD_NONE; + write_head.paras_size = 0; + + AUDIO_LOG_INFO("[voice] vbc control thread run"); + +again: + /* open vbpipe device for vb parameter interface between ap and cp */ + vbpipe_fd = open(VBPIPE_DEVICE, O_RDWR); + if (vbpipe_fd < 0) { + if (errno == EINTR) + goto again; + AUDIO_LOG_ERROR("[voice] vbpipe open failed: %s", strerror(errno)); + return (void*)AUDIO_ERR_IOCTL; + } + am->modem.vbc.vbpipe_fd = vbpipe_fd; + + if (fcntl(vbpipe_fd, F_SETFL, O_NONBLOCK) < 0) { + AUDIO_LOG_DEBUG("[voice] vbpipe_fd(%d) fcntl error.", vbpipe_fd); + } + + AUDIO_LOG_INFO("[voice] %s opened. vbc start loop", VBPIPE_DEVICE); + + /* start loop */ + while (!exit_thread) { + int ret; + timeout.tv_sec = 5;; + timeout.tv_usec = 0; + + /* read command received from cp */ + + FD_ZERO(&fds_read); + FD_SET(vbpipe_fd, &fds_read); + + ret = select(vbpipe_fd+1, &fds_read, NULL, NULL, &timeout); + if (ret < 0) { + ALOGE("voice:select error %d", errno); + continue; + } + + ret = __read_nonblock(vbpipe_fd, &read_head, sizeof(vbc_parameters_head)); + if (ret < 0) { + continue; + } + + AUDIO_LOG_DEBUG("[voice] Received %d bytes. data: %s, cmd_type: %d", ret, read_head.tag, read_head.cmd_type); + + if (!memcmp(&read_head.tag[0], VBC_CMD_TAG, 3)) { + switch (read_head.cmd_type) { + case VBC_CMD_PCM_OPEN: { + open_pcm_t open_pcm_params; + uint32_t paras_size = ((am->cp->i2s_bt.is_switch << 8) | (am->cp->i2s_bt.index << 0) + | (am->cp->i2s_extspk.is_switch << 9) | (am->cp->i2s_extspk.index << 4)); + + AUDIO_LOG_INFO("[voice] Received VBC_CMD_PCM_OPEN"); + + /* close all pcm */ + if (am->cb_intf.close_all_devices) { + am->cb_intf.close_all_devices(am->platform_data); + } + + /* set Voice verb only when modem is enabled */ + audio_ret = _set_route_voicecall(am, am->device.active_in, am->device.active_out, am->device.route_flag); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set voicecall route return 0x%x", audio_ret); + } + + /* open pcm for voice call */ + ret = _voice_pcm_open(am); + if (ret < 0) { + _voice_pcm_close(am, 1); + break; + } + + memset(&open_pcm_params, 0, sizeof(open_pcm_t)); + ret = __read_nonblock(vbpipe_fd, &open_pcm_params, sizeof(open_pcm_t)); + if (ret < 0) + AUDIO_LOG_ERROR("read failed"); + else + am->modem.sim_id = open_pcm_params.sim_card; + + if (am->device.active_out & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_BT_SCO)) { + if (am->cp_type == CP_TG) + i2s_pin_mux_sel(am, 1); + else if(am->cp_type == CP_W) + i2s_pin_mux_sel(am, 0); + } + + AUDIO_LOG_DEBUG("[voice] Send response for VBC_CMD_PCM_OPEN"); + __vbc_write_response(vbpipe_fd, VBC_CMD_PCM_OPEN, paras_size); + break; + } + + case VBC_CMD_PCM_CLOSE: { + AUDIO_LOG_INFO("[voice] Received VBC_CMD_PCM_CLOSE"); + + am->modem.samplerate = 0; + + /* close all pcm */ + if (am->cb_intf.close_all_devices) { + am->cb_intf.close_all_devices(am->platform_data); + } + + /* close device */ + _voice_pcm_close(am, 1); + + _audio_mixer_control_set_value(am, MIXER_VBC_SWITCH, VBC_ARM_CHANNELID); + + /* open pcm of default device */ + _open_device_from_ucm(am, NULL); + + AUDIO_LOG_DEBUG("[voice] Send response for VBC_CMD_PCM_CLOSE"); + __vbc_write_response(vbpipe_fd, VBC_CMD_PCM_CLOSE, 0); + break; + } + + case VBC_CMD_RESP_CLOSE: { + AUDIO_LOG_INFO("[voice] Received VBC_CMD_RESP_CLOSE & send response"); + ret = __vbc_write_response(vbpipe_fd, VBC_CMD_PCM_CLOSE, 0); + break; + } + + case VBC_CMD_SET_MODE: { + char dummy[52]; + memset(dummy, 0, sizeof(dummy)); + AUDIO_LOG_INFO("[voice] Received VBC_CMD_SET_MODE"); + + if (am->device.active_out & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_BT_SCO)) { + if (am->cp_type == CP_TG) + i2s_pin_mux_sel(am, 1); + else if(am->cp_type == CP_W) + i2s_pin_mux_sel(am, 0); + } + /* To do: set mode params : __vbc_set_mode_params(am, vbpipe_fd); */ + + __read_nonblock(vbpipe_fd, dummy, sizeof(dummy)); + AUDIO_LOG_DEBUG("[voice] Send response for VBC_CMD_SET_MODE"); + __vbc_write_response(vbpipe_fd, VBC_CMD_SET_MODE, 0); + break; + } + case VBC_CMD_SET_GAIN: { + AUDIO_LOG_INFO("[voice] Received VBC_CMD_SET_GAIN"); + + /* To do: set gain params : __vbc_set_gain_params(am, vbpipe_fd); */ + + AUDIO_LOG_DEBUG("[voice] Send response for VBC_CMD_SET_GAIN"); + __vbc_write_response(vbpipe_fd, VBC_CMD_SET_GAIN, 0); + break; + } + case VBC_CMD_SWITCH_CTRL: { + switch_ctrl_t switch_ctrl_params; + + AUDIO_LOG_INFO("[voice] Received VBC_CMD_SWITCH_CTRL"); + + memset(&switch_ctrl_params,0,sizeof(switch_ctrl_t)); + ret = __read_nonblock(vbpipe_fd, &switch_ctrl_params, sizeof(switch_ctrl_t)); + if (ret < 0) + AUDIO_LOG_ERROR("read failed"); + else + AUDIO_LOG_INFO("is_switch:%d", switch_ctrl_params.is_switch); + + _audio_mixer_control_set_value(am, MIXER_VBC_SWITCH, VBC_TD_CHANNELID); + + /* open pcm for virtual device */ + _open_virtual_device(am); + + AUDIO_LOG_DEBUG("[voice] Send response for VBC_CMD_SET_GAIN"); + __vbc_write_response(vbpipe_fd, VBC_CMD_SWITCH_CTRL, 0); + break; + } + case VBC_CMD_SET_MUTE: { + AUDIO_LOG_INFO("[voice] Received VBC_CMD_SET_MUTE"); + break; + } + case VBC_CMD_DEVICE_CTRL: { + char dummy[64]; + memset(dummy, 0, sizeof(dummy)); + AUDIO_LOG_INFO("[voice] Received VBC_CMD_DEVICE_CTRL"); + + /* To do: set device ctrl params :__vbc_set_device_ctrl_params(am, vbpipe_fd); */ + __read_nonblock(vbpipe_fd, dummy, sizeof(dummy)); + + AUDIO_LOG_DEBUG("[voice] Send response for VBC_CMD_DEVICE_CTRL"); + __vbc_write_response(vbpipe_fd, VBC_CMD_DEVICE_CTRL, 0); + + break; + } + case VBC_CMD_SET_SAMPLERATE: { + AUDIO_LOG_INFO("[voice] Received VBC_CMD_SET_SAMPLERATE"); + + _voice_pcm_close(am, 0); + + __voice_get_samplerate(am, vbpipe_fd); + + ret = _voice_pcm_open(am); + if (ret < 0) { + _voice_pcm_close(am, 1); + break; + } + + AUDIO_LOG_DEBUG("[voice] Send response for VBC_CMD_SET_SAMPLERATE"); + __vbc_write_response(vbpipe_fd, VBC_CMD_SET_SAMPLERATE, 0); + break; + } + default: + AUDIO_LOG_WARN("[voice] Unknown command received : %d", read_head.cmd_type); + break; + } + } + } + if (params) + free(params); + + close(vbpipe_fd); + + AUDIO_LOG_INFO("[voice] Exit vbc thread"); + + return (void*)0; +} + +static void *__vbc_control_voip_thread_run (void *args) +{ + open_pcm_t open_pcm_params; + vbc_parameters_head read_head; + vbc_parameters_head write_head; + int exit_thread = 0; /* make exit variable global if required to gracefully exit */ + int vbpipe_fd; + vbc_control_params_t *params = (vbc_control_params_t*)args; + if (params == NULL) { + return (void*)AUDIO_ERR_PARAMETER; + } + audio_mgr_t *am = params->am; + fd_set fds_read; + + struct timeval timeout = {5,0}; + + memset(&read_head, 0, sizeof(vbc_parameters_head)); + memset(&write_head, 0, sizeof(vbc_parameters_head)); + + memcpy(&write_head.tag[0], VBC_CMD_TAG, 3); + write_head.cmd_type = VBC_CMD_NONE; + write_head.paras_size = 0; + + AUDIO_LOG_INFO("[voip] vbc control VOIP thread run"); + +again: + /* open vbpipe device for vb parameter interface between ap and cp */ + vbpipe_fd = open(VBPIPE_VOIP_DEVICE, O_RDWR); + if (vbpipe_fd < 0) { + if (errno == EINTR) + goto again; + AUDIO_LOG_ERROR("[voip] vbpipe open failed: %s", strerror(errno)); + return (void*)0; + } + am->modem.vbc.vbpipe_voip_fd = vbpipe_fd; + + if (fcntl(vbpipe_fd, F_SETFL, O_NONBLOCK) < 0) { + AUDIO_LOG_DEBUG("[voip] vbpipe_fd(%d) fcntl error.", vbpipe_fd); + } + + AUDIO_LOG_INFO("[voip] %s opened. vbc start loop", VBPIPE_DEVICE); + + /* start loop */ + while (!exit_thread) { + int ret; + timeout.tv_sec = 5;; + timeout.tv_usec = 0; + + /* read command received from cp */ + + FD_ZERO(&fds_read); + FD_SET(vbpipe_fd, &fds_read); + + ret = select(vbpipe_fd+1, &fds_read, NULL, NULL, &timeout); + if (ret < 0) { + ALOGE("[voip] select error %d", errno); + continue; + } + + ret = __read_nonblock(vbpipe_fd, &read_head, sizeof(vbc_parameters_head)); + if (ret < 0) { + continue; + } + + AUDIO_LOG_DEBUG("[voip] Received %d bytes. data: %s, cmd_type: %d", ret, read_head.tag, read_head.cmd_type); + + if (!memcmp(&read_head.tag[0], VBC_CMD_TAG, 3)) { + switch (read_head.cmd_type) { + case VBC_CMD_PCM_OPEN: { + uint32_t paras_size = ((am->cp->i2s_bt.is_switch << 8) | (am->cp->i2s_bt.index << 0) + | (am->cp->i2s_extspk.is_switch << 9) | (am->cp->i2s_extspk.index << 4)); + + AUDIO_LOG_INFO("[voip] Received VBC_CMD_PCM_OPEN"); + + memset(&open_pcm_params, 0, sizeof(open_pcm_t)); + ret = __read_nonblock(vbpipe_fd, &open_pcm_params, sizeof(open_pcm_t)); + if (ret < 0) + AUDIO_LOG_ERROR("read failed"); + + AUDIO_LOG_DEBUG("[voip] Send response for VBC_CMD_PCM_OPEN"); + __vbc_write_response(vbpipe_fd, VBC_CMD_PCM_OPEN, paras_size); + break; + } + case VBC_CMD_PCM_CLOSE: { + AUDIO_LOG_INFO("[voip] Received VBC_CMD_PCM_CLOSE & send response"); + + __vbc_write_response(vbpipe_fd, VBC_CMD_PCM_CLOSE, 0); + + break; + } + case VBC_CMD_RESP_CLOSE: { + AUDIO_LOG_INFO("[voip] Received VBC_CMD_RESP_CLOSE & send response"); + + ret = __vbc_write_response(vbpipe_fd, VBC_CMD_PCM_CLOSE, 0); + break; + } + case VBC_CMD_SET_MODE: { + AUDIO_LOG_INFO("[voip] Received VBC_CMD_SET_MODE"); + + if (am->device.active_out & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_BT_SCO)) { + if (am->cp_type == CP_TG) + i2s_pin_mux_sel(am, 1); + else if(am->cp_type == CP_W) + i2s_pin_mux_sel(am, 0); + } + /* To do: set mode params : __vbc_set_mode_params(am, vbpipe_fd); */ + AUDIO_LOG_DEBUG("[voip] Send response for VBC_CMD_SET_MODE"); + __vbc_write_response(vbpipe_fd, VBC_CMD_SET_MODE, 0); + break; + } + case VBC_CMD_SET_GAIN: { + AUDIO_LOG_INFO("[voip] Received VBC_CMD_SET_GAIN"); + + /* To do: set gain params : __vbc_set_gain_params(am, vbpipe_fd); */ + AUDIO_LOG_DEBUG("[voip] Send response for VBC_CMD_SET_GAIN"); + __vbc_write_response(vbpipe_fd, VBC_CMD_SET_GAIN, 0); + break; + } + case VBC_CMD_SWITCH_CTRL: { + switch_ctrl_t switch_ctrl_params; + + AUDIO_LOG_INFO("[voip] Received VBC_CMD_SWITCH_CTRL"); + + memset(&switch_ctrl_params, 0, sizeof(switch_ctrl_t)); + ret = __read_nonblock(vbpipe_fd, &switch_ctrl_params, sizeof(switch_ctrl_t)); + if (ret < 0) + AUDIO_LOG_ERROR("read failed"); + else + AUDIO_LOG_INFO("is_switch:%d", switch_ctrl_params.is_switch); + + _audio_mixer_control_set_value(am, MIXER_VBC_SWITCH, VBC_TD_CHANNELID); + + AUDIO_LOG_DEBUG("[voip] Send response for VBC_CMD_SWITCH_CTRL"); + __vbc_write_response(vbpipe_fd, VBC_CMD_SWITCH_CTRL, 0); + break; + } + case VBC_CMD_SET_MUTE: { + AUDIO_LOG_INFO("[voip] Received VBC_CMD_SET_MUTE & send response"); + __vbc_write_response(vbpipe_fd, VBC_CMD_SET_MUTE, 0); + break; + } + case VBC_CMD_DEVICE_CTRL: { + AUDIO_LOG_INFO("[voip] Received VBC_CMD_DEVICE_CTRL"); + __vbc_write_response(vbpipe_fd, VBC_CMD_DEVICE_CTRL, 0); + break; + } + case VBC_CMD_SET_SAMPLERATE: { + AUDIO_LOG_INFO("[voip] Received VBC_CMD_SET_SAMPLERATE"); + + _voice_pcm_close(am, 0); + __voice_get_samplerate(am, vbpipe_fd); + + ret = _voice_pcm_open(am); + if (ret < 0) { + _voice_pcm_close(am, 1); + break; + } + AUDIO_LOG_DEBUG("[voip] Send response for VBC_CMD_SET_SAMPLERATE"); + __vbc_write_response(vbpipe_fd, VBC_CMD_SET_SAMPLERATE, 0); + break; + } + default: + AUDIO_LOG_WARN("Unknown command received : %d", read_head.cmd_type); + break; + } + } + } + close(vbpipe_fd); + if (params) + free(params); + + AUDIO_LOG_INFO("Exit vbc VOIP thread"); + + return (void*)0; +} + +static audio_return_t __vbc_control_open (audio_mgr_t *am) +{ + vbc_control_params_t *params = (vbc_control_params_t*)malloc(sizeof(vbc_control_params_t)); + audio_return_t ret = AUDIO_RET_OK; + audio_return_t ret2 = AUDIO_RET_OK; + + if (params == NULL) { + AUDIO_LOG_ERROR("vbc control param allocation failed"); + return AUDIO_ERR_RESOURCE; + } + + params->am = am; + AUDIO_LOG_INFO("vbc control thread create"); + ret = vbc_thread_new(&am->modem.vbc.thread_handle, NULL, __vbc_control_thread_run, (void*)params); + if (ret < 0) { + AUDIO_LOG_ERROR("vbc control thread create failed"); + ret = AUDIO_ERR_RESOURCE; + return ret; + } + + ret2 = vbc_thread_new(&am->modem.vbc.voip_thread_handle, NULL, __vbc_control_voip_thread_run, (void*)params); + if (ret2 < 0) { + AUDIO_LOG_ERROR("vbc control VOIP thread create failed"); + ret2 = AUDIO_ERR_RESOURCE; + return ret2; + } + + return AUDIO_RET_OK; +} + +static audio_return_t __vbc_control_close (audio_mgr_t *am) +{ + /* TODO. Make sure we always receive CLOSE command from modem and then close pcm device */ + am->modem.vbc.exit_vbc_thread = 1; + close(am->modem.vbc.vbpipe_fd); + + pthread_cancel(am->modem.vbc.thread_handle); + pthread_cancel(am->modem.vbc.voip_thread_handle); + + return AUDIO_RET_OK; +} + +static vbc_ctrl_pipe_para_t *__audio_modem_create (audio_modem_t *modem, const char *num) +{ + if (!atoi((char *)num)) { + AUDIO_LOG_ERROR("Unnormal modem num!"); + return NULL; + } + + modem->num = atoi((char *)num); + /* check if we need to allocate space for modem profile */ + if(!modem->vbc_ctrl_pipe_info) + { + modem->vbc_ctrl_pipe_info = malloc(modem->num * + sizeof(vbc_ctrl_pipe_para_t)); + + if (modem->vbc_ctrl_pipe_info == NULL) { + AUDIO_LOG_ERROR("Unable to allocate modem profiles"); + return NULL; + } + else + { + /* initialise the new profile */ + memset((void*)modem->vbc_ctrl_pipe_info, 0x00, modem->num * + sizeof(vbc_ctrl_pipe_para_t)); + } + } + + AUDIO_LOG_DEBUG("peter: modem num is %d",modem->num); + /* return the profile just added */ + return modem->vbc_ctrl_pipe_info; +} + + +static void __audio_modem_start_tag (void *data, const XML_Char *tag_name, + const XML_Char **attr) +{ + struct modem_config_parse_state *state = data; + audio_modem_t *modem = state->modem_info; + + /* Look at tags */ + if (strcmp(tag_name, "audio") == 0) { + if (strcmp(attr[0], "device") == 0) { + AUDIO_LOG_INFO("The device name is %s", attr[1]); + } else { + AUDIO_LOG_ERROR("Unnamed audio!"); + } + } else if (strcmp(tag_name, "modem") == 0) { + /* Obtain the modem num */ + if (strcmp(attr[0], "num") == 0) { + AUDIO_LOG_DEBUG("The modem num is '%s'", attr[1]); + state->vbc_ctrl_pipe_info = __audio_modem_create(modem, attr[1]); + } else { + AUDIO_LOG_ERROR("no modem num!"); + } + } else if (strcmp(tag_name, "cp") == 0) { + if (state->vbc_ctrl_pipe_info) { + /* Obtain the modem name \pipe\vbc filed */ + if (strcmp(attr[0], "name") != 0) { + AUDIO_LOG_ERROR("Unnamed modem!"); + goto attr_err; + } + if (strcmp(attr[2], "pipe") != 0) { + AUDIO_LOG_ERROR("'%s' No pipe filed!", attr[0]); + goto attr_err; + } + if (strcmp(attr[4], "vbchannel") != 0) { + AUDIO_LOG_ERROR("'%s' No vbc filed!", attr[0]); + goto attr_err; + } + AUDIO_LOG_DEBUG("cp name is '%s', pipe is '%s',vbc is '%s'", attr[1], attr[3],attr[5]); + if(strcmp(attr[1], "w") == 0) + { + state->vbc_ctrl_pipe_info->cp_type = CP_W; + } + else if(strcmp(attr[1], "t") == 0) + { + state->vbc_ctrl_pipe_info->cp_type = CP_TG; + } + memcpy((void*)state->vbc_ctrl_pipe_info->s_vbc_ctrl_pipe_name,(void*)attr[3],strlen((char *)attr[3])); + state->vbc_ctrl_pipe_info->channel_id = atoi((char *)attr[5]); + state->vbc_ctrl_pipe_info++; + + } else { + AUDIO_LOG_ERROR("error profile!"); + } + } else if (strcmp(tag_name, "i2s_for_btcall") == 0) { + if (strcmp(attr[0], "index") == 0) { + AUDIO_LOG_DEBUG("The iis_for_btcall index is '%s'", attr[1]); + modem->i2s_bt.index = atoi((char *)attr[1]); + } else { + AUDIO_LOG_ERROR("no iis_ctl index for bt call!"); + } + + if (strcmp(attr[2], "switch") == 0) { + AUDIO_LOG_DEBUG("The iis_for_btcall switch is '%s'", attr[3]); + if(strcmp(attr[3],"1") == 0) + modem->i2s_bt.is_switch = true; + else if(strcmp(attr[3],"0") == 0) + modem->i2s_bt.is_switch = false; + } else { + AUDIO_LOG_ERROR("no iis_ctl switch for bt call!"); + } + if (strcmp(attr[4], "dst") == 0) { + AUDIO_LOG_DEBUG("The iis_for_btcall dst is '%s'", attr[5]); + if (strcmp(attr[5], "internal") == 0) + modem->i2s_bt.is_ext = 0; + else if (strcmp(attr[5], "external") == 0) + modem->i2s_bt.is_ext = 1; + } else { + AUDIO_LOG_ERROR("no dst path for bt call!"); + } + } else if (strcmp(tag_name, "i2s_for_extspeaker") == 0) { + if (strcmp(attr[0], "index") == 0) { + AUDIO_LOG_DEBUG("The i2s_for_extspeaker index is '%s'", attr[1]); + modem->i2s_extspk.index = atoi((char *)attr[1]); + } else { + AUDIO_LOG_ERROR("no iis_ctl index for extspk call!"); + } + if (strcmp(attr[2], "switch") == 0) { + AUDIO_LOG_DEBUG("The iis_for_btcall switch is '%s'", attr[3]); + if(strcmp(attr[3],"1") == 0) + modem->i2s_extspk.is_switch = true; + else if(strcmp(attr[3],"0") == 0) + modem->i2s_extspk.is_switch = false; + } else { + AUDIO_LOG_ERROR("no iis_ctl switch for extspk call!"); + } + if (strcmp(attr[4], "dst") == 0) { + if (strcmp(attr[5], "external") == 0) + modem->i2s_extspk.is_ext = 1; + else if(strcmp(attr[5], "internal") == 0) + modem->i2s_extspk.is_ext = 0; + + AUDIO_LOG_DEBUG("The i2s_for_extspeaker dst is '%d'", modem->i2s_extspk.is_ext); + + } else { + AUDIO_LOG_ERROR("no dst path for bt call!"); + } + } else if (strcmp(tag_name, "debug") == 0) { //parse debug info + if (strcmp(attr[0], "enable") == 0) { + if (strcmp(attr[1], "0") == 0) { + modem->debug_info.enable = 0; + } else { + modem->debug_info.enable = 1; + } + } else { + AUDIO_LOG_ERROR("no adaptable type for debug!"); + goto attr_err; + } + } else if (strcmp(tag_name, "debuginfo") == 0) { //parse debug info + if (strcmp(attr[0], "sleepdeltatimegate") == 0) { + AUDIO_LOG_DEBUG("The sleepdeltatimegate is '%s'", attr[1]); + modem->debug_info.sleeptime_gate=atoi((char *)attr[1]); + } else if (strcmp(attr[0], "pcmwritetimegate") == 0) { + AUDIO_LOG_DEBUG("The pcmwritetimegate is '%s'", attr[1]); + modem->debug_info.pcmwritetime_gate=atoi((char *)attr[1]); + } else if (strcmp(attr[0], "lastthiswritetimegate") == 0) { + AUDIO_LOG_DEBUG("The lastthiswritetimegate is '%s'", attr[1]); + modem->debug_info.lastthis_outwritetime_gate=atoi((char *)attr[1]); + } else { + AUDIO_LOG_ERROR("no adaptable info for debuginfo!"); + goto attr_err; + } + } + +attr_err: + return; +} +static void __audio_modem_end_tag (void *data, const XML_Char *tag_name) +{ + return; +} + +static audio_modem_t * __audio_modem_parse (void) +{ + struct config_parse_state state; + XML_Parser parser; + FILE *file; + int bytes_read; + void *buf; + audio_modem_t *modem = NULL; + + modem = calloc(1, sizeof(audio_modem_t)); + + if(modem == NULL) { + goto err_alloc; + } + memset(modem, 0, sizeof(audio_modem_t)); + modem->num = 0; + modem->vbc_ctrl_pipe_info = NULL; + + file = fopen(AUDIO_XML_PATH, "r"); + if (!file) { + AUDIO_LOG_ERROR("Failed to open %s", AUDIO_XML_PATH); + goto err_fopen; + } + + parser = XML_ParserCreate(NULL); + if (!parser) { + AUDIO_LOG_ERROR("Failed to create XML parser"); + goto err_parser_create; + } + + memset(&state, 0, sizeof(state)); + state.modem_info = modem; + XML_SetUserData(parser, &state); + XML_SetElementHandler(parser, __audio_modem_start_tag, __audio_modem_end_tag); + + for (;;) { + buf = XML_GetBuffer(parser, BUF_SIZE); + if (buf == NULL) + goto err_parse; + + bytes_read = fread(buf, 1, BUF_SIZE, file); + if (bytes_read < 0) + goto err_parse; + + if (XML_ParseBuffer(parser, bytes_read, bytes_read == 0) == XML_STATUS_ERROR) { + AUDIO_LOG_ERROR("Error in codec PGA xml (%s)", AUDIO_XML_PATH); + goto err_parse; + } + + if (bytes_read == 0) + break; + } + XML_ParserFree(parser); + fclose(file); + return modem; + +err_parse: + XML_ParserFree(parser); +err_parser_create: + fclose(file); +err_fopen: + free(modem); +err_alloc: + modem = NULL; + return NULL; +} +audio_return_t _audio_modem_init (audio_mgr_t *am) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + am->modem.vbc.vbpipe_count = 0; + + /* Initialize vbc interface */ + audio_ret = __vbc_control_open(am); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_ERROR("__vbc_control_open failed"); + goto exit; + } + am->modem.vbc.voice_pcm_handle_p = NULL; + am->modem.vbc.voice_pcm_handle_c = NULL; + am->modem.samplerate = 0; + am->cp = __audio_modem_parse(); + if (am->cp == NULL) { + AUDIO_LOG_ERROR("modem parse failed"); + goto exit; + } + am->cp_type = am->cp->vbc_ctrl_pipe_info->cp_type; + + /* This ctrl need to be set "0" always - SPRD */ + _audio_mixer_control_set_value(am, PIN_SWITCH_BT_IIS_CON_SWITCH, 0); + +exit: + return audio_ret; +} + +audio_return_t _audio_modem_deinit (audio_mgr_t *am) +{ + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + /* Close vbc interface */ + __vbc_control_close(am); + + return AUDIO_RET_OK; +} + diff --git a/tizen-audio-session.c b/tizen-audio-session.c new file mode 100644 index 0000000..08ef9aa --- /dev/null +++ b/tizen-audio-session.c @@ -0,0 +1,245 @@ +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 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" + +static const char *__get_session_string_by_idx (uint32_t session_idx) +{ + switch (session_idx) { + case AUDIO_SESSION_MEDIA: return "media"; + case AUDIO_SESSION_VOICECALL: return "voicecall"; + case AUDIO_SESSION_VIDEOCALL: return "videocall"; + case AUDIO_SESSION_VOIP: return "voip"; + case AUDIO_SESSION_FMRADIO: return "fmradio"; + case AUDIO_SESSION_CAMCORDER: return "camcorder"; + case AUDIO_SESSION_NOTIFICATION: return "notification"; + case AUDIO_SESSION_ALARM: return "alarm"; + case AUDIO_SESSION_EMERGENCY: return "emergency"; + case AUDIO_SESSION_VOICE_RECOGNITION: return "voice_recognition"; + default: return "invalid"; + } +} + +static const char *__get_subsession_string_by_idx (uint32_t subsession_idx) +{ + switch (subsession_idx) { + case AUDIO_SUBSESSION_NONE: return "none"; + case AUDIO_SUBSESSION_VOICE: return "voice"; + case AUDIO_SUBSESSION_RINGTONE: return "ringtone"; + case AUDIO_SUBSESSION_MEDIA: return "media"; + case AUDIO_SUBSESSION_INIT: return "init"; + case AUDIO_SUBSESSION_VR_NORMAL: return "vr_normal"; + case AUDIO_SUBSESSION_VR_DRIVE: return "vr_drive"; + case AUDIO_SUBSESSION_STEREO_REC: return "stereo_rec"; + case AUDIO_SUBSESSION_MONO_REC: return "mono_rec"; + default: return "invalid"; + } +} + +static const char * __get_sessin_cmd_string (uint32_t cmd) +{ + switch (cmd) { + case AUDIO_SESSION_CMD_START: return "start"; + case AUDIO_SESSION_CMD_SUBSESSION: return "subsession"; + case AUDIO_SESSION_CMD_END: return "end"; + default: return "invalid"; + } +} + +audio_return_t _audio_session_init (audio_mgr_t *am) +{ + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + am->session.session = AUDIO_SESSION_MEDIA; + am->session.subsession = AUDIO_SUBSESSION_NONE; + am->session.is_recording = 0; + am->session.is_radio_on = 0; + am->session.is_call_session = 0; + + return AUDIO_RET_OK; +} + +audio_return_t _audio_session_deinit (audio_mgr_t *am) +{ + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + return AUDIO_RET_OK; +} + +audio_return_t audio_set_session (void *userdata, uint32_t session, uint32_t subsession, uint32_t cmd) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + uint32_t prev_subsession = am->session.subsession; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO("session %s:%s(%s)->%s(%s)", __get_sessin_cmd_string(cmd), + __get_session_string_by_idx(am->session.session), __get_subsession_string_by_idx(am->session.subsession), + __get_session_string_by_idx(session), __get_subsession_string_by_idx(subsession)); + + if (cmd == AUDIO_SESSION_CMD_START) { + if (am->session.is_call_session) { + AUDIO_LOG_ERROR("call active its not possible to have any other session start now"); + return audio_ret; + } + am->session.session = session; + am->session.subsession = subsession; + + if (session == AUDIO_SESSION_FMRADIO) { +#ifdef USE_FMRADIO_V4L2_SPRD + int media_volume = 0; +#endif + am->session.is_radio_on = 1; +#ifdef USE_FMRADIO_V4L2_SPRD + _fmradio_pcm_open(am, am->device.active_in, am->device.active_out, 0); +#endif + audio_set_route(userdata, session, subsession, am->device.active_in, am->device.active_out, 0); +#ifdef USE_FMRADIO_V4L2_SPRD + audio_get_volume_level(am, AUDIO_VOLUME_TYPE_MEDIA, &media_volume); + _set_volume_level_fmradio(am, media_volume); + i2s_pin_mux_sel(am, FM_IIS); + + /* change source due to limitation of FM recording */ + _unload_device_from_ucm(am, NULL); + _load_device_from_ucm(am, NULL); +#endif + } + + if ((session == AUDIO_SESSION_VIDEOCALL) || + (session == AUDIO_SESSION_VOICECALL) || + (session == AUDIO_SESSION_VOIP)) { + AUDIO_LOG_INFO("set call session"); + am->session.is_call_session = 1; + } + + } else if (cmd == AUDIO_SESSION_CMD_END) { + + if ((session == AUDIO_SESSION_VIDEOCALL) || + (session == AUDIO_SESSION_VOICECALL) || + (session == AUDIO_SESSION_VOIP)) { + AUDIO_LOG_INFO("unset call session"); + am->session.is_call_session = 0; + } + + if (am->session.is_call_session) { + AUDIO_LOG_ERROR("call active its not possible to have any other session end now"); + return audio_ret; + } + + if (session == AUDIO_SESSION_VIDEOCALL && _audio_modem_is_call_connected(am)) { + _unload_device_from_ucm(am, NULL); + + /* close all pcm */ + if (am->cb_intf.close_all_devices) { + am->cb_intf.close_all_devices(am->platform_data); + } + + _audio_mixer_control_set_value(am, MIXER_VBC_SWITCH, VBC_ARM_CHANNELID); + + /* close device */ + _voice_pcm_close(am, 1); + + /* open pcm of default device */ + _open_device_from_ucm(am, NULL); + } + if (session == AUDIO_SESSION_MEDIA && (prev_subsession == AUDIO_SUBSESSION_STEREO_REC || AUDIO_SUBSESSION_MONO_REC)) { + am->session.is_recording = 0; + } + if (session != AUDIO_SESSION_FMRADIO && am->session.is_radio_on) { + am->session.session = AUDIO_SESSION_FMRADIO; + } else { + am->session.session = AUDIO_SESSION_MEDIA; + } + am->session.subsession = AUDIO_SUBSESSION_NONE; + + if (session == AUDIO_SESSION_FMRADIO) { + am->session.is_radio_on = 0; +#ifdef USE_FMRADIO_V4L2_SPRD + _fmradio_pcm_close(am); +#endif + audio_ret = _reset_route(am, 0); + if (audio_ret != AUDIO_RET_OK) { + AUDIO_LOG_ERROR("_reset_route failed with %d", audio_ret); + } +#ifdef USE_FMRADIO_V4L2_SPRD + /* change source due to limitation of FM recording */ + _unload_device_from_ucm(am, NULL); + _open_device_from_ucm(am, NULL); +#endif + } + } else if (cmd == AUDIO_SESSION_CMD_SUBSESSION) { + + if (am->session.is_call_session) { + if ((subsession != AUDIO_SUBSESSION_VOICE) && + (subsession != AUDIO_SUBSESSION_MEDIA) && + (subsession != AUDIO_SUBSESSION_RINGTONE)) { + AUDIO_LOG_ERROR("call active we can only have one of AUDIO_SUBSESSION_VOICE AUDIO_SUBSESSION_MEDIA AUDIO_SUBSESSION_RINGTONE as a sub-session"); + return audio_ret; + } + } + + am->session.subsession = subsession; + + if (prev_subsession != subsession && am->session.session == AUDIO_SESSION_VIDEOCALL && subsession == AUDIO_SUBSESSION_VOICE) { + /* close all pcm */ + if (am->cb_intf.close_all_devices) { + am->cb_intf.close_all_devices(am->platform_data); + } + if (am->device.active_out & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_BT_SCO)) { + if (am->cp_type == CP_TG) + i2s_pin_mux_sel(am, 1); + else if(am->cp_type == CP_W) + i2s_pin_mux_sel(am, 0); + } + + audio_ret = _set_route_videocall(am, am->device.active_in, am->device.active_out, am->device.route_flag); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_WARN("set videocall route return 0x%x", audio_ret); + } + + if (_voice_pcm_open(am) < 0) { + _voice_pcm_close(am, 1); + return AUDIO_ERR_RESOURCE; + } + _open_device_from_ucm(am, NULL); + } + + if (prev_subsession != subsession && subsession == AUDIO_SUBSESSION_VOICE) { + am->session.is_radio_on = 0; + am->session.is_recording = 0; + } + + if (subsession == AUDIO_SUBSESSION_STEREO_REC || subsession == AUDIO_SUBSESSION_MONO_REC) { + am->session.is_recording = 1; + } else if (am->session.is_recording == 1 && subsession == AUDIO_SUBSESSION_INIT) { + am->session.is_recording = 0; + } + } + + return audio_ret; +} diff --git a/tizen-audio-stream.c b/tizen-audio-stream.c new file mode 100644 index 0000000..8ddbbad --- /dev/null +++ b/tizen-audio-stream.c @@ -0,0 +1,638 @@ +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 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 "/usr/etc/mmfw_audio_volume.ini" +#define VOLUME_INI_TEMP_PATH "/opt/system/mmfw_audio_volume.ini" +#define VOLUME_VALUE_MAX (1.0f) +#define GAIN_VALUE_MAX (1.0f) + +#ifdef USE_FMRADIO_V4L2_SPRD +#define RADIO_TUNING_DEFUALT_FILE "/usr/etc/mmfw_fmradio.ini" +#define RADIO_TUNING_TEMP_FILE "/opt/usr/media/.mmfw_fmradio.ini" +#define RADIO_TUNING_ENABLE "tuning:enable" +#define RADIO_TUNING_VOLUME_LEVELS "fmradio:volume_levels" +#define RADIO_TUNING_VOLUME_TABLE "fmradio:volume_table" +#endif + +enum { + STREAM_DEVICE_SPEAKER, + STREAM_DEVICE_HEADSET, + STREAM_DEVICE_BLUETOOTH, + STREAM_DEVICE_HDMI, + STREAM_DEVICE_DOCK, + STREAM_DEVICE_MAX, +}; + +static const char *g_volume_vconf[AUDIO_VOLUME_TYPE_VCONF_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 inline uint8_t __get_volume_dev_index(audio_mgr_t *am, uint32_t volume_type) +{ + + switch (am->device.active_out) { + case AUDIO_DEVICE_OUT_SPEAKER: return AUDIO_VOLUME_DEVICE_SPEAKER; + case AUDIO_DEVICE_OUT_RECEIVER: return AUDIO_VOLUME_DEVICE_RECEIVER; + case AUDIO_DEVICE_OUT_WIRED_ACCESSORY: return AUDIO_VOLUME_DEVICE_EARJACK; + case AUDIO_DEVICE_OUT_BT_SCO: return AUDIO_VOLUME_DEVICE_BT_SCO; + case AUDIO_DEVICE_OUT_BT_A2DP: return AUDIO_VOLUME_DEVICE_BT_A2DP; + case AUDIO_DEVICE_OUT_DOCK: return AUDIO_VOLUME_DEVICE_DOCK; + case AUDIO_DEVICE_OUT_HDMI: return AUDIO_VOLUME_DEVICE_HDMI; + case AUDIO_DEVICE_OUT_MIRRORING: return AUDIO_VOLUME_DEVICE_MIRRORING; + case AUDIO_DEVICE_OUT_USB_AUDIO: return AUDIO_VOLUME_DEVICE_USB; + case AUDIO_DEVICE_OUT_MULTIMEDIA_DOCK: return AUDIO_VOLUME_DEVICE_MULTIMEDIA_DOCK; + default: return AUDIO_VOLUME_DEVICE_SPEAKER; + } +} + +static const uint8_t __get_stream_dev_index (uint32_t device_out) +{ + switch (device_out) { + case AUDIO_DEVICE_OUT_SPEAKER: return STREAM_DEVICE_SPEAKER; + case AUDIO_DEVICE_OUT_RECEIVER: return STREAM_DEVICE_SPEAKER; + case AUDIO_DEVICE_OUT_WIRED_ACCESSORY: return STREAM_DEVICE_HEADSET; + case AUDIO_DEVICE_OUT_BT_SCO: return STREAM_DEVICE_BLUETOOTH; + case AUDIO_DEVICE_OUT_BT_A2DP: return STREAM_DEVICE_BLUETOOTH; + case AUDIO_DEVICE_OUT_DOCK: return STREAM_DEVICE_DOCK; + case AUDIO_DEVICE_OUT_HDMI: return STREAM_DEVICE_HDMI; + case AUDIO_DEVICE_OUT_MIRRORING: return STREAM_DEVICE_SPEAKER; + case AUDIO_DEVICE_OUT_USB_AUDIO: return STREAM_DEVICE_SPEAKER; + case AUDIO_DEVICE_OUT_MULTIMEDIA_DOCK: return STREAM_DEVICE_DOCK; + default: + AUDIO_LOG_DEBUG("invalid device_out:%d", device_out); + break; + } + + return STREAM_DEVICE_SPEAKER; +} + +static const char *__get_device_string_by_idx (uint32_t dev_idx) +{ + switch (dev_idx) { + case STREAM_DEVICE_SPEAKER: return "speaker"; + case STREAM_DEVICE_HEADSET: return "headset"; + case STREAM_DEVICE_BLUETOOTH: return "btheadset"; + case STREAM_DEVICE_HDMI: return "hdmi"; + case STREAM_DEVICE_DOCK: return "dock"; + default: return "invalid"; + } +} + +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"; + case AUDIO_VOLUME_TYPE_FIXED: return "fixed"; + default: return "invalid"; + } +} + +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"; + } +} +#ifdef USE_FMRADIO_V4L2_SPRD +audio_return_t _set_volume_level_fmradio(audio_mgr_t *am, uint32_t level) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + + int volume = 0; + int mute = -1; + + /* Applying mute at volume zero */ + if (level == 0) { + audio_ret = _audio_mixer_control_set_value(am, MIXER_FMRADIO_MUTE, 0); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_ERROR("set mixer(%s) failed", MIXER_FMRADIO_MUTE); + } + } else { + audio_ret = _audio_mixer_control_get_value(am, MIXER_FMRADIO_MUTE, &mute); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_ERROR("get mixer(%s) failed", MIXER_FMRADIO_MUTE); + return audio_ret; + } + if (mute == 0) { + audio_ret = _audio_mixer_control_set_value(am, MIXER_FMRADIO_MUTE, 1); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_ERROR("set mixer(%s) failed", MIXER_FMRADIO_MUTE); + return audio_ret; + } + } + } + + if (AUDIO_IS_ERROR(_audio_mixer_control_get_value(am, MIXER_FMRADIO_L_VOLUME, &volume))) { + AUDIO_LOG_ERROR("get mixer(%s) failed", MIXER_FMRADIO_L_VOLUME); + } else { + if (volume != am->stream.fmradio_volume_table[level]) { + audio_ret = _audio_mixer_control_set_value(am, MIXER_FMRADIO_L_VOLUME, am->stream.fmradio_volume_table[level]); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_ERROR("set mixer(%s) failed", MIXER_FMRADIO_L_VOLUME); + return audio_ret; + } + } + } + + if (AUDIO_IS_ERROR(_audio_mixer_control_get_value(am, MIXER_FMRADIO_R_VOLUME, &volume))) { + AUDIO_LOG_ERROR("get mixer(%s) failed", MIXER_FMRADIO_R_VOLUME); + } else { + if (volume != am->stream.fmradio_volume_table[level]) { + audio_ret = _audio_mixer_control_set_value(am, MIXER_FMRADIO_R_VOLUME, am->stream.fmradio_volume_table[level]); + if (AUDIO_IS_ERROR(audio_ret)) { + AUDIO_LOG_ERROR("set mixer(%s) failed", MIXER_FMRADIO_R_VOLUME); + } + } + } + return audio_ret; +} +#endif +static void __dump_info(char *dump, audio_info_t *info) +{ + int len; + char name[64] = { '\0', }; + + if (info->device.api == AUDIO_DEVICE_API_ALSA) { + len = snprintf(dump, AUDIO_DUMP_STR_LEN, "device:alsa(%d.%d)", info->device.alsa.card_idx, info->device.alsa.device_idx); + } else if (info->device.api == AUDIO_DEVICE_API_ALSA) { + len = snprintf(dump, AUDIO_DUMP_STR_LEN, "device:bluez(%s,nrec:%d)", info->device.bluez.protocol, info->device.bluez.nrec); + } else { + len = snprintf(dump, AUDIO_DUMP_STR_LEN, "device:unknown"); + } + + if (len > 0) + dump += len; + + strncpy(name, info->stream.name ? info->stream.name : "null", sizeof(name)-1); + len = snprintf(dump, AUDIO_DUMP_STR_LEN, "stream:%s(%dhz,%dch,vol:%s,gain:%s)", + name, info->stream.samplerate, info->stream.channels, + __get_volume_type_string_by_idx(info->stream.volume_type), __get_gain_type_string_by_idx(info->stream.gain_type)); + + if (len > 0) + dump += len; + + *dump = '\0'; +} + +static void __dump_tb (audio_mgr_t *am) +{ + audio_volume_gain_table_t *volume_gain_table = am->stream.volume_gain_table; + uint32_t dev_idx, 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 >>>>>"); + + + for (vol_type_idx = 0; vol_type_idx < AUDIO_VOLUME_TYPE_VCONF_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 < volume_gain_table->volume_level_max[vol_type_idx]; vol_level_idx++) { + snprintf(dump_str_ptr, 6, "%01.2f ", volume_gain_table->volume[vol_type_idx][vol_level_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + } + + + /* 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)); + + 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_gain_table->gain[gain_type_idx]); + dump_str_ptr += strlen(dump_str_ptr); + } + AUDIO_LOG_INFO("%s", dump_str); + +} + +static audio_return_t __load_volume_gain_table_from_ini (audio_mgr_t *am) +{ + dictionary * dict = NULL; + uint32_t vol_type_idx, vol_level_idx, gain_type_idx; + audio_volume_gain_table_t *volume_gain_table = am->stream.volume_gain_table; + int size = 0; + const char delimiter[] = ", "; + char *key, *list_str, *token, *ptr = NULL; + const char *section = "volumes"; + + 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; + } + } + + /* Load volume table */ + for (vol_type_idx = 0; vol_type_idx < AUDIO_VOLUME_TYPE_VCONF_MAX; vol_type_idx++) { + const char *vol_type_str = __get_volume_type_string_by_idx(vol_type_idx); + + volume_gain_table->volume_level_max[vol_type_idx] = 0; + size = strlen(section) + strlen(vol_type_str) + 2; + key = malloc(size); + if (key) { + snprintf(key, size, "%s:%s", section, vol_type_str); + list_str = iniparser_getstr(dict, key); + 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_gain_table->volume[vol_type_idx][volume_gain_table->volume_level_max[vol_type_idx]++] = vol_value; + token = strtok_r(NULL, delimiter, &ptr); + } + } else { + volume_gain_table->volume_level_max[vol_type_idx] = 1; + for (vol_level_idx = 0; vol_level_idx < AUDIO_VOLUME_LEVEL_MAX; vol_level_idx++) { + volume_gain_table->volume[vol_type_idx][vol_level_idx] = VOLUME_VALUE_MAX; + } + } + free(key); + } + } + + /* Load gain table */ + volume_gain_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(section) + strlen("gain") + strlen(gain_type_str) + 3; + key = malloc(size); + if (key) { + snprintf(key, size, "%s:gain_%s", section, gain_type_str); + token = iniparser_getstr(dict, key); + if (token) { + volume_gain_table->gain[gain_type_idx] = atof(token); + } else { + volume_gain_table->gain[gain_type_idx] = GAIN_VALUE_MAX; + } + free(key); + } else { + volume_gain_table->gain[gain_type_idx] = GAIN_VALUE_MAX; + } + } + + iniparser_freedict(dict); + + __dump_tb(am); + + return AUDIO_RET_OK; +} + +#ifdef USE_FMRADIO_V4L2_SPRD +int _radio_load_volume_table(int** volume_table, int *number_of_elements) +{ + dictionary * dict = NULL; + const char delimiter[] = ", "; + char* ptr = NULL; + char* token = NULL; + char* list_str = NULL; + int* temp_table = NULL; + int index = 0; + int ret = 0; + + bool tuning_enable = 0; + int not_found = -1; + int value = 0; + + dict = iniparser_load(RADIO_TUNING_DEFUALT_FILE); + if (dict == NULL) { + AUDIO_LOG_ERROR("%s load failed", RADIO_TUNING_DEFUALT_FILE); + return AUDIO_ERR_UNDEFINED; + } else { + /*tuning enable */ + value = iniparser_getboolean(dict, RADIO_TUNING_ENABLE, not_found); + if (value == not_found) { + AUDIO_LOG_ERROR("Can't get Tuning Enable value"); + } else { + tuning_enable = value; + AUDIO_LOG_INFO("Tuning enabled."); + } + iniparser_freedict(dict); /*Cleanup*/ + } + + if (tuning_enable) { + AUDIO_LOG_INFO("Tuning enabled. load temp tuning file."); + dict = iniparser_load(RADIO_TUNING_TEMP_FILE); + if (!dict) { + AUDIO_LOG_WARN("%s load failed. Tuning enabled but there is not tuning temp file. Use temporary file", RADIO_TUNING_TEMP_FILE); + dict = iniparser_load(RADIO_TUNING_DEFUALT_FILE); + if (!dict) { + AUDIO_LOG_ERROR("%s load failed", RADIO_TUNING_DEFUALT_FILE); + return AUDIO_ERR_UNDEFINED; + } + } + } else { + AUDIO_LOG_INFO("Tuning diabled. load default tuning file."); + dict = iniparser_load(RADIO_TUNING_DEFUALT_FILE); + if (!dict) { + AUDIO_LOG_ERROR("%s load failed", RADIO_TUNING_DEFUALT_FILE); + return AUDIO_ERR_UNDEFINED; + } + } + + *number_of_elements = iniparser_getint (dict, RADIO_TUNING_VOLUME_LEVELS, -1); + if (*number_of_elements == -1) { + ret = AUDIO_ERR_UNDEFINED; + goto error; + } + temp_table = (int *)malloc ((*number_of_elements) * sizeof(int)); + if (!temp_table) { + goto error; + } + *volume_table = temp_table; + + list_str = iniparser_getstr(dict, RADIO_TUNING_VOLUME_TABLE); + if (list_str) { + token = strtok_r(list_str, delimiter, &ptr); + while (token) { + temp_table[index] = atoi(token); + AUDIO_LOG_INFO("fm volume index %d is %d", index, temp_table[index]); + index++; + token = strtok_r(NULL, delimiter, &ptr); + } + } +error: + iniparser_freedict(dict); + return ret; +} +#endif + +audio_return_t _audio_stream_init (audio_mgr_t *am) +{ + int i, val; + audio_return_t audio_ret = AUDIO_RET_OK; + int init_value[AUDIO_VOLUME_TYPE_VCONF_MAX] = { 9, 11, 7, 11, 7, 4, 4, 7 }; +#ifdef USE_FMRADIO_V4L2_SPRD + int number_of_steps = 0; + int index =0; + int* fm_table = NULL; +#endif + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + for (i = 0; i < AUDIO_VOLUME_TYPE_VCONF_MAX; i++) { + am->stream.volume_level[i] = init_value[i]; + } + + for (i = 0; i < AUDIO_VOLUME_TYPE_VCONF_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); + + am->stream.volume_level[i] = val; + } + + if (!(am->stream.volume_gain_table = malloc(sizeof(audio_volume_gain_table_t)))) { + AUDIO_LOG_ERROR("volume_gain_table malloc failed"); + return AUDIO_ERR_RESOURCE; + } + + audio_ret = __load_volume_gain_table_from_ini(am); + if(audio_ret != AUDIO_RET_OK) { + AUDIO_LOG_ERROR("gain table load error"); + return AUDIO_ERR_UNDEFINED; + } +#ifdef USE_FMRADIO_V4L2_SPRD + _radio_load_volume_table(&fm_table, &number_of_steps); + if (fm_table) { + AUDIO_LOG_DEBUG("number of steps -> %d", number_of_steps); + /*copy from temp structure to main strcture*/ + for (index = 0; index < number_of_steps; index++) { + am->stream.fmradio_volume_table[index] = fm_table[index]; + } + free(fm_table); + fm_table = NULL; + } + +#endif + return audio_ret; +} + +audio_return_t _audio_stream_deinit (audio_mgr_t *am) +{ + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + if (am->stream.volume_gain_table) { + free(am->stream.volume_gain_table); + am->stream.volume_gain_table = NULL; + } + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_volume_level_max (void *userdata, uint32_t volume_type, uint32_t *level) +{ + audio_mgr_t *am = (audio_mgr_t *)userdata; + audio_volume_gain_table_t *volume_gain_table; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(am->stream.volume_gain_table, AUDIO_ERR_PARAMETER); + + /* Get max volume level by device & type */ + volume_gain_table = am->stream.volume_gain_table; + + if (volume_type < AUDIO_VOLUME_TYPE_VCONF_MAX) { + *level = volume_gain_table->volume_level_max[volume_type]; + AUDIO_LOG_DEBUG("get_volume_level_max:%s=>%d", __get_volume_type_string_by_idx(volume_type), *level); + } + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_volume_level (void *userdata, uint32_t volume_type, uint32_t *level) +{ + audio_mgr_t *am = (audio_mgr_t *)userdata; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + if (volume_type < AUDIO_VOLUME_TYPE_VCONF_MAX) + *level = am->stream.volume_level[volume_type]; + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_volume_value (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t level, double *value) +{ + if (info) { + audio_mgr_t *am = (audio_mgr_t *)userdata; + audio_volume_gain_table_t *volume_gain_table; + char dump_str[AUDIO_DUMP_STR_LEN]; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(am->stream.volume_gain_table, AUDIO_ERR_PARAMETER); + __dump_info(&dump_str[0], info); + /* Get basic volume by device & type & level */ + volume_gain_table = am->stream.volume_gain_table; + if (volume_type < AUDIO_VOLUME_TYPE_VCONF_MAX) { + if (volume_gain_table->volume_level_max[volume_type] < level) + *value = VOLUME_VALUE_MAX; + else + *value = volume_gain_table->volume[volume_type][level]; + *value *= volume_gain_table->gain[info->stream.gain_type]; + } else if (volume_type == AUDIO_VOLUME_TYPE_FIXED) + *value = 1.0 * volume_gain_table->gain[info->stream.gain_type]; + + AUDIO_LOG_DEBUG("get_volume_value:%d(%s)=>%f %s", level, __get_volume_type_string_by_idx(volume_type), *value, &dump_str[0]); + } + + return AUDIO_RET_OK; +} + +audio_return_t audio_set_volume_level (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t level) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + if (info == NULL && volume_type < AUDIO_VOLUME_TYPE_VCONF_MAX) { + + /* Update volume level */ + am->stream.volume_level[volume_type] = level; + AUDIO_LOG_INFO("set_volume_level:session(%d), %d(%s)", am->session.session, level, __get_volume_type_string_by_idx(volume_type)); + } +#ifdef USE_FMRADIO_V4L2_SPRD + if (volume_type == AUDIO_VOLUME_TYPE_MEDIA && am->session.is_radio_on == 1) { + audio_ret = _set_volume_level_fmradio(am, level); + } +#endif + return audio_ret; +} + +audio_return_t audio_get_gain_value (void *userdata, audio_info_t *info, uint32_t volume_type, double *value) +{ + audio_mgr_t *am = (audio_mgr_t *)userdata; + audio_volume_gain_table_t *volume_gain_table; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(am->stream.volume_gain_table, AUDIO_ERR_PARAMETER); + + if (info != NULL) { + volume_gain_table = am->stream.volume_gain_table; + *value = volume_gain_table->gain[info->stream.gain_type]; + } + + return AUDIO_RET_OK; +} + +audio_return_t audio_get_mute (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t *mute) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + /* TODO. Not implemented */ + + return audio_ret; +} + +audio_return_t audio_set_mute (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t mute) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + /* TODO. Not implemented */ + + return audio_ret; +} diff --git a/tizen-audio-ucm.c b/tizen-audio-ucm.c new file mode 100644 index 0000000..c987e30 --- /dev/null +++ b/tizen-audio-ucm.c @@ -0,0 +1,466 @@ +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 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 + +audio_return_t _audio_ucm_init (audio_mgr_t *am) +{ + snd_use_case_mgr_open(&am->ucm.uc_mgr, ALSA_DEFAULT_CARD); + + if (!am->ucm.uc_mgr) { + AUDIO_LOG_ERROR("uc_mgr open failed"); + return AUDIO_ERR_RESOURCE; + } + pthread_mutex_init(&(am->ucm.mutex), NULL); + + return AUDIO_RET_OK; +} + +audio_return_t _audio_ucm_deinit (audio_mgr_t *am) +{ + if (am->ucm.uc_mgr != NULL) { + snd_use_case_mgr_close(am->ucm.uc_mgr); + am->ucm.uc_mgr = NULL; + } + + pthread_mutex_destroy(&am->ucm.mutex); + + return AUDIO_RET_OK; +} + +void _audio_ucm_get_device_name (audio_mgr_t *am, const char *use_case, audio_direction_t direction, const char **value) +{ + char identifier[70] = {0}; + + if (direction == AUDIO_DIRECTION_IN) { + sprintf(identifier, "CapturePCM//%s", use_case); + } else { + sprintf(identifier, "PlaybackPCM//%s", use_case); + } + snd_use_case_get(am->ucm.uc_mgr, identifier, value); +} + +static inline void __add_ucm_device_info (audio_mgr_t *am, const char *use_case, audio_direction_t direction, audio_device_info_t *device_info_list, int *device_info_count) +{ + audio_device_info_t *device_info; + const char *device_name = NULL; + char *needle = NULL; + + _audio_ucm_get_device_name(am, use_case, direction, &device_name); + if (device_name) { + device_info = &device_info_list[(*device_info_count)++]; + + memset(device_info, 0x00, sizeof(audio_device_info_t)); + device_info->api = AUDIO_DEVICE_API_ALSA; + device_info->direction = direction; + needle = strstr(&device_name[3], ","); + if (needle) { + device_info->alsa.device_idx = *(needle+1) - '0'; + device_info->alsa.card_name = strndup(&device_name[3], needle - (device_name+3)); + device_info->alsa.card_idx = snd_card_get_index(device_info->alsa.card_name); + AUDIO_LOG_DEBUG("Card name: %s", device_info->alsa.card_name); + } + + free((void *)device_name); + } +} + +int _audio_ucm_fill_device_info_list (audio_mgr_t *am, audio_device_info_t *device_info_list, const char *verb) +{ + int device_info_count = 0; + const char *curr_verb = NULL; + + if (!verb) { + snd_use_case_get(am->ucm.uc_mgr, "_verb", &curr_verb); + verb = curr_verb; + } + + /* prepare destination */ + /*If the devices are VOICECALL LOOPBACK or FMRADIO then pulseaudio need not get the device notification*/ + if (verb) { + if (strncmp(verb, AUDIO_USE_CASE_VERB_VOICECALL, strlen(AUDIO_USE_CASE_VERB_VOICECALL)) && + strncmp(verb, AUDIO_USE_CASE_VERB_LOOPBACK, strlen(AUDIO_USE_CASE_VERB_LOOPBACK))) { + __add_ucm_device_info(am, verb, AUDIO_DIRECTION_IN, device_info_list, &device_info_count); + if(strncmp(verb, AUDIO_USE_CASE_VERB_FMRADIO, strlen(AUDIO_USE_CASE_VERB_FMRADIO))) { + __add_ucm_device_info(am, verb, AUDIO_DIRECTION_OUT, device_info_list, &device_info_count); + } + } + + if (curr_verb) + free((void *)curr_verb); + + } + + return device_info_count; +} + +static void __dump_use_case(const char *verb, const char *devices[], int dev_count, const char *modifiers[], int mod_count, char *dump) +{ + int i, len; + + len = sprintf(dump, "Verb [ %s ] Devices [ ", verb ? verb : AUDIO_USE_CASE_VERB_INACTIVE); + if (len > 0) + dump += len; + + for (i = 0; i < dev_count; i++) { + if (i != dev_count - 1) { + len = sprintf(dump, "%s, ", devices[i]); + } else { + len = sprintf(dump, "%s", devices[i]); + } + if (len > 0) + dump += len; + } + + len = sprintf(dump, " ] Modifier [ "); + if (len > 0) + dump += len; + + for (i = 0; i < mod_count; i++) { + if (i != mod_count - 1) { + len = sprintf(dump, "%s, ", modifiers[i]); + } else { + len = sprintf(dump, "%s", modifiers[i]); + } + if (len > 0) + dump += len; + } + + len = sprintf(dump, " ]"); + if (len > 0) + dump += len; + + *dump = '\0'; +} + +#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 + +/* 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 _audio_ucm_set_use_case (audio_mgr_t *am, 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; + char dump_str[512]; + int is_radio_dev_on = 0, is_radio_mod_on = 0; + + pthread_mutex_lock(&am->ucm.mutex); + + if (!am->ucm.uc_mgr || !verb) { + pthread_mutex_unlock(&am->ucm.mutex); + return AUDIO_ERR_PARAMETER; + } + + snd_use_case_get(am->ucm.uc_mgr, "_verb", &old_verb); + old_dev_count = snd_use_case_get_list(am->ucm.uc_mgr, "_enadevs", &old_dev_list); + old_mod_count = snd_use_case_get_list(am->ucm.uc_mgr, "_enamods", &old_mod_list); + __dump_use_case(old_verb, old_dev_list, old_dev_count, old_mod_list, old_mod_count, &dump_str[0]); + AUDIO_LOG_INFO(">>> UCM current %s", dump_str); + + 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(verb, devices, dev_count, modifiers, mod_count, &dump_str[0]); + AUDIO_LOG_INFO("> UCM requested %s", dump_str); + + 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; + + if (is_radio_mod_on == 0 && streq(old_mod_list[i], AUDIO_USE_CASE_MODIFIER_FMRADIO)) { + is_radio_mod_on = 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; + + if (is_radio_dev_on == 0 && streq(old_dev_list[i], AUDIO_USE_CASE_DEV_FMRADIO)) { + is_radio_dev_on = 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(am->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(am->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(am->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(am->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(am->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(am->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(am->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(am->ucm.uc_mgr, "_verb", &new_verb); + new_dev_count = snd_use_case_get_list(am->ucm.uc_mgr, "_enadevs", &new_dev_list); + new_mod_count = snd_use_case_get_list(am->ucm.uc_mgr, "_enamods", &new_mod_list); + __dump_use_case(new_verb, new_dev_list, new_dev_count, new_mod_list, new_mod_count, &dump_str[0]); + AUDIO_LOG_INFO("<<< UCM changed %s", dump_str); + + 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); + } + + pthread_mutex_unlock(&am->ucm.mutex); + + return audio_ret; +} + + +audio_return_t _audio_ucm_get_verb (audio_mgr_t *am, const char **value) +{ + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + AUDIO_RETURN_VAL_IF_FAIL(value, AUDIO_ERR_PARAMETER); + + if ((ret = snd_use_case_get(am->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 _audio_ucm_reset_use_case (audio_mgr_t *am) +{ + audio_return_t ret = AUDIO_RET_OK; + + AUDIO_RETURN_VAL_IF_FAIL(am, AUDIO_ERR_PARAMETER); + + AUDIO_LOG_INFO(">>> UCM reset Verb [ %s ]", AUDIO_USE_CASE_VERB_INACTIVE); + + if ((ret = snd_use_case_set(am->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-util.c b/tizen-audio-util.c new file mode 100644 index 0000000..5b2477d --- /dev/null +++ b/tizen-audio-util.c @@ -0,0 +1,357 @@ +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 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" + +audio_return_t _audio_util_init (audio_mgr_t *am) +{ + pthread_mutex_init(&(am->mixer.mutex), NULL); + return AUDIO_RET_OK; +} + +audio_return_t _audio_util_deinit (audio_mgr_t *am) +{ + pthread_mutex_destroy(&(am->mixer.mutex)); + return AUDIO_RET_OK; +} + +#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 _audio_mixer_control_set_param(audio_mgr_t *am, const char* ctl_name, snd_ctl_elem_value_t* param, int size) +{ + /* TODO. */ + return AUDIO_RET_OK; +} + +audio_return_t audio_mixer_control_get_value (void *userdata, const char *ctl_name, int *val) +{ + audio_return_t audio_ret = AUDIO_RET_OK; + audio_mgr_t *am = (audio_mgr_t *)userdata; + audio_ret = _audio_mixer_control_get_value(am, ctl_name, val); + return audio_ret; +} + +audio_return_t _audio_mixer_control_get_value(audio_mgr_t *am, 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; + + pthread_mutex_lock(&(am->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(&(am->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(&(am->mixer.mutex)); + return AUDIO_RET_USE_HW_CONTROL; + +close: + AUDIO_LOG_ERROR ("Error\n"); + snd_ctl_close(handle); + pthread_mutex_unlock(&(am->mixer.mutex)); + return AUDIO_ERR_UNDEFINED; +} + +audio_return_t _audio_mixer_control_set_value(audio_mgr_t *am, 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; + + char *card_name = NULL; + int ret = 0, count = 0, i = 0; + + pthread_mutex_lock(&(am->mixer.mutex)); + + ret = snd_ctl_open(&handle, ALSA_DEFAULT_CARD, 0); + if (ret < 0) { + AUDIO_LOG_ERROR("snd_ctl_open error, card: %s: %s", card_name, snd_strerror(ret)); + pthread_mutex_unlock(&(am->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(&(am->mixer.mutex)); + return AUDIO_RET_USE_HW_CONTROL; + +close: + AUDIO_LOG_ERROR("Error"); + snd_ctl_close(handle); + pthread_mutex_unlock(&(am->mixer.mutex)); + return -1; +} + +audio_return_t _audio_mixer_control_set_value_string(audio_mgr_t *am, const char* ctl_name, const char* value) +{ + /* TODO. */ + return AUDIO_RET_OK; +} + + +audio_return_t _audio_mixer_control_get_element(audio_mgr_t *am, const char *ctl_name, snd_hctl_elem_t **elem) +{ + /* TODO. */ + return AUDIO_RET_OK; +} + + +/* Generic snd pcm interface APIs */ +audio_return_t _audio_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; + + 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_ERROR("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 _audio_pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event, uint32_t start_threshold, uint32_t rate) +{ + return AUDIO_ERR_NOT_IMPLEMENTED; +} diff --git a/tizen-audio.c b/tizen-audio.c new file mode 100644 index 0000000..9913307 --- /dev/null +++ b/tizen-audio.c @@ -0,0 +1,131 @@ +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 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" + +int audio_get_revision (void) +{ + return AUDIO_REVISION; +} + +audio_return_t audio_init (void **userdata, void *platform_data) +{ + audio_mgr_t *am; + audio_return_t ret = AUDIO_RET_OK; + + if (!(am = malloc(sizeof(audio_mgr_t)))) { + AUDIO_LOG_ERROR("am malloc failed"); + return AUDIO_ERR_RESOURCE; + } + am->platform_data = platform_data; + memset(&am->cb_intf, 0, sizeof(audio_cb_interface_t)); + if (AUDIO_IS_ERROR((ret = _audio_session_init(am)))) { + AUDIO_LOG_ERROR("session init failed"); + goto error_exit; + } + if (AUDIO_IS_ERROR((ret = _audio_device_init(am)))) { + AUDIO_LOG_ERROR("device init failed"); + goto error_exit; + } + if (AUDIO_IS_ERROR((ret = _audio_stream_init(am)))) { + AUDIO_LOG_ERROR("stream init failed"); + goto error_exit; + } + if (AUDIO_IS_ERROR((ret = _audio_ucm_init(am)))) { + AUDIO_LOG_ERROR("ucm init failed"); + goto error_exit; + } + if (AUDIO_IS_ERROR((ret = _audio_modem_init(am)))) { + AUDIO_LOG_ERROR("modem init failed"); + /* We don't want to stop even though modem failed */ + /* goto error_exit; */ + } + if (AUDIO_IS_ERROR((ret = _audio_util_init(am)))) { + AUDIO_LOG_ERROR("mixer init failed"); + goto error_exit; + } + + *userdata = (void *)am; + return AUDIO_RET_OK; + +error_exit: + if (am) + free(am); + + return ret; +} + +audio_return_t audio_deinit (void **userdata) +{ + audio_mgr_t *am = (audio_mgr_t *)*userdata; + + if (am) { + _audio_session_deinit(am); + _audio_device_deinit(am); + _audio_stream_deinit(am); + _audio_ucm_deinit(am); + _audio_modem_deinit(am); + _audio_util_deinit(am); + free(am); + *userdata = NULL; + } + + return AUDIO_RET_OK; +} + +/* this function is only called from audio tuning app for updating volume */ +audio_return_t audio_reset (void **userdata) +{ + audio_mgr_t *am = (audio_mgr_t *)*userdata; + audio_return_t ret = AUDIO_RET_OK; + + if (am) { + _audio_stream_deinit(am); + + if (AUDIO_IS_ERROR((ret = _audio_stream_init(am)))) { + AUDIO_LOG_ERROR("stream init failed"); + goto error_exit; + } + } + + return AUDIO_RET_OK; + +error_exit: + if (am) + free(am); + *userdata = NULL; + + return ret; +} + +audio_return_t audio_set_callback (void *userdata, audio_cb_interface_t *cb_interface) +{ + audio_mgr_t *am = (audio_mgr_t *)userdata; + + if (am) { + memcpy(&am->cb_intf, cb_interface, sizeof(audio_cb_interface_t)); + return AUDIO_RET_OK; + } else { + return AUDIO_ERR_PARAMETER; + } +} diff --git a/tizen-audio.h b/tizen-audio.h new file mode 100644 index 0000000..f869889 --- /dev/null +++ b/tizen-audio.h @@ -0,0 +1,279 @@ +#ifndef footizenaudiofoo +#define footizenaudiofoo + +/* + * audio-hal + * + * Copyright (c) 2000 - 2013 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 <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#define AUDIO_REVISION 1 + +/* Error code */ + +#define AUDIO_IS_ERROR(ret) (ret < 0) + +typedef enum audio_return { + AUDIO_RET_OK = 0, + AUDIO_RET_USE_HW_CONTROL = (int32_t)0x40001000, + 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_NOT_IMPLEMENTED = (int32_t)0x80001004, +} audio_return_t ; + + +/* Session */ +typedef enum audio_session { + AUDIO_SESSION_MEDIA, + AUDIO_SESSION_VOICECALL, + AUDIO_SESSION_VIDEOCALL, + AUDIO_SESSION_VOIP, + AUDIO_SESSION_FMRADIO, + AUDIO_SESSION_CAMCORDER, + AUDIO_SESSION_NOTIFICATION, + AUDIO_SESSION_ALARM, + AUDIO_SESSION_EMERGENCY, + AUDIO_SESSION_VOICE_RECOGNITION, + AUDIO_SESSION_MAX +} audio_session_t; + +/* Sub session */ +typedef enum audio_subsession { + AUDIO_SUBSESSION_NONE, + AUDIO_SUBSESSION_VOICE, + AUDIO_SUBSESSION_RINGTONE, + AUDIO_SUBSESSION_MEDIA, + AUDIO_SUBSESSION_INIT, + AUDIO_SUBSESSION_VR_NORMAL, + AUDIO_SUBSESSION_VR_DRIVE, + AUDIO_SUBSESSION_STEREO_REC, + AUDIO_SUBSESSION_MONO_REC, + AUDIO_SUBSESSION_MAX +} audio_subsession_t; + +/* Session command */ +typedef enum audio_session_command { + AUDIO_SESSION_CMD_START, + AUDIO_SESSION_CMD_SUBSESSION, + AUDIO_SESSION_CMD_END, +} audio_session_command_t; + + +/* Direction */ +typedef enum audio_direction { + AUDIO_DIRECTION_NONE, + AUDIO_DIRECTION_IN, /**< Capture */ + AUDIO_DIRECTION_OUT, /**< Playback */ +} audio_direction_t; + + +/* Device */ + +typedef enum audio_device_in { + AUDIO_DEVICE_IN_NONE, + AUDIO_DEVICE_IN_MIC, /**< Device builtin mic. */ + AUDIO_DEVICE_IN_WIRED_ACCESSORY, /**< Wired input devices */ + AUDIO_DEVICE_IN_BT_SCO, /**< Bluetooth SCO device */ + AUDIO_DEVICE_IN_MAX, +} audio_device_in_t; + +typedef enum audio_device_out { + AUDIO_DEVICE_OUT_NONE, + AUDIO_DEVICE_OUT_SPEAKER, /**< Device builtin speaker */ + AUDIO_DEVICE_OUT_RECEIVER, /**< Device builtin receiver */ + AUDIO_DEVICE_OUT_WIRED_ACCESSORY, /**< Wired output devices such as headphone, headset, and so on. */ + AUDIO_DEVICE_OUT_BT_SCO, /**< Bluetooth SCO device */ + AUDIO_DEVICE_OUT_BT_A2DP, /**< Bluetooth A2DP device */ + AUDIO_DEVICE_OUT_DOCK, /**< DOCK device */ + AUDIO_DEVICE_OUT_HDMI, /**< HDMI device */ + AUDIO_DEVICE_OUT_MIRRORING, /**< MIRRORING device */ + AUDIO_DEVICE_OUT_USB_AUDIO, /**< USB Audio device */ + AUDIO_DEVICE_OUT_MULTIMEDIA_DOCK, /**< Multimedia DOCK device */ + AUDIO_DEVICE_OUT_MAX, +} audio_device_out_t; + +typedef enum audio_route_flag { + AUDIO_ROUTE_FLAG_NONE = 0, + AUDIO_ROUTE_FLAG_MUTE_POLICY = 0x00000001, + AUDIO_ROUTE_FLAG_DUAL_OUT = 0x00000002, + AUDIO_ROUTE_FLAG_NOISE_REDUCTION = 0x00000010, + AUDIO_ROUTE_FLAG_EXTRA_VOL = 0x00000020, + AUDIO_ROUTE_FLAG_NETWORK_WB = 0x00000040, + AUDIO_ROUTE_FLAG_BT_WB = 0x00000100, + AUDIO_ROUTE_FLAG_BT_NREC = 0x00000200, + AUDIO_ROUTE_FLAG_VOICE_COMMAND = 0x00040000, +} audio_route_flag_t; + +typedef enum audio_device_api { + AUDIO_DEVICE_API_UNKNOWN, + AUDIO_DEVICE_API_ALSA, + AUDIO_DEVICE_API_BLUEZ, +} audio_device_api_t; + +typedef enum audio_device_param { + AUDIO_DEVICE_PARAM_NONE, + AUDIO_DEVICE_PARAM_CHANNELS, + AUDIO_DEVICE_PARAM_SAMPLERATE, + AUDIO_DEVICE_PARAM_FRAGMENT_SIZE, + AUDIO_DEVICE_PARAM_FRAGMENT_NB, + AUDIO_DEVICE_PARAM_START_THRESHOLD, + AUDIO_DEVICE_PARAM_USE_MMAP, + AUDIO_DEVICE_PARAM_USE_TSCHED, + AUDIO_DEVICE_PARAM_TSCHED_BUF_SIZE, + AUDIO_DEVICE_PARAM_SUSPEND_TIMEOUT, + AUDIO_DEVICE_PARAM_ALTERNATE_RATE, + AUDIO_DEVICE_PARAM_MAX, +} audio_device_param_t; + +typedef struct audio_device_param_info { + audio_device_param_t param; + union { + int64_t s64_v; + uint64_t u64_v; + int32_t s32_v; + uint32_t u32_v; + }; +} audio_device_param_info_t; + +typedef struct audio_device_alsa_info { + char *card_name; + uint32_t card_idx; + uint32_t device_idx; +} audio_device_alsa_info_t; + +typedef struct audio_device_bluz_info { + char *protocol; + uint32_t nrec; +} audio_device_bluez_info_t; + +typedef struct audio_device_info { + audio_device_api_t api; + audio_direction_t direction; + char *name; + uint8_t is_default_device; + union { + audio_device_alsa_info_t alsa; + audio_device_bluez_info_t bluez; + }; +} audio_device_info_t; + + +/* Stream */ + +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_FIXED, /**< Fixed volume type */ + AUDIO_VOLUME_TYPE_MAX, /**< Volume type count */ +} audio_volume_t; + +#define AUDIO_VOLUME_TYPE_VCONF_MAX AUDIO_VOLUME_TYPE_FIXED + +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_stream_info { + char *name; + uint32_t samplerate; + uint8_t channels; + uint32_t volume_type; + uint32_t gain_type; +} audio_stream_info_t ; + + +/* Overall */ + +typedef struct audio_info { + audio_device_info_t device; + audio_stream_info_t stream; +} audio_info_t; + +typedef struct audio_cb_interface { + audio_return_t (*load_device)(void *platform_data, audio_device_info_t *device_info, audio_device_param_info_t *params); + audio_return_t (*open_device)(void *platform_data, audio_device_info_t *device_info, audio_device_param_info_t *params); + audio_return_t (*close_all_devices)(void *platform_data); + audio_return_t (*close_device)(void *platform_data, audio_device_info_t *device_info); + audio_return_t (*unload_device)(void *platform_data, audio_device_info_t *device_info); +} audio_cb_interface_t; + +typedef struct audio_interface { + audio_return_t (*init)(void **userdata, void *platform_data); + audio_return_t (*deinit)(void **userdata); + audio_return_t (*reset)(void **userdata); + audio_return_t (*set_callback)(void *userdata, audio_cb_interface_t *cb_interface); + audio_return_t (*get_volume_level_max)(void *userdata, uint32_t volume_type, uint32_t *level); + audio_return_t (*get_volume_level)(void *userdata, uint32_t volume_type, uint32_t *level); + audio_return_t (*get_volume_value)(void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t level, double *value); + audio_return_t (*set_volume_level)(void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t level); + audio_return_t (*set_volume_value)(void *userdata, audio_info_t *info, uint32_t volume_type, double* value); + audio_return_t (*get_gain_value)(void *userdata, audio_info_t *info, uint32_t volume_type, double *value); + audio_return_t (*get_mute)(void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t *mute); + audio_return_t (*set_mute)(void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t mute); + audio_return_t (*set_session)(void *userdata, uint32_t session, uint32_t subsession, uint32_t cmd); + audio_return_t (*set_route)(void *userdata, uint32_t session, uint32_t subsession, uint32_t device_in, uint32_t device_out, uint32_t route_flag); + audio_return_t (*alsa_pcm_open)(void *userdata, void **pcm_handle, char *device_name, uint32_t direction, int mode); + audio_return_t (*alsa_pcm_close)(void *userdata, void *pcm_handle); + audio_return_t (*set_mixer_value_string)(void *userdata, const char* ctl, const char* value); + audio_return_t (*set_route_info)(void *userdata, const char* key, const char* value); + +} audio_interface_t; + +int audio_get_revision (void); +audio_return_t audio_init (void **userdata, void *platform_data); +audio_return_t audio_deinit (void **userdata); +audio_return_t audio_reset (void **userdata); +audio_return_t audio_set_callback (void *userdata, audio_cb_interface_t *cb_interface); +audio_return_t audio_get_volume_level_max (void *userdata, uint32_t volume_type, uint32_t *level); +audio_return_t audio_get_volume_level (void *userdata, uint32_t volume_type, uint32_t *level); +audio_return_t audio_get_volume_value (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t level, double *value); +audio_return_t audio_set_volume_level (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t level); +audio_return_t audio_set_volume_value (void *userdata, audio_info_t *info, uint32_t volume_type, double* value); +audio_return_t audio_get_gain_value (void *userdata, audio_info_t *info, uint32_t volume_type, double *value); +audio_return_t audio_get_mute (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t *mute); +audio_return_t audio_set_mute (void *userdata, audio_info_t *info, uint32_t volume_type, uint32_t direction, uint32_t mute); +audio_return_t audio_set_session (void *userdata, uint32_t session, uint32_t subsession, uint32_t cmd); +audio_return_t audio_alsa_pcm_open (void *userdata, void **pcm_handle, char *device_name, uint32_t direction, int mode); +audio_return_t audio_alsa_pcm_close (void *userdata, void *pcm_handle); +audio_return_t audio_set_route (void *userdata, uint32_t session, uint32_t subsession, uint32_t device_in, uint32_t device_out, uint32_t route_flag); +audio_return_t audio_set_mixer_value_string(void *userdata, const char* ctl, const char* value); +audio_return_t audio_mixer_control_get_value (void *userdata, const char *ctl_name, int *val); +audio_return_t audio_set_route_info(void *userdata, const char* key, const char* value); + +#endif diff --git a/vb_control_parameters.h b/vb_control_parameters.h new file mode 100644 index 0000000..0b64e66 --- /dev/null +++ b/vb_control_parameters.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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 VBC_CONTROL_PARAMETERS_H +#define VBC_CONTROL_PARAMETERS_H + +#include "pthread.h" + + +#define BUF_SIZE 1024 + +#define VBC_PIPE_NAME_MAX_LEN 16 +#define VOIP_PIPE_NAME_MAX VBC_PIPE_NAME_MAX_LEN +#define NAME_LEN_MAX 16 + +#define AUDIO_XML_PATH "/usr/etc/audio_hw.xml" + +#define MODEM_T_ENABLE_PROPERTY "persist.modem.t.enable" +#define MODEM_W_ENABLE_PROPERTY "persist.modem.w.enable" + + +typedef enum { + CP_W, + CP_TG, + CP_MAX +}cp_type_t; + +/*support multiple call for multiple modem(cp0/cp1/...): +different modem is corresponding to different pipe and all pipes use the only vbc. +support multiple pipe: +1. change VBC_PIPE_COUNT +2. change the definition of s_vbc_ctrl_pipe_info. +3. change channel_id for different cp .On sharp, 0 for cp0, 1 for cp1,2 for ap +*/ + +typedef struct +{ + char s_vbc_ctrl_pipe_name[VBC_PIPE_NAME_MAX_LEN]; + int channel_id; + cp_type_t cp_type; +}vbc_ctrl_pipe_para_t; + + +struct voip_res +{ + cp_type_t cp_type; + int8_t pipe_name[VOIP_PIPE_NAME_MAX]; + int channel_id; + int enable; + int is_done; + void *adev; + pthread_t thread_id; +}; + +typedef struct +{ + int8_t index; + int is_switch; + int8_t is_ext; +}i2s_ctl_t; + + + +typedef struct debuginfo +{ + int enable; + int sleeptime_gate; + int pcmwritetime_gate; + int lastthis_outwritetime_gate; +}debuginfo; + +typedef struct{ + int num; + vbc_ctrl_pipe_para_t *vbc_ctrl_pipe_info; + i2s_ctl_t i2s_bt; + i2s_ctl_t i2s_extspk; + struct voip_res voip_res; + debuginfo debug_info; +}audio_modem_t; + +/*audio mode structure,we can expand for more fields if necessary*/ +typedef struct +{ + int index; + char mode_name[NAME_LEN_MAX]; + +}audio_mode_item_t; + +/*we mostly have four mode,(headset,headfree,handset,handsfree), + differet product may configure different mode number,htc have 25 modes.*/ +typedef struct{ + int num; + audio_mode_item_t *audio_mode_item_info; +}aud_mode_t; +struct modem_config_parse_state{ + audio_modem_t *modem_info; + vbc_ctrl_pipe_para_t *vbc_ctrl_pipe_info; + aud_mode_t *audio_mode_info; + audio_mode_item_t *audio_mode_item_info; +}; + +#endif + |