blob: 01a7b96ec273562dc868962d85d67c5c28896936 [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
53/* Prototypes */
54int start_voice_call(struct audio_device *adev);
55int stop_voice_call(struct audio_device *adev);
56
57void set_voice_session_audio_path(struct voice_session *session)
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +010058{
Christopher N. Hesse696959d2017-02-02 20:49:55 +010059 enum _AudioPath device_type;
60
61 switch(session->out_device) {
62 case AUDIO_DEVICE_OUT_SPEAKER:
63 device_type = SOUND_AUDIO_PATH_SPEAKER;
64 break;
65 case AUDIO_DEVICE_OUT_EARPIECE:
66 device_type = SOUND_AUDIO_PATH_HANDSET;
67 break;
68 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
69 device_type = SOUND_AUDIO_PATH_HEADSET;
70 break;
71 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
72 device_type = SOUND_AUDIO_PATH_HEADPHONE;
73 break;
74 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
75 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
76 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
77 device_type = SOUND_AUDIO_PATH_BLUETOOTH;
78 break;
79 default:
80 /* if output device isn't supported, use handset by default */
81 device_type = SOUND_AUDIO_PATH_HANDSET;
82 break;
83 }
84
85 ALOGV("%s: ril_set_call_audio_path(%d)", __func__, device_type);
86
87 ril_set_call_audio_path(&session->ril, device_type);
88}
89
90/*
91 * This decides based on the output device, if we enable
92 * two mic control
93 */
94void prepare_voice_session(struct voice_session *session,
95 audio_devices_t active_out_devices)
96{
97 ALOGV("%s: active_out_devices: 0x%x", __func__, active_out_devices);
98
99 session->out_device = active_out_devices;
100
101 switch (session->out_device) {
102 case AUDIO_DEVICE_OUT_EARPIECE:
103 case AUDIO_DEVICE_OUT_SPEAKER:
104 session->two_mic_control = true;
105 break;
106 default:
107 session->two_mic_control = false;
108 break;
109 }
110
111 if (session->two_mic_disabled) {
112 session->two_mic_control = false;
113 }
114}
115
116/*
117 * This function must be called with hw device mutex locked, OK to hold other
118 * mutexes
119 */
120int start_voice_session(struct voice_session *session)
121{
122 struct pcm_config *voice_config;
123
124 if (session->pcm_voice_rx != NULL || session->pcm_voice_tx != NULL) {
125 ALOGW("%s: Voice PCMs already open!\n", __func__);
126 return 0;
127 }
128
129 ALOGV("%s: Opening voice PCMs", __func__);
130
131 if (session->wb_amr) {
132 ALOGV("%s: pcm_config wideband", __func__);
133 voice_config = &pcm_config_voicecall_wideband;
134 } else {
135 ALOGV("%s: pcm_config narrowband", __func__);
136 voice_config = &pcm_config_voicecall;
137 }
138
139 /* Open modem PCM channels */
140 session->pcm_voice_rx = pcm_open(SOUND_CARD,
141 SOUND_PLAYBACK_VOICE_DEVICE,
142 PCM_OUT|PCM_MONOTONIC,
143 voice_config);
144 if (session->pcm_voice_rx != NULL && !pcm_is_ready(session->pcm_voice_rx)) {
145 ALOGE("%s: cannot open PCM voice RX stream: %s",
146 __func__,
147 pcm_get_error(session->pcm_voice_rx));
148
149 pcm_close(session->pcm_voice_tx);
150 session->pcm_voice_tx = NULL;
151
152 return -ENOMEM;
153 }
154
155 session->pcm_voice_tx = pcm_open(SOUND_CARD,
156 SOUND_CAPTURE_VOICE_DEVICE,
157 PCM_IN|PCM_MONOTONIC,
158 voice_config);
159 if (session->pcm_voice_tx != NULL && !pcm_is_ready(session->pcm_voice_tx)) {
160 ALOGE("%s: cannot open PCM voice TX stream: %s",
161 __func__,
162 pcm_get_error(session->pcm_voice_tx));
163
164 pcm_close(session->pcm_voice_rx);
165 session->pcm_voice_rx = NULL;
166
167 return -ENOMEM;
168 }
169
170 pcm_start(session->pcm_voice_rx);
171 pcm_start(session->pcm_voice_tx);
172
173 /* TODO: handle SCO */
174
175 if (session->two_mic_control) {
176 ALOGV("%s: enabling two mic control", __func__);
177 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_ON);
178 } else {
179 ALOGV("%s: disabling two mic control", __func__);
180 ril_set_two_mic_control(&session->ril, AUDIENCE, TWO_MIC_SOLUTION_OFF);
181 }
182
183 ril_set_call_clock_sync(&session->ril, SOUND_CLOCK_START);
184
185 return 0;
186}
187
188/*
189 * This function must be called with hw device mutex locked, OK to hold other
190 * mutexes
191 */
192void stop_voice_session(struct voice_session *session)
193{
194 int status = 0;
195
196 ALOGV("%s: Closing active PCMs", __func__);
197
198 if (session->pcm_voice_rx != NULL) {
199 pcm_stop(session->pcm_voice_rx);
200 pcm_close(session->pcm_voice_rx);
201 session->pcm_voice_rx = NULL;
202 status++;
203 }
204
205 if (session->pcm_voice_tx != NULL) {
206 pcm_stop(session->pcm_voice_tx);
207 pcm_close(session->pcm_voice_tx);
208 session->pcm_voice_tx = NULL;
209 status++;
210 }
211
212 /* TODO: handle SCO */
213
214 session->out_device = AUDIO_DEVICE_NONE;
215
216 ALOGV("%s: Successfully closed %d active PCMs", __func__, status);
217}
218
219void set_voice_session_volume(struct voice_session *session, float volume)
220{
221 enum _SoundType sound_type;
222
223 switch (session->out_device) {
224 case AUDIO_DEVICE_OUT_EARPIECE:
225 sound_type = SOUND_TYPE_VOICE;
226 break;
227 case AUDIO_DEVICE_OUT_SPEAKER:
228 sound_type = SOUND_TYPE_SPEAKER;
229 break;
230 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
231 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
232 sound_type = SOUND_TYPE_HEADSET;
233 break;
234 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
235 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
236 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
237 case AUDIO_DEVICE_OUT_ALL_SCO:
238 sound_type = SOUND_TYPE_BTVOICE;
239 break;
240 default:
241 sound_type = SOUND_TYPE_VOICE;
242 }
243
244 ril_set_call_volume(&session->ril, sound_type, volume);
245}
246
Andreas Schneider107a8482017-02-06 12:36:31 +0100247void set_voice_session_mic_mute(struct voice_session *session, bool state)
248{
249 enum _MuteCondition mute_condition = state ? TX_MUTE : TX_UNMUTE;
250
251 ril_set_mute(&session->ril, mute_condition);
252}
253
Andreas Schneider82f32482017-02-06 09:00:48 +0100254bool voice_session_uses_twomic(struct voice_session *session)
255{
256 if (session->two_mic_disabled) {
257 return false;
258 }
259
260 return session->two_mic_control;
261}
262
Andreas Schneider59486fa2017-02-06 09:16:39 +0100263bool voice_session_uses_wideband(struct voice_session *session)
264{
265 return session->wb_amr;
266}
267
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100268static void voice_session_wb_amr_callback(void *data, int enable)
269{
270 struct audio_device *adev = (struct audio_device *)data;
271 struct voice_session *session =
272 (struct voice_session *)adev->voice.session;
273
274 pthread_mutex_lock(&adev->lock);
275
276 if (session->wb_amr != enable) {
277 session->wb_amr = enable;
278
279 /* reopen the modem PCMs at the new rate */
280 if (adev->voice.in_call) {
281 ALOGV("%s: %s wide band voice call",
282 __func__,
283 enable ? "Enable" : "Disable");
284
285 stop_voice_call(adev);
286 start_voice_call(adev);
287 }
288 }
289
290 pthread_mutex_unlock(&adev->lock);
291}
292
293struct voice_session *voice_session_init(struct audio_device *adev)
294{
295 char voice_config[PROPERTY_VALUE_MAX];
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100296 struct voice_session *session;
297 int ret;
298
299 session = calloc(1, sizeof(struct voice_session));
300 if (session == NULL) {
301 return NULL;
302 }
303
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100304 /* Two mic control */
305 ret = property_get_bool("audio_hal.disable_two_mic", false);
306 if (ret > 0) {
307 session->two_mic_disabled = true;
308 }
309
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100310 /* Do this as the last step so we do not have to close it on error */
311 ret = ril_open(&session->ril);
312 if (ret != 0) {
313 free(session);
314 return NULL;
315 }
316
Christopher N. Hesse696959d2017-02-02 20:49:55 +0100317 ret = property_get("audio_hal.force_voice_config", voice_config, "");
318 if (ret > 0) {
319 if ((strncmp(voice_config, "narrow", 6)) == 0)
320 session->wb_amr = false;
321 else if ((strncmp(voice_config, "wide", 4)) == 0)
322 session->wb_amr = true;
323 ALOGV("%s: Forcing voice config: %s", __func__, voice_config);
324 } else {
325 /* register callback for wideband AMR setting */
326 ret = ril_set_wb_amr_callback(&session->ril,
327 voice_session_wb_amr_callback,
328 (void *)adev);
329 if (ret != 0) {
330 ALOGE("%s: Failed to register WB_AMR callback", __func__);
331 free(session);
332 return NULL;
333 }
334
335 ALOGV("%s: Registered WB_AMR callback", __func__);
336 }
337
Christopher N. Hesse41c9f3d2017-02-02 20:48:56 +0100338 return session;
339}
340
341void voice_session_deinit(struct voice_session *session)
342{
343 ril_close(&session->ril);
344 free(session);
345}