blob: 824724a01de9f4d13f18fcd39d446d7751465e88 [file] [log] [blame]
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +01001/*
2 * Copyright (C) 2017 Christopher N. Hesse <raymanfx@gmail.com>
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 "audio_hw_voice"
Christopher N. Hesse86f7ea82017-02-15 09:41:05 +010018#define LOG_NDEBUG 0
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +010019/*#define VERY_VERY_VERBOSE_LOGGING*/
20#ifdef VERY_VERY_VERBOSE_LOGGING
21#define ALOGVV ALOGV
22#else
23#define ALOGVV(a...) do { } while(0)
24#endif
25
26#include <stdlib.h>
27#include <pthread.h>
28
Christopher N. Hesse696959d2017-02-02 20:49:55 +010029#include <cutils/log.h>
30#include <cutils/properties.h>
31
32#include <samsung_audio.h>
33
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +010034#include "audio_hw.h"
35#include "voice.h"
36
Christopher N. Hesse0fdef0c2017-02-25 01:37:56 +010037#ifdef AUDIENCE_EARSMART_IC
38#include "audience.h"
39#endif
40
Christopher N. Hesse696959d2017-02-02 20:49:55 +010041static struct pcm_config pcm_config_voicecall = {
42 .channels = 2,
43 .rate = 8000,
44 .period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
45 .period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
46 .format = PCM_FORMAT_S16_LE,
47};
48
49static struct pcm_config pcm_config_voicecall_wideband = {
50 .channels = 2,
51 .rate = 16000,
52 .period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
53 .period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
54 .format = PCM_FORMAT_S16_LE,
55};
56
Andreas Schneider05bc1882017-02-09 14:03:11 +010057struct pcm_config pcm_config_voice_sco = {
58 .channels = 1,
59 .rate = SCO_DEFAULT_SAMPLING_RATE,
60 .period_size = SCO_PERIOD_SIZE,
61 .period_count = SCO_PERIOD_COUNT,
62 .format = PCM_FORMAT_S16_LE,
63};
64
Christopher N. Hesse696959d2017-02-02 20:49:55 +010065/* Prototypes */
66int start_voice_call(struct audio_device *adev);
67int stop_voice_call(struct audio_device *adev);
68
69void set_voice_session_audio_path(struct voice_session *session)
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +010070{
Christopher N. Hesse696959d2017-02-02 20:49:55 +010071 enum _AudioPath device_type;
Andreas Schneiderd44edaa2017-02-13 16:21:35 +010072 int rc;
Christopher N. Hesse696959d2017-02-02 20:49:55 +010073
74 switch(session->out_device) {
75 case AUDIO_DEVICE_OUT_SPEAKER:
76 device_type = SOUND_AUDIO_PATH_SPEAKER;
77 break;
78 case AUDIO_DEVICE_OUT_EARPIECE:
Andreas Schneidere6286b92017-02-14 17:24:23 +010079 device_type = SOUND_AUDIO_PATH_EARPIECE;
Christopher N. Hesse696959d2017-02-02 20:49:55 +010080 break;
81 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
82 device_type = SOUND_AUDIO_PATH_HEADSET;
83 break;
84 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
85 device_type = SOUND_AUDIO_PATH_HEADPHONE;
86 break;
87 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
88 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
89 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
90 device_type = SOUND_AUDIO_PATH_BLUETOOTH;
91 break;
92 default:
Andreas Schneidere6286b92017-02-14 17:24:23 +010093 /* if output device isn't supported, use earpiece by default */
94 device_type = SOUND_AUDIO_PATH_EARPIECE;
Christopher N. Hesse696959d2017-02-02 20:49:55 +010095 break;
96 }
97
98 ALOGV("%s: ril_set_call_audio_path(%d)", __func__, device_type);
99
Andreas Schneiderd44edaa2017-02-13 16:21:35 +0100100 rc = ril_set_call_audio_path(&session->ril, device_type);
101 ALOGE_IF(rc != 0, "Failed to set audio path: (%d)", rc);
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100102}
103
104/*
105 * This decides based on the output device, if we enable
106 * two mic control
107 */
108void prepare_voice_session(struct voice_session *session,
109 audio_devices_t active_out_devices)
110{
111 ALOGV("%s: active_out_devices: 0x%x", __func__, active_out_devices);
112
113 session->out_device = active_out_devices;
114
115 switch (session->out_device) {
116 case AUDIO_DEVICE_OUT_EARPIECE:
117 case AUDIO_DEVICE_OUT_SPEAKER:
118 session->two_mic_control = true;
119 break;
120 default:
121 session->two_mic_control = false;
122 break;
123 }
124
125 if (session->two_mic_disabled) {
126 session->two_mic_control = false;
127 }
128}
129
130/*
Andreas Schneider05bc1882017-02-09 14:03:11 +0100131 * This must be called with the hw device mutex locked, OK to hold other
132 * mutexes.
133 */
134static void stop_voice_session_bt_sco(struct voice_session *session) {
135 ALOGV("%s: Closing SCO PCMs", __func__);
136
137 if (session->pcm_sco_rx != NULL) {
138 pcm_stop(session->pcm_sco_rx);
139 pcm_close(session->pcm_sco_rx);
140 session->pcm_sco_rx = NULL;
141 }
142
143 if (session->pcm_sco_tx != NULL) {
144 pcm_stop(session->pcm_sco_tx);
145 pcm_close(session->pcm_sco_tx);
146 session->pcm_sco_tx = NULL;
147 }
148}
149
150/* must be called with the hw device mutex locked, OK to hold other mutexes */
151void start_voice_session_bt_sco(struct voice_session *session)
152{
153 if (session->pcm_sco_rx != NULL || session->pcm_sco_tx != NULL) {
154 ALOGW("%s: SCO PCMs already open!\n", __func__);
155 return;
156 }
157
158 ALOGV("%s: Opening SCO PCMs", __func__);
159
160 /* TODO: Add wideband support */
161
162 session->pcm_sco_rx = pcm_open(SOUND_CARD,
163 SOUND_PLAYBACK_SCO_DEVICE,
164 PCM_OUT|PCM_MONOTONIC,
165 &pcm_config_voice_sco);
166 if (session->pcm_sco_rx != NULL && !pcm_is_ready(session->pcm_sco_rx)) {
167 ALOGE("%s: cannot open PCM SCO RX stream: %s",
168 __func__, pcm_get_error(session->pcm_sco_rx));
169 goto err_sco_rx;
170 }
171
172 session->pcm_sco_tx = pcm_open(SOUND_CARD,
173 SOUND_CAPTURE_SCO_DEVICE,
174 PCM_IN|PCM_MONOTONIC,
175 &pcm_config_voice_sco);
176 if (session->pcm_sco_tx && !pcm_is_ready(session->pcm_sco_tx)) {
177 ALOGE("%s: cannot open PCM SCO TX stream: %s",
178 __func__, pcm_get_error(session->pcm_sco_tx));
179 goto err_sco_tx;
180 }
181
182 pcm_start(session->pcm_sco_rx);
183 pcm_start(session->pcm_sco_tx);
184
185 return;
186
187err_sco_tx:
188 pcm_close(session->pcm_sco_tx);
189 session->pcm_sco_tx = NULL;
190err_sco_rx:
191 pcm_close(session->pcm_sco_rx);
192 session->pcm_sco_rx = NULL;
193}
194/*
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100195 * This function must be called with hw device mutex locked, OK to hold other
196 * mutexes
197 */
198int start_voice_session(struct voice_session *session)
199{
200 struct pcm_config *voice_config;
201
202 if (session->pcm_voice_rx != NULL || session->pcm_voice_tx != NULL) {
203 ALOGW("%s: Voice PCMs already open!\n", __func__);
204 return 0;
205 }
206
207 ALOGV("%s: Opening voice PCMs", __func__);
208
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100209 /* TODO: Handle wb_amr=2 */
210 if (session->wb_amr_type >= 1) {
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100211 ALOGV("%s: pcm_config wideband", __func__);
212 voice_config = &pcm_config_voicecall_wideband;
213 } else {
214 ALOGV("%s: pcm_config narrowband", __func__);
215 voice_config = &pcm_config_voicecall;
216 }
217
218 /* Open modem PCM channels */
219 session->pcm_voice_rx = pcm_open(SOUND_CARD,
220 SOUND_PLAYBACK_VOICE_DEVICE,
221 PCM_OUT|PCM_MONOTONIC,
222 voice_config);
223 if (session->pcm_voice_rx != NULL && !pcm_is_ready(session->pcm_voice_rx)) {
224 ALOGE("%s: cannot open PCM voice RX stream: %s",
225 __func__,
226 pcm_get_error(session->pcm_voice_rx));
227
228 pcm_close(session->pcm_voice_tx);
229 session->pcm_voice_tx = NULL;
230
231 return -ENOMEM;
232 }
233
234 session->pcm_voice_tx = pcm_open(SOUND_CARD,
235 SOUND_CAPTURE_VOICE_DEVICE,
236 PCM_IN|PCM_MONOTONIC,
237 voice_config);
238 if (session->pcm_voice_tx != NULL && !pcm_is_ready(session->pcm_voice_tx)) {
239 ALOGE("%s: cannot open PCM voice TX stream: %s",
240 __func__,
241 pcm_get_error(session->pcm_voice_tx));
242
243 pcm_close(session->pcm_voice_rx);
244 session->pcm_voice_rx = NULL;
245
246 return -ENOMEM;
247 }
248
249 pcm_start(session->pcm_voice_rx);
250 pcm_start(session->pcm_voice_tx);
251
Andreas Schneider05bc1882017-02-09 14:03:11 +0100252 if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
253 start_voice_session_bt_sco(session);
254 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100255
Christopher N. Hesse0fdef0c2017-02-25 01:37:56 +0100256#ifdef AUDIENCE_EARSMART_IC
257 ALOGV("%s: Enabling Audience IC", __func__);
258 es_start_voice_session(session);
259#endif
260
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100261 if (session->two_mic_control) {
262 ALOGV("%s: enabling two mic control", __func__);
263 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_ON);
264 } else {
265 ALOGV("%s: disabling two mic control", __func__);
266 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_OFF);
267 }
268
269 ril_set_call_clock_sync(&session->ril, SOUND_CLOCK_START);
270
271 return 0;
272}
273
274/*
275 * This function must be called with hw device mutex locked, OK to hold other
276 * mutexes
277 */
278void stop_voice_session(struct voice_session *session)
279{
280 int status = 0;
281
282 ALOGV("%s: Closing active PCMs", __func__);
283
284 if (session->pcm_voice_rx != NULL) {
285 pcm_stop(session->pcm_voice_rx);
286 pcm_close(session->pcm_voice_rx);
287 session->pcm_voice_rx = NULL;
288 status++;
289 }
290
291 if (session->pcm_voice_tx != NULL) {
292 pcm_stop(session->pcm_voice_tx);
293 pcm_close(session->pcm_voice_tx);
294 session->pcm_voice_tx = NULL;
295 status++;
296 }
297
Andreas Schneider05bc1882017-02-09 14:03:11 +0100298 if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
299 stop_voice_session_bt_sco(session);
300 }
301
Christopher N. Hesse0fdef0c2017-02-25 01:37:56 +0100302#ifdef AUDIENCE_EARSMART_IC
303 ALOGV("%s: Disabling Audience IC", __func__);
304 es_stop_voice_session();
305#endif
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100306
307 session->out_device = AUDIO_DEVICE_NONE;
308
309 ALOGV("%s: Successfully closed %d active PCMs", __func__, status);
310}
311
312void set_voice_session_volume(struct voice_session *session, float volume)
313{
314 enum _SoundType sound_type;
315
316 switch (session->out_device) {
317 case AUDIO_DEVICE_OUT_EARPIECE:
318 sound_type = SOUND_TYPE_VOICE;
319 break;
320 case AUDIO_DEVICE_OUT_SPEAKER:
321 sound_type = SOUND_TYPE_SPEAKER;
322 break;
323 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
324 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
325 sound_type = SOUND_TYPE_HEADSET;
326 break;
327 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
328 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
329 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
330 case AUDIO_DEVICE_OUT_ALL_SCO:
331 sound_type = SOUND_TYPE_BTVOICE;
332 break;
333 default:
334 sound_type = SOUND_TYPE_VOICE;
335 }
336
337 ril_set_call_volume(&session->ril, sound_type, volume);
338}
339
Andreas Schneider107a8482017-02-06 12:36:31 +0100340void set_voice_session_mic_mute(struct voice_session *session, bool state)
341{
342 enum _MuteCondition mute_condition = state ? TX_MUTE : TX_UNMUTE;
343
344 ril_set_mute(&session->ril, mute_condition);
345}
346
Andreas Schneider82f32482017-02-06 09:00:48 +0100347bool voice_session_uses_twomic(struct voice_session *session)
348{
349 if (session->two_mic_disabled) {
350 return false;
351 }
352
353 return session->two_mic_control;
354}
355
Andreas Schneider59486fa2017-02-06 09:16:39 +0100356bool voice_session_uses_wideband(struct voice_session *session)
357{
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100358 return session->wb_amr_type >= 1;
Andreas Schneider59486fa2017-02-06 09:16:39 +0100359}
360
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100361static void voice_session_wb_amr_callback(void *data, int wb_amr_type)
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100362{
363 struct audio_device *adev = (struct audio_device *)data;
364 struct voice_session *session =
365 (struct voice_session *)adev->voice.session;
366
367 pthread_mutex_lock(&adev->lock);
368
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100369 if (session->wb_amr_type != wb_amr_type) {
370 session->wb_amr_type = wb_amr_type;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100371
372 /* reopen the modem PCMs at the new rate */
373 if (adev->voice.in_call) {
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100374 ALOGV("%s: %s wide band voice call (WB_AMR=%d)",
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100375 __func__,
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100376 wb_amr_type > 0 ? "Enable" : "Disable",
377 wb_amr_type);
378
379 /* TODO Handle wb_amr_type=2 */
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100380
Andreas Schneidere9a44a22017-02-14 13:00:48 +0100381 /*
382 * We need stop the PCM and start with the
383 * wide band pcm_config.
384 */
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100385 stop_voice_call(adev);
386 start_voice_call(adev);
387 }
388 }
389
390 pthread_mutex_unlock(&adev->lock);
391}
392
393struct voice_session *voice_session_init(struct audio_device *adev)
394{
395 char voice_config[PROPERTY_VALUE_MAX];
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100396 struct voice_session *session;
397 int ret;
398
399 session = calloc(1, sizeof(struct voice_session));
400 if (session == NULL) {
401 return NULL;
402 }
403
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100404 /* Two mic control */
405 ret = property_get_bool("audio_hal.disable_two_mic", false);
406 if (ret > 0) {
407 session->two_mic_disabled = true;
408 }
409
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100410 /* Do this as the last step so we do not have to close it on error */
411 ret = ril_open(&session->ril);
412 if (ret != 0) {
413 free(session);
414 return NULL;
415 }
416
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100417 ret = property_get("audio_hal.force_voice_config", voice_config, "");
418 if (ret > 0) {
419 if ((strncmp(voice_config, "narrow", 6)) == 0)
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100420 session->wb_amr_type = 0;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100421 else if ((strncmp(voice_config, "wide", 4)) == 0)
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100422 session->wb_amr_type = 1;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100423 ALOGV("%s: Forcing voice config: %s", __func__, voice_config);
424 } else {
Christopher N. Hesse74c317d2017-02-15 09:47:20 +0100425 if (RIL_UNSOL_SNDMGR_WB_AMR_REPORT > 0) {
426 /* register callback for wideband AMR setting */
427 ret = ril_set_wb_amr_callback(&session->ril,
428 voice_session_wb_amr_callback,
429 (void *)adev);
430 if (ret != 0) {
431 ALOGE("%s: Failed to register WB_AMR callback", __func__);
432 free(session);
433 return NULL;
434 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100435
Christopher N. Hesse74c317d2017-02-15 09:47:20 +0100436 ALOGV("%s: Registered WB_AMR callback", __func__);
437 } else {
438 ALOGV("%s: WB_AMR callback not supported", __func__);
439 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100440 }
441
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100442 return session;
443}
444
445void voice_session_deinit(struct voice_session *session)
446{
447 ril_close(&session->ril);
448 free(session);
449}