blob: 08d8baf38ee845663f60525380f1179c9a1fef91 [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"
18#define LOG_NDEBUG 0
19/*#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;
68
69 switch(session->out_device) {
70 case AUDIO_DEVICE_OUT_SPEAKER:
71 device_type = SOUND_AUDIO_PATH_SPEAKER;
72 break;
73 case AUDIO_DEVICE_OUT_EARPIECE:
74 device_type = SOUND_AUDIO_PATH_HANDSET;
75 break;
76 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
77 device_type = SOUND_AUDIO_PATH_HEADSET;
78 break;
79 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
80 device_type = SOUND_AUDIO_PATH_HEADPHONE;
81 break;
82 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
83 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
84 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
85 device_type = SOUND_AUDIO_PATH_BLUETOOTH;
86 break;
87 default:
88 /* if output device isn't supported, use handset by default */
89 device_type = SOUND_AUDIO_PATH_HANDSET;
90 break;
91 }
92
93 ALOGV("%s: ril_set_call_audio_path(%d)", __func__, device_type);
94
95 ril_set_call_audio_path(&session->ril, device_type);
96}
97
98/*
99 * This decides based on the output device, if we enable
100 * two mic control
101 */
102void prepare_voice_session(struct voice_session *session,
103 audio_devices_t active_out_devices)
104{
105 ALOGV("%s: active_out_devices: 0x%x", __func__, active_out_devices);
106
107 session->out_device = active_out_devices;
108
109 switch (session->out_device) {
110 case AUDIO_DEVICE_OUT_EARPIECE:
111 case AUDIO_DEVICE_OUT_SPEAKER:
112 session->two_mic_control = true;
113 break;
114 default:
115 session->two_mic_control = false;
116 break;
117 }
118
119 if (session->two_mic_disabled) {
120 session->two_mic_control = false;
121 }
122}
123
124/*
Andreas Schneider05bc1882017-02-09 14:03:11 +0100125 * This must be called with the hw device mutex locked, OK to hold other
126 * mutexes.
127 */
128static void stop_voice_session_bt_sco(struct voice_session *session) {
129 ALOGV("%s: Closing SCO PCMs", __func__);
130
131 if (session->pcm_sco_rx != NULL) {
132 pcm_stop(session->pcm_sco_rx);
133 pcm_close(session->pcm_sco_rx);
134 session->pcm_sco_rx = NULL;
135 }
136
137 if (session->pcm_sco_tx != NULL) {
138 pcm_stop(session->pcm_sco_tx);
139 pcm_close(session->pcm_sco_tx);
140 session->pcm_sco_tx = NULL;
141 }
142}
143
144/* must be called with the hw device mutex locked, OK to hold other mutexes */
145void start_voice_session_bt_sco(struct voice_session *session)
146{
147 if (session->pcm_sco_rx != NULL || session->pcm_sco_tx != NULL) {
148 ALOGW("%s: SCO PCMs already open!\n", __func__);
149 return;
150 }
151
152 ALOGV("%s: Opening SCO PCMs", __func__);
153
154 /* TODO: Add wideband support */
155
156 session->pcm_sco_rx = pcm_open(SOUND_CARD,
157 SOUND_PLAYBACK_SCO_DEVICE,
158 PCM_OUT|PCM_MONOTONIC,
159 &pcm_config_voice_sco);
160 if (session->pcm_sco_rx != NULL && !pcm_is_ready(session->pcm_sco_rx)) {
161 ALOGE("%s: cannot open PCM SCO RX stream: %s",
162 __func__, pcm_get_error(session->pcm_sco_rx));
163 goto err_sco_rx;
164 }
165
166 session->pcm_sco_tx = pcm_open(SOUND_CARD,
167 SOUND_CAPTURE_SCO_DEVICE,
168 PCM_IN|PCM_MONOTONIC,
169 &pcm_config_voice_sco);
170 if (session->pcm_sco_tx && !pcm_is_ready(session->pcm_sco_tx)) {
171 ALOGE("%s: cannot open PCM SCO TX stream: %s",
172 __func__, pcm_get_error(session->pcm_sco_tx));
173 goto err_sco_tx;
174 }
175
176 pcm_start(session->pcm_sco_rx);
177 pcm_start(session->pcm_sco_tx);
178
179 return;
180
181err_sco_tx:
182 pcm_close(session->pcm_sco_tx);
183 session->pcm_sco_tx = NULL;
184err_sco_rx:
185 pcm_close(session->pcm_sco_rx);
186 session->pcm_sco_rx = NULL;
187}
188/*
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100189 * This function must be called with hw device mutex locked, OK to hold other
190 * mutexes
191 */
192int start_voice_session(struct voice_session *session)
193{
194 struct pcm_config *voice_config;
195
196 if (session->pcm_voice_rx != NULL || session->pcm_voice_tx != NULL) {
197 ALOGW("%s: Voice PCMs already open!\n", __func__);
198 return 0;
199 }
200
201 ALOGV("%s: Opening voice PCMs", __func__);
202
203 if (session->wb_amr) {
204 ALOGV("%s: pcm_config wideband", __func__);
205 voice_config = &pcm_config_voicecall_wideband;
206 } else {
207 ALOGV("%s: pcm_config narrowband", __func__);
208 voice_config = &pcm_config_voicecall;
209 }
210
211 /* Open modem PCM channels */
212 session->pcm_voice_rx = pcm_open(SOUND_CARD,
213 SOUND_PLAYBACK_VOICE_DEVICE,
214 PCM_OUT|PCM_MONOTONIC,
215 voice_config);
216 if (session->pcm_voice_rx != NULL && !pcm_is_ready(session->pcm_voice_rx)) {
217 ALOGE("%s: cannot open PCM voice RX stream: %s",
218 __func__,
219 pcm_get_error(session->pcm_voice_rx));
220
221 pcm_close(session->pcm_voice_tx);
222 session->pcm_voice_tx = NULL;
223
224 return -ENOMEM;
225 }
226
227 session->pcm_voice_tx = pcm_open(SOUND_CARD,
228 SOUND_CAPTURE_VOICE_DEVICE,
229 PCM_IN|PCM_MONOTONIC,
230 voice_config);
231 if (session->pcm_voice_tx != NULL && !pcm_is_ready(session->pcm_voice_tx)) {
232 ALOGE("%s: cannot open PCM voice TX stream: %s",
233 __func__,
234 pcm_get_error(session->pcm_voice_tx));
235
236 pcm_close(session->pcm_voice_rx);
237 session->pcm_voice_rx = NULL;
238
239 return -ENOMEM;
240 }
241
242 pcm_start(session->pcm_voice_rx);
243 pcm_start(session->pcm_voice_tx);
244
Andreas Schneider05bc1882017-02-09 14:03:11 +0100245 if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
246 start_voice_session_bt_sco(session);
247 }
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100248
249 if (session->two_mic_control) {
250 ALOGV("%s: enabling two mic control", __func__);
251 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_ON);
252 } else {
253 ALOGV("%s: disabling two mic control", __func__);
254 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_OFF);
255 }
256
257 ril_set_call_clock_sync(&session->ril, SOUND_CLOCK_START);
258
259 return 0;
260}
261
262/*
263 * This function must be called with hw device mutex locked, OK to hold other
264 * mutexes
265 */
266void stop_voice_session(struct voice_session *session)
267{
268 int status = 0;
269
270 ALOGV("%s: Closing active PCMs", __func__);
271
272 if (session->pcm_voice_rx != NULL) {
273 pcm_stop(session->pcm_voice_rx);
274 pcm_close(session->pcm_voice_rx);
275 session->pcm_voice_rx = NULL;
276 status++;
277 }
278
279 if (session->pcm_voice_tx != NULL) {
280 pcm_stop(session->pcm_voice_tx);
281 pcm_close(session->pcm_voice_tx);
282 session->pcm_voice_tx = NULL;
283 status++;
284 }
285
Andreas Schneider05bc1882017-02-09 14:03:11 +0100286 if (session->out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
287 stop_voice_session_bt_sco(session);
288 }
289
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100290
291 session->out_device = AUDIO_DEVICE_NONE;
292
293 ALOGV("%s: Successfully closed %d active PCMs", __func__, status);
294}
295
296void set_voice_session_volume(struct voice_session *session, float volume)
297{
298 enum _SoundType sound_type;
299
300 switch (session->out_device) {
301 case AUDIO_DEVICE_OUT_EARPIECE:
302 sound_type = SOUND_TYPE_VOICE;
303 break;
304 case AUDIO_DEVICE_OUT_SPEAKER:
305 sound_type = SOUND_TYPE_SPEAKER;
306 break;
307 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
308 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
309 sound_type = SOUND_TYPE_HEADSET;
310 break;
311 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
312 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
313 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
314 case AUDIO_DEVICE_OUT_ALL_SCO:
315 sound_type = SOUND_TYPE_BTVOICE;
316 break;
317 default:
318 sound_type = SOUND_TYPE_VOICE;
319 }
320
321 ril_set_call_volume(&session->ril, sound_type, volume);
322}
323
Andreas Schneider107a8482017-02-06 12:36:31 +0100324void set_voice_session_mic_mute(struct voice_session *session, bool state)
325{
326 enum _MuteCondition mute_condition = state ? TX_MUTE : TX_UNMUTE;
327
328 ril_set_mute(&session->ril, mute_condition);
329}
330
Andreas Schneider82f32482017-02-06 09:00:48 +0100331bool voice_session_uses_twomic(struct voice_session *session)
332{
333 if (session->two_mic_disabled) {
334 return false;
335 }
336
337 return session->two_mic_control;
338}
339
Andreas Schneider59486fa2017-02-06 09:16:39 +0100340bool voice_session_uses_wideband(struct voice_session *session)
341{
342 return session->wb_amr;
343}
344
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100345static void voice_session_wb_amr_callback(void *data, int enable)
346{
347 struct audio_device *adev = (struct audio_device *)data;
348 struct voice_session *session =
349 (struct voice_session *)adev->voice.session;
350
351 pthread_mutex_lock(&adev->lock);
352
353 if (session->wb_amr != enable) {
354 session->wb_amr = enable;
355
356 /* reopen the modem PCMs at the new rate */
357 if (adev->voice.in_call) {
358 ALOGV("%s: %s wide band voice call",
359 __func__,
360 enable ? "Enable" : "Disable");
361
362 stop_voice_call(adev);
363 start_voice_call(adev);
364 }
365 }
366
367 pthread_mutex_unlock(&adev->lock);
368}
369
370struct voice_session *voice_session_init(struct audio_device *adev)
371{
372 char voice_config[PROPERTY_VALUE_MAX];
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100373 struct voice_session *session;
374 int ret;
375
376 session = calloc(1, sizeof(struct voice_session));
377 if (session == NULL) {
378 return NULL;
379 }
380
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100381 /* Two mic control */
382 ret = property_get_bool("audio_hal.disable_two_mic", false);
383 if (ret > 0) {
384 session->two_mic_disabled = true;
385 }
386
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100387 /* Do this as the last step so we do not have to close it on error */
388 ret = ril_open(&session->ril);
389 if (ret != 0) {
390 free(session);
391 return NULL;
392 }
393
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100394 ret = property_get("audio_hal.force_voice_config", voice_config, "");
395 if (ret > 0) {
396 if ((strncmp(voice_config, "narrow", 6)) == 0)
397 session->wb_amr = false;
398 else if ((strncmp(voice_config, "wide", 4)) == 0)
399 session->wb_amr = true;
400 ALOGV("%s: Forcing voice config: %s", __func__, voice_config);
401 } else {
402 /* register callback for wideband AMR setting */
403 ret = ril_set_wb_amr_callback(&session->ril,
404 voice_session_wb_amr_callback,
405 (void *)adev);
406 if (ret != 0) {
407 ALOGE("%s: Failed to register WB_AMR callback", __func__);
408 free(session);
409 return NULL;
410 }
411
412 ALOGV("%s: Registered WB_AMR callback", __func__);
413 }
414
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100415 return session;
416}
417
418void voice_session_deinit(struct voice_session *session)
419{
420 ril_close(&session->ril);
421 free(session);
422}