blob: 2b1ac87cbdd86a0735928f533ea764b519f434da [file] [log] [blame]
Shiv Maliyappanahalli34b585f2013-10-01 15:49:05 -07001/*
2 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
3 * Not a contribution.
4 *
5 * Copyright (C) 2013 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20#define LOG_TAG "voice"
21/*#define LOG_NDEBUG 0*/
22#define LOG_NDDEBUG 0
23
24#include <errno.h>
25#include <math.h>
26#include <cutils/log.h>
27#include <cutils/str_parms.h>
28
29#include "audio_hw.h"
30#include "voice.h"
31#include "voice_extn/voice_extn.h"
32#include "platform.h"
33#include "platform_api.h"
34
35struct pcm_config pcm_config_voice_call = {
36 .channels = 1,
37 .rate = 8000,
38 .period_size = 160,
39 .period_count = 2,
40 .format = PCM_FORMAT_S16_LE,
41};
42
43extern struct audio_usecase *get_usecase_from_list(struct audio_device *adev,
44 audio_usecase_t uc_id);
45extern int disable_snd_device(struct audio_device *adev,
46 snd_device_t snd_device,
47 bool update_mixer);
48extern int disable_audio_route(struct audio_device *adev,
49 struct audio_usecase *usecase,
50 bool update_mixer);
51
52extern int disable_snd_device(struct audio_device *adev,
53 snd_device_t snd_device,
54 bool update_mixer);
55extern int select_devices(struct audio_device *adev,
56 audio_usecase_t uc_id);
57
58static struct voice_session *voice_get_session_from_use_case(struct audio_device *adev,
59 audio_usecase_t usecase_id)
60{
61 struct voice_session *session = NULL;
62 int ret = 0;
63
64 ret = voice_extn_get_session_from_use_case(adev, usecase_id, &session);
65 if (ret == -ENOSYS) {
66 session = &adev->voice.session[VOICE_SESS_IDX];
67 }
68
69 return session;
70}
71
72int stop_call(struct audio_device *adev, audio_usecase_t usecase_id)
73{
74 int i, ret = 0;
75 struct audio_usecase *uc_info;
76 struct voice_session *session = NULL;
77
78 ALOGD("%s: enter", __func__);
79
80 session = (struct voice_session *)voice_get_session_from_use_case(adev, usecase_id);
81 session->state.current = CALL_INACTIVE;
82
83 ret = platform_stop_voice_call(adev->platform);
84
85 /* 1. Close the PCM devices */
86 if (session->pcm_rx) {
87 pcm_close(session->pcm_rx);
88 session->pcm_rx = NULL;
89 }
90 if (session->pcm_tx) {
91 pcm_close(session->pcm_tx);
92 session->pcm_tx = NULL;
93 }
94
95 uc_info = get_usecase_from_list(adev, usecase_id);
96 if (uc_info == NULL) {
97 ALOGE("%s: Could not find the usecase (%d) in the list",
98 __func__, usecase_id);
99 return -EINVAL;
100 }
101
102 /* 2. Get and set stream specific mixer controls */
103 disable_audio_route(adev, uc_info, true);
104
105 /* 3. Disable the rx and tx devices */
106 disable_snd_device(adev, uc_info->out_snd_device, false);
107 disable_snd_device(adev, uc_info->in_snd_device, true);
108
109 list_remove(&uc_info->list);
110 free(uc_info);
111
112 ALOGD("%s: exit: status(%d)", __func__, ret);
113 return ret;
114}
115
116int start_call(struct audio_device *adev, audio_usecase_t usecase_id)
117{
118 int i, ret = 0;
119 struct audio_usecase *uc_info;
120 int pcm_dev_rx_id, pcm_dev_tx_id;
121 struct voice_session *session = NULL;
122
123 ALOGD("%s: enter", __func__);
124
125 session = (struct voice_session *)voice_get_session_from_use_case(adev, usecase_id);
126
127 uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
128 uc_info->id = usecase_id;
129 uc_info->type = VOICE_CALL;
130 uc_info->stream.out = adev->primary_output;
131 uc_info->devices = adev->primary_output->devices;
132 uc_info->in_snd_device = SND_DEVICE_NONE;
133 uc_info->out_snd_device = SND_DEVICE_NONE;
134
135 list_add_tail(&adev->usecase_list, &uc_info->list);
136
137 select_devices(adev, usecase_id);
138
139 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
140 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
141
142 if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0) {
143 ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(%d)",
144 __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
145 ret = -EIO;
146 goto error_start_voice;
147 }
148
149 ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
150 __func__, SOUND_CARD, pcm_dev_rx_id);
151 session->pcm_rx = pcm_open(SOUND_CARD,
152 pcm_dev_rx_id,
153 PCM_OUT, &pcm_config_voice_call);
154 if (session->pcm_rx && !pcm_is_ready(session->pcm_rx)) {
155 ALOGE("%s: %s", __func__, pcm_get_error(session->pcm_rx));
156 ret = -EIO;
157 goto error_start_voice;
158 }
159
160 ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
161 __func__, SOUND_CARD, pcm_dev_tx_id);
162 session->pcm_tx = pcm_open(SOUND_CARD,
163 pcm_dev_tx_id,
164 PCM_IN, &pcm_config_voice_call);
165 if (session->pcm_tx && !pcm_is_ready(session->pcm_tx)) {
166 ALOGE("%s: %s", __func__, pcm_get_error(session->pcm_tx));
167 ret = -EIO;
168 goto error_start_voice;
169 }
170 pcm_start(session->pcm_rx);
171 pcm_start(session->pcm_tx);
172
173 ret = platform_start_voice_call(adev->platform);
174 if (ret < 0) {
175 ALOGE("%s: platform_start_voice_call error %d\n", __func__, ret);
176 goto error_start_voice;
177 }
178
179 session->state.current = CALL_ACTIVE;
180 return 0;
181
182error_start_voice:
183 stop_call(adev, usecase_id);
184
185 ALOGD("%s: exit: status(%d)", __func__, ret);
186 return ret;
187}
188
189bool voice_is_in_call(struct audio_device *adev)
190{
191 bool in_call = false;
192 int ret = 0;
193
194 ret = voice_extn_is_in_call(adev, &in_call);
195 if (ret == -ENOSYS) {
196 in_call = (adev->voice.session[VOICE_SESS_IDX].state.current == CALL_ACTIVE) ? true : false;
197 }
198
199 return in_call;
200}
201
202int voice_set_mic_mute(struct audio_device *adev, bool state)
203{
204 int err = 0;
205
206 pthread_mutex_lock(&adev->lock);
207
208 err = platform_set_mic_mute(adev->platform, state);
209 if (!err) {
210 adev->voice.mic_mute = state;
211 }
212
213 pthread_mutex_unlock(&adev->lock);
214 return err;
215}
216
217bool voice_get_mic_mute(struct audio_device *adev)
218{
219 return adev->voice.mic_mute;
220}
221
222int voice_set_volume(struct audio_device *adev, float volume)
223{
224 int vol, err = 0;
225
226 pthread_mutex_lock(&adev->lock);
227 if (adev->mode == AUDIO_MODE_IN_CALL) {
228 if (volume < 0.0) {
229 volume = 0.0;
230 } else if (volume > 1.0) {
231 volume = 1.0;
232 }
233
234 vol = lrint(volume * 100.0);
235
236 // Voice volume levels from android are mapped to driver volume levels as follows.
237 // 0 -> 5, 20 -> 4, 40 ->3, 60 -> 2, 80 -> 1, 100 -> 0
238 // So adjust the volume to get the correct volume index in driver
239 vol = 100 - vol;
240
241 err = platform_set_voice_volume(adev->platform, vol);
242 if (!err) {
243 adev->voice.volume = volume;
244 }
245 }
246 pthread_mutex_unlock(&adev->lock);
247 return err;
248}
249
250int voice_start_call(struct audio_device *adev)
251{
252 int ret = 0;
253
254 ret = voice_extn_update_calls(adev);
255 if (ret == -ENOSYS) {
256 ret = start_call(adev, USECASE_VOICE_CALL);
257 }
258
259 return ret;
260}
261
262int voice_stop_call(struct audio_device *adev)
263{
264 int ret = 0;
265
266 ret = voice_extn_update_calls(adev);
267 if (ret == -ENOSYS) {
268 ret = stop_call(adev, USECASE_VOICE_CALL);
269 }
270
271 return ret;
272}
273
274int voice_set_parameters(struct audio_device *adev, struct str_parms *parms)
275{
276 char *str;
277 char value[32];
278 int val;
279 int ret = 0;
280
281 ALOGV("%s: enter: %s", __func__, str_parms_to_str(parms));
282
283 voice_extn_set_parameters(adev, parms);
284
285 ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TTY_MODE, value, sizeof(value));
286 if (ret >= 0) {
287 int tty_mode;
288 str_parms_del(parms, AUDIO_PARAMETER_KEY_TTY_MODE);
289 if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0)
290 tty_mode = TTY_MODE_OFF;
291 else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0)
292 tty_mode = TTY_MODE_VCO;
293 else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0)
294 tty_mode = TTY_MODE_HCO;
295 else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0)
296 tty_mode = TTY_MODE_FULL;
297 else {
298 ret = -EINVAL;
299 goto done;
300 }
301
302 pthread_mutex_lock(&adev->lock);
303 if (tty_mode != adev->voice.tty_mode) {
304 adev->voice.tty_mode = tty_mode;
305 adev->acdb_settings = (adev->acdb_settings & TTY_MODE_CLEAR) | tty_mode;
306 if (voice_is_in_call(adev))
307 //todo: what about voice2, volte and qchat usecases?
308 select_devices(adev, USECASE_VOICE_CALL);
309 }
310 pthread_mutex_unlock(&adev->lock);
311 }
312
313done:
314 ALOGV("%s: exit with code(%d)", __func__, ret);
315 return ret;
316}
317
318void voice_init(struct audio_device *adev)
319{
320 int i = 0;
321
322 memset(&adev->voice, 0, sizeof(adev->voice));
323 adev->voice.tty_mode = TTY_MODE_OFF;
324 adev->voice.volume = 1.0f;
325 adev->voice.mic_mute = false;
326 for (i = 0; i < MAX_VOICE_SESSIONS; i++) {
327 adev->voice.session[i].pcm_rx = NULL;
328 adev->voice.session[i].pcm_tx = NULL;
329 adev->voice.session[i].state.current = CALL_INACTIVE;
330 adev->voice.session[i].state.new = CALL_INACTIVE;
331 adev->voice.session[i].vsid = 0;
332 }
333
334 voice_extn_init(adev);
335}
336
337