blob: 1f36b36b3314699bb64089c816ed7e4c6f2770fe [file] [log] [blame]
Vineeta Srivastava4b89e372014-06-19 14:21:42 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "voice"
18/*#define LOG_NDEBUG 0*/
19#define LOG_NDDEBUG 0
20
Vineeta Srivastava7c685a12015-03-20 15:34:33 -070021#include <stdlib.h>
Vineeta Srivastava4b89e372014-06-19 14:21:42 -070022#include <errno.h>
23#include <math.h>
24#include <cutils/log.h>
25#include <cutils/str_parms.h>
26
27#include "audio_hw.h"
28#include "voice.h"
29#include "voice_extn/voice_extn.h"
30#include "platform.h"
31#include "platform_api.h"
32
33struct pcm_config pcm_config_voice_call = {
34 .channels = 1,
35 .rate = 8000,
36 .period_size = 160,
37 .period_count = 2,
38 .format = PCM_FORMAT_S16_LE,
39};
40
Vineeta Srivastava4b89e372014-06-19 14:21:42 -070041static struct voice_session *voice_get_session_from_use_case(struct audio_device *adev,
42 audio_usecase_t usecase_id)
43{
44 struct voice_session *session = NULL;
45 int ret = 0;
46
47 ret = voice_extn_get_session_from_use_case(adev, usecase_id, &session);
48 if (ret == -ENOSYS) {
49 session = &adev->voice.session[VOICE_SESS_IDX];
50 }
51
52 return session;
53}
54
Ravi Kumar Alamanda08dbcfc2014-08-20 16:24:38 -070055int voice_stop_usecase(struct audio_device *adev, audio_usecase_t usecase_id)
Vineeta Srivastava4b89e372014-06-19 14:21:42 -070056{
57 int i, ret = 0;
58 struct audio_usecase *uc_info;
59 struct voice_session *session = NULL;
60
61 ALOGD("%s: enter usecase:%s", __func__, use_case_table[usecase_id]);
62
63 session = (struct voice_session *)voice_get_session_from_use_case(adev, usecase_id);
64 session->state.current = CALL_INACTIVE;
65
66 ret = platform_stop_voice_call(adev->platform, session->vsid);
67
68 /* 1. Close the PCM devices */
69 if (session->pcm_rx) {
70 pcm_close(session->pcm_rx);
71 session->pcm_rx = NULL;
72 }
73 if (session->pcm_tx) {
74 pcm_close(session->pcm_tx);
75 session->pcm_tx = NULL;
76 }
77
78 uc_info = get_usecase_from_list(adev, usecase_id);
79 if (uc_info == NULL) {
80 ALOGE("%s: Could not find the usecase (%d) in the list",
81 __func__, usecase_id);
82 return -EINVAL;
83 }
84
85 /* 2. Get and set stream specific mixer controls */
86 disable_audio_route(adev, uc_info);
87
88 /* 3. Disable the rx and tx devices */
89 disable_snd_device(adev, uc_info->out_snd_device);
90 disable_snd_device(adev, uc_info->in_snd_device);
91
92 list_remove(&uc_info->list);
93 free(uc_info);
94
95 ALOGD("%s: exit: status(%d)", __func__, ret);
96 return ret;
97}
98
Eric Laurent75a2e1d2014-08-29 12:38:47 -070099int voice_start_usecase(struct audio_device *adev, audio_usecase_t usecase_id)
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700100{
101 int i, ret = 0;
102 struct audio_usecase *uc_info;
103 int pcm_dev_rx_id, pcm_dev_tx_id;
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700104 struct voice_session *session = NULL;
105 struct pcm_config voice_config = pcm_config_voice_call;
106
107 ALOGD("%s: enter usecase:%s", __func__, use_case_table[usecase_id]);
108
109 session = (struct voice_session *)voice_get_session_from_use_case(adev, usecase_id);
110 uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
111 uc_info->id = usecase_id;
112 uc_info->type = VOICE_CALL;
Eric Laurent75a2e1d2014-08-29 12:38:47 -0700113 uc_info->stream.out = adev->current_call_output ;
114 uc_info->devices = adev->current_call_output ->devices;
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700115 uc_info->in_snd_device = SND_DEVICE_NONE;
116 uc_info->out_snd_device = SND_DEVICE_NONE;
117
118 list_add_tail(&adev->usecase_list, &uc_info->list);
119
120 select_devices(adev, usecase_id);
121
122 pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
123 pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
124
125 if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0) {
126 ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(%d)",
127 __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
128 ret = -EIO;
129 goto error_start_voice;
130 }
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700131
132 ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
133 __func__, adev->snd_card, pcm_dev_rx_id);
134 session->pcm_rx = pcm_open(adev->snd_card,
135 pcm_dev_rx_id,
136 PCM_OUT, &voice_config);
137 if (session->pcm_rx && !pcm_is_ready(session->pcm_rx)) {
138 ALOGE("%s: %s", __func__, pcm_get_error(session->pcm_rx));
139 ret = -EIO;
140 goto error_start_voice;
141 }
142
143 ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
144 __func__, adev->snd_card, pcm_dev_tx_id);
145 session->pcm_tx = pcm_open(adev->snd_card,
146 pcm_dev_tx_id,
147 PCM_IN, &voice_config);
148 if (session->pcm_tx && !pcm_is_ready(session->pcm_tx)) {
149 ALOGE("%s: %s", __func__, pcm_get_error(session->pcm_tx));
150 ret = -EIO;
151 goto error_start_voice;
152 }
153 pcm_start(session->pcm_rx);
154 pcm_start(session->pcm_tx);
155
156 voice_set_volume(adev, adev->voice.volume);
157
158 ret = platform_start_voice_call(adev->platform, session->vsid);
159 if (ret < 0) {
160 ALOGE("%s: platform_start_voice_call error %d\n", __func__, ret);
161 goto error_start_voice;
162 }
163
164 session->state.current = CALL_ACTIVE;
Ravi Kumar Alamandaf1819242014-08-05 18:20:42 -0700165 goto done;
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700166
167error_start_voice:
Ravi Kumar Alamanda08dbcfc2014-08-20 16:24:38 -0700168 voice_stop_usecase(adev, usecase_id);
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700169
Ravi Kumar Alamandaf1819242014-08-05 18:20:42 -0700170done:
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700171 ALOGD("%s: exit: status(%d)", __func__, ret);
172 return ret;
173}
174
Ravi Kumar Alamandaf1819242014-08-05 18:20:42 -0700175bool voice_is_call_state_active(struct audio_device *adev)
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700176{
Ravi Kumar Alamandaf1819242014-08-05 18:20:42 -0700177 bool call_state = false;
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700178 int ret = 0;
179
Ravi Kumar Alamandaf1819242014-08-05 18:20:42 -0700180 ret = voice_extn_is_call_state_active(adev, &call_state);
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700181 if (ret == -ENOSYS) {
Ravi Kumar Alamandaf1819242014-08-05 18:20:42 -0700182 call_state = (adev->voice.session[VOICE_SESS_IDX].state.current == CALL_ACTIVE) ? true : false;
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700183 }
184
Ravi Kumar Alamandaf1819242014-08-05 18:20:42 -0700185 return call_state;
186}
187
188bool voice_is_in_call(struct audio_device *adev)
189{
190 return adev->voice.in_call;
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700191}
192
193bool voice_is_in_call_rec_stream(struct stream_in *in)
194{
195 bool in_call_rec = false;
196 int ret = 0;
197
198 ret = voice_extn_is_in_call_rec_stream(in, &in_call_rec);
199 if (ret == -ENOSYS) {
200 in_call_rec = false;
201 }
202
203 return in_call_rec;
204}
205
206uint32_t voice_get_active_session_id(struct audio_device *adev)
207{
208 int ret = 0;
209 uint32_t session_id;
210
211 ret = voice_extn_get_active_session_id(adev, &session_id);
212 if (ret == -ENOSYS) {
213 session_id = VOICE_VSID;
214 }
215 return session_id;
216}
217
218int voice_check_and_set_incall_rec_usecase(struct audio_device *adev,
219 struct stream_in *in)
220{
221 int ret = 0;
222 uint32_t session_id;
223 int usecase_id;
224 int rec_mode = INCALL_REC_NONE;
225
Ravi Kumar Alamandaf1819242014-08-05 18:20:42 -0700226 if (voice_is_call_state_active(adev)) {
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700227 switch (in->source) {
228 case AUDIO_SOURCE_VOICE_UPLINK:
229 in->usecase = USECASE_INCALL_REC_UPLINK;
230 rec_mode = INCALL_REC_UPLINK;
231 break;
232 case AUDIO_SOURCE_VOICE_DOWNLINK:
233 in->usecase = USECASE_INCALL_REC_DOWNLINK;
234 rec_mode = INCALL_REC_DOWNLINK;
235 break;
236 case AUDIO_SOURCE_VOICE_CALL:
237 in->usecase = USECASE_INCALL_REC_UPLINK_AND_DOWNLINK;
238 rec_mode = INCALL_REC_UPLINK_AND_DOWNLINK;
239 break;
240 default:
241 ALOGV("%s: Source type %d doesnt match incall recording criteria",
242 __func__, in->source);
243 return ret;
244 }
245
246 session_id = voice_get_active_session_id(adev);
247 ret = platform_set_incall_recording_session_id(adev->platform,
248 session_id, rec_mode);
249 ALOGV("%s: Update usecase to %d",__func__, in->usecase);
250 } else {
251 ALOGV("%s: voice call not active", __func__);
252 }
253
254 return ret;
255}
256
257int voice_check_and_stop_incall_rec_usecase(struct audio_device *adev,
258 struct stream_in *in)
259{
260 int ret = 0;
261
262 if (in->source == AUDIO_SOURCE_VOICE_UPLINK ||
263 in->source == AUDIO_SOURCE_VOICE_DOWNLINK ||
264 in->source == AUDIO_SOURCE_VOICE_CALL) {
265 ret = platform_stop_incall_recording_usecase(adev->platform);
266 ALOGV("%s: Stop In-call recording", __func__);
267 }
268
269 return ret;
270}
271
272int voice_check_and_set_incall_music_usecase(struct audio_device *adev,
273 struct stream_out *out)
274{
275 int ret = 0;
276
277 ret = voice_extn_check_and_set_incall_music_usecase(adev, out);
278 if (ret == -ENOSYS) {
279 /* Incall music delivery is used only for LCH call state */
280 ret = -EINVAL;
281 }
282
283 return ret;
284}
285
286int voice_set_mic_mute(struct audio_device *adev, bool state)
287{
288 int err = 0;
289
290 adev->voice.mic_mute = state;
291 if (adev->mode == AUDIO_MODE_IN_CALL)
292 err = platform_set_mic_mute(adev->platform, state);
293
294 return err;
295}
296
297bool voice_get_mic_mute(struct audio_device *adev)
298{
299 return adev->voice.mic_mute;
300}
301
302int voice_set_volume(struct audio_device *adev, float volume)
303{
304 int vol, err = 0;
305
306 adev->voice.volume = volume;
307 if (adev->mode == AUDIO_MODE_IN_CALL) {
308 if (volume < 0.0) {
309 volume = 0.0;
310 } else if (volume > 1.0) {
311 volume = 1.0;
312 }
313
314 vol = lrint(volume * 100.0);
315
316 // Voice volume levels from android are mapped to driver volume levels as follows.
317 // 0 -> 5, 20 -> 4, 40 ->3, 60 -> 2, 80 -> 1, 100 -> 0
318 // So adjust the volume to get the correct volume index in driver
319 vol = 100 - vol;
320
321 err = platform_set_voice_volume(adev->platform, vol);
322 }
323
324 return err;
325}
326
Eric Laurent75a2e1d2014-08-29 12:38:47 -0700327int voice_start_call(struct audio_device *adev)
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700328{
329 int ret = 0;
330
Ravi Kumar Alamandab09e4a02014-10-20 17:07:43 -0700331 adev->voice.in_call = true;
Ravi Kumar Alamandaeecfa9a2015-03-20 17:31:46 -0700332
333 voice_set_mic_mute(adev, adev->voice.mic_mute);
334
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700335 ret = voice_extn_start_call(adev);
336 if (ret == -ENOSYS) {
Eric Laurent75a2e1d2014-08-29 12:38:47 -0700337 ret = voice_start_usecase(adev, USECASE_VOICE_CALL);
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700338 }
339
340 return ret;
341}
342
343int voice_stop_call(struct audio_device *adev)
344{
345 int ret = 0;
346
Ravi Kumar Alamandaf1819242014-08-05 18:20:42 -0700347 adev->voice.in_call = false;
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700348 ret = voice_extn_stop_call(adev);
349 if (ret == -ENOSYS) {
Ravi Kumar Alamanda08dbcfc2014-08-20 16:24:38 -0700350 ret = voice_stop_usecase(adev, USECASE_VOICE_CALL);
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700351 }
352
353 return ret;
354}
355
356void voice_get_parameters(struct audio_device *adev,
357 struct str_parms *query,
358 struct str_parms *reply)
359{
360 voice_extn_get_parameters(adev, query, reply);
361}
362
363int voice_set_parameters(struct audio_device *adev, struct str_parms *parms)
364{
365 char *str;
366 char value[32];
367 int val;
368 int ret = 0, err;
369 char *kv_pairs = str_parms_to_str(parms);
370
371 ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs);
372
373 ret = voice_extn_set_parameters(adev, parms);
Haynes Mathew Georgeff5d6512014-07-18 15:31:44 -0700374 if (ret != 0) {
375 if (ret == -ENOSYS) {
376 ret = 0; /* ignore error */
377 } else {
378 goto done;
379 }
380 }
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700381
382 err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TTY_MODE, value, sizeof(value));
383 if (err >= 0) {
384 int tty_mode;
385 str_parms_del(parms, AUDIO_PARAMETER_KEY_TTY_MODE);
386 if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0)
387 tty_mode = TTY_MODE_OFF;
388 else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0)
389 tty_mode = TTY_MODE_VCO;
390 else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0)
391 tty_mode = TTY_MODE_HCO;
392 else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0)
393 tty_mode = TTY_MODE_FULL;
394 else {
395 ret = -EINVAL;
396 goto done;
397 }
398
399 if (tty_mode != adev->voice.tty_mode) {
400 adev->voice.tty_mode = tty_mode;
401 adev->acdb_settings = (adev->acdb_settings & TTY_MODE_CLEAR) | tty_mode;
Ravi Kumar Alamandaf1819242014-08-05 18:20:42 -0700402 if (voice_is_call_state_active(adev))
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700403 voice_update_devices_for_all_voice_usecases(adev);
404 }
405 }
406
Eric Laurent9d0d3f12014-07-25 12:40:29 -0500407 err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HAC,
408 value, sizeof(value));
409 if (err >= 0) {
410 bool hac = false;
411 str_parms_del(parms, AUDIO_PARAMETER_KEY_HAC);
412 if (strcmp(value, AUDIO_PARAMETER_VALUE_HAC_ON) == 0)
413 hac = true;
414
415 if (hac != adev->voice.hac) {
416 adev->voice.hac = hac;
417 if (voice_is_in_call(adev))
418 voice_update_devices_for_all_voice_usecases(adev);
419 }
420 }
421
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700422 err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_INCALLMUSIC,
423 value, sizeof(value));
424 if (err >= 0) {
425 str_parms_del(parms, AUDIO_PARAMETER_KEY_INCALLMUSIC);
426 if (strcmp(value, AUDIO_PARAMETER_VALUE_TRUE) == 0)
427 platform_start_incall_music_usecase(adev->platform);
428 else
429 platform_stop_incall_music_usecase(adev->platform);
430 }
431
432done:
433 ALOGV("%s: exit with code(%d)", __func__, ret);
434 free(kv_pairs);
435 return ret;
436}
437
438void voice_init(struct audio_device *adev)
439{
440 int i = 0;
441
442 memset(&adev->voice, 0, sizeof(adev->voice));
443 adev->voice.tty_mode = TTY_MODE_OFF;
Eric Laurent9d0d3f12014-07-25 12:40:29 -0500444 adev->voice.hac = false;
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700445 adev->voice.volume = 1.0f;
446 adev->voice.mic_mute = false;
Ravi Kumar Alamandaf1819242014-08-05 18:20:42 -0700447 adev->voice.in_call = false;
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700448 for (i = 0; i < MAX_VOICE_SESSIONS; i++) {
449 adev->voice.session[i].pcm_rx = NULL;
450 adev->voice.session[i].pcm_tx = NULL;
451 adev->voice.session[i].state.current = CALL_INACTIVE;
452 adev->voice.session[i].state.new = CALL_INACTIVE;
453 adev->voice.session[i].vsid = VOICE_VSID;
454 }
455
456 voice_extn_init(adev);
457}
458
459void voice_update_devices_for_all_voice_usecases(struct audio_device *adev)
460{
461 struct listnode *node;
462 struct audio_usecase *usecase;
463
464 list_for_each(node, &adev->usecase_list) {
465 usecase = node_to_item(node, struct audio_usecase, list);
466 if (usecase->type == VOICE_CALL) {
467 ALOGV("%s: updating device for usecase:%s", __func__,
468 use_case_table[usecase->id]);
Ravi Kumar Alamandaa4fc9022014-10-08 18:57:46 -0700469 usecase->stream.out = adev->current_call_output;
Vineeta Srivastava4b89e372014-06-19 14:21:42 -0700470 select_devices(adev, usecase->id);
471 }
472 }
473}
474
475