blob: ba7b45b80a2e2f928e41eec9e40f313e2e7e887c [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. Hesse696959d2017-02-02 20:49:55 +010037static struct pcm_config pcm_config_voicecall = {
38 .channels = 2,
39 .rate = 8000,
40 .period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
41 .period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
42 .format = PCM_FORMAT_S16_LE,
43};
44
45static struct pcm_config pcm_config_voicecall_wideband = {
46 .channels = 2,
47 .rate = 16000,
48 .period_size = CAPTURE_PERIOD_SIZE_LOW_LATENCY,
49 .period_count = CAPTURE_PERIOD_COUNT_LOW_LATENCY,
50 .format = PCM_FORMAT_S16_LE,
51};
52
Andreas Schneider05bc1882017-02-09 14:03:11 +010053struct pcm_config pcm_config_voice_sco = {
54 .channels = 1,
55 .rate = SCO_DEFAULT_SAMPLING_RATE,
56 .period_size = SCO_PERIOD_SIZE,
57 .period_count = SCO_PERIOD_COUNT,
58 .format = PCM_FORMAT_S16_LE,
59};
60
Christopher N. Hesse696959d2017-02-02 20:49:55 +010061/* Prototypes */
62int start_voice_call(struct audio_device *adev);
63int stop_voice_call(struct audio_device *adev);
64
65void set_voice_session_audio_path(struct voice_session *session)
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +010066{
Christopher N. Hesse696959d2017-02-02 20:49:55 +010067 enum _AudioPath device_type;
Andreas Schneiderd44edaa2017-02-13 16:21:35 +010068 int rc;
Christopher N. Hesse696959d2017-02-02 20:49:55 +010069
70 switch(session->out_device) {
71 case AUDIO_DEVICE_OUT_SPEAKER:
72 device_type = SOUND_AUDIO_PATH_SPEAKER;
73 break;
74 case AUDIO_DEVICE_OUT_EARPIECE:
Andreas Schneidere6286b92017-02-14 17:24:23 +010075 device_type = SOUND_AUDIO_PATH_EARPIECE;
Christopher N. Hesse696959d2017-02-02 20:49:55 +010076 break;
77 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
78 device_type = SOUND_AUDIO_PATH_HEADSET;
79 break;
80 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
81 device_type = SOUND_AUDIO_PATH_HEADPHONE;
82 break;
83 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
84 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
85 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
86 device_type = SOUND_AUDIO_PATH_BLUETOOTH;
87 break;
88 default:
Andreas Schneidere6286b92017-02-14 17:24:23 +010089 /* if output device isn't supported, use earpiece by default */
90 device_type = SOUND_AUDIO_PATH_EARPIECE;
Christopher N. Hesse696959d2017-02-02 20:49:55 +010091 break;
92 }
93
94 ALOGV("%s: ril_set_call_audio_path(%d)", __func__, device_type);
95
Andreas Schneiderd44edaa2017-02-13 16:21:35 +010096 rc = ril_set_call_audio_path(&session->ril, device_type);
97 ALOGE_IF(rc != 0, "Failed to set audio path: (%d)", rc);
Christopher N. Hesse696959d2017-02-02 20:49:55 +010098}
99
100/*
101 * This decides based on the output device, if we enable
102 * two mic control
103 */
104void prepare_voice_session(struct voice_session *session,
105 audio_devices_t active_out_devices)
106{
107 ALOGV("%s: active_out_devices: 0x%x", __func__, active_out_devices);
108
109 session->out_device = active_out_devices;
110
111 switch (session->out_device) {
112 case AUDIO_DEVICE_OUT_EARPIECE:
113 case AUDIO_DEVICE_OUT_SPEAKER:
114 session->two_mic_control = true;
115 break;
116 default:
117 session->two_mic_control = false;
118 break;
119 }
120
121 if (session->two_mic_disabled) {
122 session->two_mic_control = false;
123 }
124}
125
126/*
Andreas Schneider05bc1882017-02-09 14:03:11 +0100127 * This must be called with the hw device mutex locked, OK to hold other
128 * mutexes.
129 */
130static void stop_voice_session_bt_sco(struct voice_session *session) {
131 ALOGV("%s: Closing SCO PCMs", __func__);
132
133 if (session->pcm_sco_rx != NULL) {
134 pcm_stop(session->pcm_sco_rx);
135 pcm_close(session->pcm_sco_rx);
136 session->pcm_sco_rx = NULL;
137 }
138
139 if (session->pcm_sco_tx != NULL) {
140 pcm_stop(session->pcm_sco_tx);
141 pcm_close(session->pcm_sco_tx);
142 session->pcm_sco_tx = NULL;
143 }
144}
145
146/* must be called with the hw device mutex locked, OK to hold other mutexes */
147void start_voice_session_bt_sco(struct voice_session *session)
148{
149 if (session->pcm_sco_rx != NULL || session->pcm_sco_tx != NULL) {
150 ALOGW("%s: SCO PCMs already open!\n", __func__);
151 return;
152 }
153
154 ALOGV("%s: Opening SCO PCMs", __func__);
155
156 /* TODO: Add wideband support */
157
158 session->pcm_sco_rx = pcm_open(SOUND_CARD,
159 SOUND_PLAYBACK_SCO_DEVICE,
160 PCM_OUT|PCM_MONOTONIC,
161 &pcm_config_voice_sco);
162 if (session->pcm_sco_rx != NULL && !pcm_is_ready(session->pcm_sco_rx)) {
163 ALOGE("%s: cannot open PCM SCO RX stream: %s",
164 __func__, pcm_get_error(session->pcm_sco_rx));
165 goto err_sco_rx;
166 }
167
168 session->pcm_sco_tx = pcm_open(SOUND_CARD,
169 SOUND_CAPTURE_SCO_DEVICE,
170 PCM_IN|PCM_MONOTONIC,
171 &pcm_config_voice_sco);
172 if (session->pcm_sco_tx && !pcm_is_ready(session->pcm_sco_tx)) {
173 ALOGE("%s: cannot open PCM SCO TX stream: %s",
174 __func__, pcm_get_error(session->pcm_sco_tx));
175 goto err_sco_tx;
176 }
177
178 pcm_start(session->pcm_sco_rx);
179 pcm_start(session->pcm_sco_tx);
180
181 return;
182
183err_sco_tx:
184 pcm_close(session->pcm_sco_tx);
185 session->pcm_sco_tx = NULL;
186err_sco_rx:
187 pcm_close(session->pcm_sco_rx);
188 session->pcm_sco_rx = NULL;
189}
190/*
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100191 * This function must be called with hw device mutex locked, OK to hold other
192 * mutexes
193 */
194int start_voice_session(struct voice_session *session)
195{
196 struct pcm_config *voice_config;
197
198 if (session->pcm_voice_rx != NULL || session->pcm_voice_tx != NULL) {
199 ALOGW("%s: Voice PCMs already open!\n", __func__);
200 return 0;
201 }
202
203 ALOGV("%s: Opening voice PCMs", __func__);
204
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100205 /* TODO: Handle wb_amr=2 */
206 if (session->wb_amr_type >= 1) {
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100207 ALOGV("%s: pcm_config wideband", __func__);
208 voice_config = &pcm_config_voicecall_wideband;
209 } else {
210 ALOGV("%s: pcm_config narrowband", __func__);
211 voice_config = &pcm_config_voicecall;
212 }
213
214 /* Open modem PCM channels */
215 session->pcm_voice_rx = pcm_open(SOUND_CARD,
216 SOUND_PLAYBACK_VOICE_DEVICE,
217 PCM_OUT|PCM_MONOTONIC,
218 voice_config);
219 if (session->pcm_voice_rx != NULL && !pcm_is_ready(session->pcm_voice_rx)) {
220 ALOGE("%s: cannot open PCM voice RX stream: %s",
221 __func__,
222 pcm_get_error(session->pcm_voice_rx));
223
224 pcm_close(session->pcm_voice_tx);
225 session->pcm_voice_tx = NULL;
226
227 return -ENOMEM;
228 }
229
230 session->pcm_voice_tx = pcm_open(SOUND_CARD,
231 SOUND_CAPTURE_VOICE_DEVICE,
232 PCM_IN|PCM_MONOTONIC,
233 voice_config);
234 if (session->pcm_voice_tx != NULL && !pcm_is_ready(session->pcm_voice_tx)) {
235 ALOGE("%s: cannot open PCM voice TX stream: %s",
236 __func__,
237 pcm_get_error(session->pcm_voice_tx));
238
239 pcm_close(session->pcm_voice_rx);
240 session->pcm_voice_rx = NULL;
241
242 return -ENOMEM;
243 }
244
245 pcm_start(session->pcm_voice_rx);
246 pcm_start(session->pcm_voice_tx);
247
Andreas Schneider05bc1882017-02-09 14:03:11 +0100248 if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
249 start_voice_session_bt_sco(session);
250 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100251
252 if (session->two_mic_control) {
253 ALOGV("%s: enabling two mic control", __func__);
254 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_ON);
255 } else {
256 ALOGV("%s: disabling two mic control", __func__);
257 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_OFF);
258 }
259
260 ril_set_call_clock_sync(&session->ril, SOUND_CLOCK_START);
261
262 return 0;
263}
264
265/*
266 * This function must be called with hw device mutex locked, OK to hold other
267 * mutexes
268 */
269void stop_voice_session(struct voice_session *session)
270{
271 int status = 0;
272
273 ALOGV("%s: Closing active PCMs", __func__);
274
275 if (session->pcm_voice_rx != NULL) {
276 pcm_stop(session->pcm_voice_rx);
277 pcm_close(session->pcm_voice_rx);
278 session->pcm_voice_rx = NULL;
279 status++;
280 }
281
282 if (session->pcm_voice_tx != NULL) {
283 pcm_stop(session->pcm_voice_tx);
284 pcm_close(session->pcm_voice_tx);
285 session->pcm_voice_tx = NULL;
286 status++;
287 }
288
Andreas Schneider05bc1882017-02-09 14:03:11 +0100289 if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
290 stop_voice_session_bt_sco(session);
291 }
292
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100293
294 session->out_device = AUDIO_DEVICE_NONE;
295
296 ALOGV("%s: Successfully closed %d active PCMs", __func__, status);
297}
298
299void set_voice_session_volume(struct voice_session *session, float volume)
300{
301 enum _SoundType sound_type;
302
303 switch (session->out_device) {
304 case AUDIO_DEVICE_OUT_EARPIECE:
305 sound_type = SOUND_TYPE_VOICE;
306 break;
307 case AUDIO_DEVICE_OUT_SPEAKER:
308 sound_type = SOUND_TYPE_SPEAKER;
309 break;
310 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
311 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
312 sound_type = SOUND_TYPE_HEADSET;
313 break;
314 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
315 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
316 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
317 case AUDIO_DEVICE_OUT_ALL_SCO:
318 sound_type = SOUND_TYPE_BTVOICE;
319 break;
320 default:
321 sound_type = SOUND_TYPE_VOICE;
322 }
323
324 ril_set_call_volume(&session->ril, sound_type, volume);
325}
326
Andreas Schneider107a8482017-02-06 12:36:31 +0100327void set_voice_session_mic_mute(struct voice_session *session, bool state)
328{
329 enum _MuteCondition mute_condition = state ? TX_MUTE : TX_UNMUTE;
330
331 ril_set_mute(&session->ril, mute_condition);
332}
333
Andreas Schneider82f32482017-02-06 09:00:48 +0100334bool voice_session_uses_twomic(struct voice_session *session)
335{
336 if (session->two_mic_disabled) {
337 return false;
338 }
339
340 return session->two_mic_control;
341}
342
Andreas Schneider59486fa2017-02-06 09:16:39 +0100343bool voice_session_uses_wideband(struct voice_session *session)
344{
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100345 return session->wb_amr_type >= 1;
Andreas Schneider59486fa2017-02-06 09:16:39 +0100346}
347
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100348static void voice_session_wb_amr_callback(void *data, int wb_amr_type)
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100349{
350 struct audio_device *adev = (struct audio_device *)data;
351 struct voice_session *session =
352 (struct voice_session *)adev->voice.session;
353
354 pthread_mutex_lock(&adev->lock);
355
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100356 if (session->wb_amr_type != wb_amr_type) {
357 session->wb_amr_type = wb_amr_type;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100358
359 /* reopen the modem PCMs at the new rate */
360 if (adev->voice.in_call) {
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100361 ALOGV("%s: %s wide band voice call (WB_AMR=%d)",
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100362 __func__,
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100363 wb_amr_type > 0 ? "Enable" : "Disable",
364 wb_amr_type);
365
366 /* TODO Handle wb_amr_type=2 */
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100367
368 stop_voice_call(adev);
369 start_voice_call(adev);
370 }
371 }
372
373 pthread_mutex_unlock(&adev->lock);
374}
375
376struct voice_session *voice_session_init(struct audio_device *adev)
377{
378 char voice_config[PROPERTY_VALUE_MAX];
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100379 struct voice_session *session;
380 int ret;
381
382 session = calloc(1, sizeof(struct voice_session));
383 if (session == NULL) {
384 return NULL;
385 }
386
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100387 /* Two mic control */
388 ret = property_get_bool("audio_hal.disable_two_mic", false);
389 if (ret > 0) {
390 session->two_mic_disabled = true;
391 }
392
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100393 /* Do this as the last step so we do not have to close it on error */
394 ret = ril_open(&session->ril);
395 if (ret != 0) {
396 free(session);
397 return NULL;
398 }
399
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100400 ret = property_get("audio_hal.force_voice_config", voice_config, "");
401 if (ret > 0) {
402 if ((strncmp(voice_config, "narrow", 6)) == 0)
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100403 session->wb_amr_type = 0;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100404 else if ((strncmp(voice_config, "wide", 4)) == 0)
Andreas Schneider49b9dcb2017-02-13 17:15:07 +0100405 session->wb_amr_type = 1;
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100406 ALOGV("%s: Forcing voice config: %s", __func__, voice_config);
407 } else {
Christopher N. Hesse74c317d2017-02-15 09:47:20 +0100408 if (RIL_UNSOL_SNDMGR_WB_AMR_REPORT > 0) {
409 /* register callback for wideband AMR setting */
410 ret = ril_set_wb_amr_callback(&session->ril,
411 voice_session_wb_amr_callback,
412 (void *)adev);
413 if (ret != 0) {
414 ALOGE("%s: Failed to register WB_AMR callback", __func__);
415 free(session);
416 return NULL;
417 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100418
Christopher N. Hesse74c317d2017-02-15 09:47:20 +0100419 ALOGV("%s: Registered WB_AMR callback", __func__);
420 } else {
421 ALOGV("%s: WB_AMR callback not supported", __func__);
422 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100423 }
424
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100425 return session;
426}
427
428void voice_session_deinit(struct voice_session *session)
429{
430 ril_close(&session->ril);
431 free(session);
432}