blob: 39e2dd961486977db0aa43bcc3f4cf15cfe14684 [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;
Shiv Maliyappanahallida107642013-10-17 11:16:13 -0700122 struct pcm_config voice_config = pcm_config_voice_call;
Shiv Maliyappanahalli34b585f2013-10-01 15:49:05 -0700123 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,
Shiv Maliyappanahallida107642013-10-17 11:16:13 -0700153 PCM_OUT, &voice_config);
Shiv Maliyappanahalli34b585f2013-10-01 15:49:05 -0700154 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,
Shiv Maliyappanahallida107642013-10-17 11:16:13 -0700164 PCM_IN, &voice_config);
Shiv Maliyappanahalli34b585f2013-10-01 15:49:05 -0700165 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
Shruthi Krishnaace10852013-10-25 14:32:12 -0700173 voice_set_volume(adev, adev->voice.volume);
174
Shiv Maliyappanahalli34b585f2013-10-01 15:49:05 -0700175 ret = platform_start_voice_call(adev->platform);
176 if (ret < 0) {
177 ALOGE("%s: platform_start_voice_call error %d\n", __func__, ret);
178 goto error_start_voice;
179 }
180
181 session->state.current = CALL_ACTIVE;
182 return 0;
183
184error_start_voice:
185 stop_call(adev, usecase_id);
186
187 ALOGD("%s: exit: status(%d)", __func__, ret);
188 return ret;
189}
190
191bool voice_is_in_call(struct audio_device *adev)
192{
193 bool in_call = false;
194 int ret = 0;
195
196 ret = voice_extn_is_in_call(adev, &in_call);
197 if (ret == -ENOSYS) {
198 in_call = (adev->voice.session[VOICE_SESS_IDX].state.current == CALL_ACTIVE) ? true : false;
199 }
200
201 return in_call;
202}
203
Shiv Maliyappanahallida107642013-10-17 11:16:13 -0700204uint32_t voice_get_active_session_id(struct audio_device *adev)
205{
206 int ret = 0;
207 uint32_t session_id;
208
209 ret = voice_extn_get_active_session_id(adev, &session_id);
210 if (ret == -ENOSYS) {
211 session_id = VOICE_VSID;
212 }
213 return session_id;
214}
215
216int voice_check_and_set_incall_rec_usecase(struct audio_device *adev,
217 struct stream_in *in)
218{
219 int ret = 0;
220 uint32_t session_id;
221 int usecase_id;
222
223 if (voice_is_in_call(adev)) {
224 switch (in->source) {
225 case AUDIO_SOURCE_VOICE_UPLINK:
226 in->usecase = USECASE_INCALL_REC_UPLINK;
227 break;
228 case AUDIO_SOURCE_VOICE_DOWNLINK:
229 in->usecase = USECASE_INCALL_REC_DOWNLINK;
230 break;
231 case AUDIO_SOURCE_VOICE_CALL:
232 in->usecase = USECASE_INCALL_REC_UPLINK_AND_DOWNLINK;
233 break;
234 default:
235 ALOGV("%s: Source type %d doesnt match incall recording criteria",
236 __func__, in->source);
237 return ret;
238 }
239
240 in->config = pcm_config_voice_call;
241 session_id = voice_get_active_session_id(adev);
242 ret = platform_set_incall_recoding_session_id(adev->platform,
243 session_id);
244 ALOGV("%s: Update usecase to %d",__func__, in->usecase);
245 } else {
246 ALOGV("%s: voice call not active", __func__);
247 }
248
249 return ret;
250}
251
Shiv Maliyappanahallif3b9a422013-10-22 16:38:08 -0700252int voice_check_and_set_incall_music_usecase(struct audio_device *adev,
253 struct stream_out *out)
254{
255 int ret = 0;
256
257 ret = voice_extn_check_and_set_incall_music_usecase(adev, out);
258 if (ret == -ENOSYS) {
259 /* Incall music delivery is used only for LCH call state */
260 ret = -EINVAL;
261 }
262
263 return ret;
264}
265
Shiv Maliyappanahalli34b585f2013-10-01 15:49:05 -0700266int voice_set_mic_mute(struct audio_device *adev, bool state)
267{
268 int err = 0;
269
270 pthread_mutex_lock(&adev->lock);
271
272 err = platform_set_mic_mute(adev->platform, state);
273 if (!err) {
274 adev->voice.mic_mute = state;
275 }
276
277 pthread_mutex_unlock(&adev->lock);
278 return err;
279}
280
281bool voice_get_mic_mute(struct audio_device *adev)
282{
283 return adev->voice.mic_mute;
284}
285
286int voice_set_volume(struct audio_device *adev, float volume)
287{
288 int vol, err = 0;
289
Shruthi Krishnaace10852013-10-25 14:32:12 -0700290 adev->voice.volume = volume;
Shiv Maliyappanahalli34b585f2013-10-01 15:49:05 -0700291 if (adev->mode == AUDIO_MODE_IN_CALL) {
292 if (volume < 0.0) {
293 volume = 0.0;
294 } else if (volume > 1.0) {
295 volume = 1.0;
296 }
297
298 vol = lrint(volume * 100.0);
299
300 // Voice volume levels from android are mapped to driver volume levels as follows.
301 // 0 -> 5, 20 -> 4, 40 ->3, 60 -> 2, 80 -> 1, 100 -> 0
302 // So adjust the volume to get the correct volume index in driver
303 vol = 100 - vol;
304
305 err = platform_set_voice_volume(adev->platform, vol);
Shiv Maliyappanahalli34b585f2013-10-01 15:49:05 -0700306 }
Shruthi Krishnaace10852013-10-25 14:32:12 -0700307
Shiv Maliyappanahalli34b585f2013-10-01 15:49:05 -0700308 return err;
309}
310
311int voice_start_call(struct audio_device *adev)
312{
313 int ret = 0;
314
315 ret = voice_extn_update_calls(adev);
316 if (ret == -ENOSYS) {
317 ret = start_call(adev, USECASE_VOICE_CALL);
318 }
319
320 return ret;
321}
322
323int voice_stop_call(struct audio_device *adev)
324{
325 int ret = 0;
326
327 ret = voice_extn_update_calls(adev);
328 if (ret == -ENOSYS) {
329 ret = stop_call(adev, USECASE_VOICE_CALL);
330 }
331
332 return ret;
333}
334
335int voice_set_parameters(struct audio_device *adev, struct str_parms *parms)
336{
337 char *str;
338 char value[32];
339 int val;
340 int ret = 0;
341
342 ALOGV("%s: enter: %s", __func__, str_parms_to_str(parms));
343
344 voice_extn_set_parameters(adev, parms);
345
346 ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TTY_MODE, value, sizeof(value));
347 if (ret >= 0) {
348 int tty_mode;
349 str_parms_del(parms, AUDIO_PARAMETER_KEY_TTY_MODE);
350 if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0)
351 tty_mode = TTY_MODE_OFF;
352 else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0)
353 tty_mode = TTY_MODE_VCO;
354 else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0)
355 tty_mode = TTY_MODE_HCO;
356 else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0)
357 tty_mode = TTY_MODE_FULL;
358 else {
359 ret = -EINVAL;
360 goto done;
361 }
362
363 pthread_mutex_lock(&adev->lock);
364 if (tty_mode != adev->voice.tty_mode) {
365 adev->voice.tty_mode = tty_mode;
366 adev->acdb_settings = (adev->acdb_settings & TTY_MODE_CLEAR) | tty_mode;
367 if (voice_is_in_call(adev))
368 //todo: what about voice2, volte and qchat usecases?
369 select_devices(adev, USECASE_VOICE_CALL);
370 }
371 pthread_mutex_unlock(&adev->lock);
372 }
373
374done:
375 ALOGV("%s: exit with code(%d)", __func__, ret);
376 return ret;
377}
378
379void voice_init(struct audio_device *adev)
380{
381 int i = 0;
382
383 memset(&adev->voice, 0, sizeof(adev->voice));
384 adev->voice.tty_mode = TTY_MODE_OFF;
385 adev->voice.volume = 1.0f;
386 adev->voice.mic_mute = false;
387 for (i = 0; i < MAX_VOICE_SESSIONS; i++) {
388 adev->voice.session[i].pcm_rx = NULL;
389 adev->voice.session[i].pcm_tx = NULL;
390 adev->voice.session[i].state.current = CALL_INACTIVE;
391 adev->voice.session[i].state.new = CALL_INACTIVE;
392 adev->voice.session[i].vsid = 0;
393 }
394
395 voice_extn_init(adev);
396}
397
398