4 * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
29 #include "tizen-audio-internal.h"
30 #include "tizen-audio-impl.h"
32 /* #define DEBUG_TIMING */
34 static device_type_t outDeviceTypes[] = {
35 { AUDIO_DEVICE_OUT_SPEAKER, "Speaker" },
36 { AUDIO_DEVICE_OUT_RECEIVER, "Earpiece" },
37 { AUDIO_DEVICE_OUT_JACK, "Headphones" },
38 { AUDIO_DEVICE_OUT_BT_SCO, "Bluetooth" },
42 static device_type_t inDeviceTypes[] = {
43 { AUDIO_DEVICE_IN_MAIN_MIC, "MainMic" },
44 { AUDIO_DEVICE_IN_SUB_MIC, "SubMic" },
45 { AUDIO_DEVICE_IN_JACK, "HeadsetMic" },
46 { AUDIO_DEVICE_IN_BT_SCO, "BT Mic" },
50 static const char* mode_to_verb_str[] = {
51 AUDIO_USE_CASE_VERB_HIFI,
52 AUDIO_USE_CASE_VERB_VOICECALL,
53 AUDIO_USE_CASE_VERB_VIDEOCALL,
54 AUDIO_USE_CASE_VERB_VOIP,
55 AUDIO_USE_CASE_VERB_FMRADIO,
58 static uint32_t __convert_device_string_to_enum(const char* device_str, uint32_t direction)
62 if (!strncmp(device_str, "builtin-speaker", MAX_NAME_LEN)) {
63 device = AUDIO_DEVICE_OUT_SPEAKER;
64 } else if (!strncmp(device_str, "builtin-receiver", MAX_NAME_LEN)) {
65 device = AUDIO_DEVICE_OUT_RECEIVER;
66 } else if ((!strncmp(device_str, "audio-jack", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_OUT)) {
67 device = AUDIO_DEVICE_OUT_JACK;
68 } else if ((!strncmp(device_str, "bt-sco", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_OUT)) {
69 device = AUDIO_DEVICE_OUT_BT_SCO;
70 } else if ((!strncmp(device_str, "builtin-mic", MAX_NAME_LEN))) {
71 device = AUDIO_DEVICE_IN_MAIN_MIC;
73 } else if ((!strncmp(device_str, "audio-jack", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_IN)) {
74 device = AUDIO_DEVICE_IN_JACK;
75 } else if ((!strncmp(device_str, "bt-sco", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_IN)) {
76 device = AUDIO_DEVICE_IN_BT_SCO;
78 device = AUDIO_DEVICE_NONE;
80 AUDIO_LOG_INFO("device type(%s), enum(0x%x)", device_str, device);
84 static void __reset_voice_devices_info(audio_hal_t *ah)
86 AUDIO_RETURN_IF_FAIL(ah);
88 AUDIO_LOG_INFO("reset voice device info");
89 if (ah->device.init_call_devices) {
90 free(ah->device.init_call_devices);
91 ah->device.init_call_devices = NULL;
92 ah->device.num_of_call_devices = 0;
98 static audio_return_t __set_devices(audio_hal_t *ah, const char *verb, device_info_t *devices, uint32_t num_of_devices)
100 audio_return_t audio_ret = AUDIO_RET_OK;
101 uint32_t new_device = 0;
102 const char *active_devices[MAX_DEVICES] = {NULL,};
103 int i = 0, j = 0, dev_idx = 0;
105 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
106 AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER);
107 AUDIO_RETURN_VAL_IF_FAIL(num_of_devices, AUDIO_ERR_PARAMETER);
109 if (num_of_devices > MAX_DEVICES) {
110 num_of_devices = MAX_DEVICES;
111 AUDIO_LOG_ERROR("error: num_of_devices");
112 return AUDIO_ERR_PARAMETER;
115 if (devices[0].direction == AUDIO_DIRECTION_OUT) {
116 ah->device.active_out &= 0x0;
117 if (ah->device.active_in) {
118 /* check the active in devices */
119 for (j = 0; j < inDeviceTypes[j].type; j++) {
120 if (((ah->device.active_in & (~AUDIO_DEVICE_IN)) & inDeviceTypes[j].type))
121 active_devices[dev_idx++] = inDeviceTypes[j].name;
124 } else if (devices[0].direction == AUDIO_DIRECTION_IN) {
125 ah->device.active_in &= 0x0;
126 if (ah->device.active_out) {
127 /* check the active out devices */
128 for (j = 0; j < outDeviceTypes[j].type; j++) {
129 if (ah->device.active_out & outDeviceTypes[j].type)
130 active_devices[dev_idx++] = outDeviceTypes[j].name;
135 for (i = 0; i < num_of_devices; i++) {
136 new_device = __convert_device_string_to_enum(devices[i].type, devices[i].direction);
137 if (new_device & AUDIO_DEVICE_IN) {
138 for (j = 0; j < inDeviceTypes[j].type; j++) {
139 if (new_device == inDeviceTypes[j].type) {
140 active_devices[dev_idx++] = inDeviceTypes[j].name;
141 ah->device.active_in |= new_device;
145 for (j = 0; j < outDeviceTypes[j].type; j++) {
146 if (new_device == outDeviceTypes[j].type) {
147 active_devices[dev_idx++] = outDeviceTypes[j].name;
148 ah->device.active_out |= new_device;
154 if (active_devices[0] == NULL) {
155 AUDIO_LOG_ERROR("Failed to set device: active device is NULL");
156 return AUDIO_ERR_PARAMETER;
159 audio_ret = _ucm_set_devices(ah, verb, active_devices);
161 AUDIO_LOG_ERROR("Failed to set device: error = %d", audio_ret);
166 static audio_return_t __connect_fm_radio(audio_hal_t *ah)
168 audio_return_t audio_ret = AUDIO_RET_OK;
170 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
172 audio_ret = _mixer_control_set_value(ah,
173 PIN_SWITCH_IIS0_SYS_SEL, PIN_SWITCH_IIS0_VBC_ID);
177 static audio_return_t __update_route_ap_playback_capture(audio_hal_t *ah, audio_route_info_t *route_info)
179 audio_return_t audio_ret = AUDIO_RET_OK;
180 device_info_t *devices = NULL;
181 const char *verb = mode_to_verb_str[VERB_NORMAL];
183 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
184 AUDIO_RETURN_VAL_IF_FAIL(route_info, AUDIO_ERR_PARAMETER);
186 if (ah->modem.is_connected) {
187 AUDIO_LOG_WARN("modem is connected, skip verb[%s]", verb);
191 if (ah->device.mode != VERB_NORMAL) {
192 if (ah->device.mode == VERB_RADIO) {
193 if (ah->device.is_radio_on && !ah->modem.is_connected) {
194 /* Skip setting NORMAL VERB and keep going with RADIO VERB */
195 AUDIO_LOG_WARN("radio is in progress, skip verb[%s]", verb);
196 verb = mode_to_verb_str[VERB_RADIO];
197 } else if (!ah->device.is_radio_on) {
198 if ((audio_ret = _fmradio_pcm_close(ah)))
199 AUDIO_LOG_ERROR("failed to _fmradio_pcm_close(), ret(0x%x)", audio_ret);
202 if (ah->device.mode == VERB_VOICECALL) {
203 __reset_voice_devices_info(ah);
204 COND_SIGNAL(ah->device.device_cond, "device_cond");
206 _reset_pcm_devices(ah);
207 ah->device.mode = VERB_NORMAL;
211 devices = route_info->device_infos;
213 AUDIO_LOG_INFO("update_route_ap_playback_capture++ ");
215 audio_ret = __set_devices(ah, verb, devices, route_info->num_of_devices);
217 AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret);
224 static audio_return_t __update_route_voicecall(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices)
226 audio_return_t audio_ret = AUDIO_RET_OK;
227 const char *verb = mode_to_verb_str[VERB_VOICECALL];
229 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
230 /* if both params are 0, return error for invalid state,
231 * this error would be used to tizen-audio-modem.c */
232 AUDIO_RETURN_VAL_IF_FAIL((devices||num_of_devices), AUDIO_ERR_INVALID_STATE);
233 AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER);
235 AUDIO_LOG_INFO("update_route_voicecall++");
237 audio_ret = __set_devices(ah, verb, devices, num_of_devices);
239 AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret);
243 if (ah->device.mode != VERB_VOICECALL) {
245 ah->device.mode = VERB_VOICECALL;
246 /* FIXME. Get network info and configure rate in pcm device */
252 static audio_return_t __update_route_voip(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices)
254 audio_return_t audio_ret = AUDIO_RET_OK;
255 const char *verb = mode_to_verb_str[VERB_NORMAL];
257 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
258 AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER);
260 AUDIO_LOG_INFO("update_route_voip++");
262 audio_ret = __set_devices(ah, verb, devices, num_of_devices);
264 AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret);
267 /* FIXME. If necessary, set VERB_VOIP */
268 ah->device.mode = VERB_NORMAL;
270 /* TO DO: Set modifiers */
274 static audio_return_t __update_route_fmradio(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices)
276 audio_return_t audio_ret = AUDIO_RET_OK;
277 const char *verb = mode_to_verb_str[VERB_RADIO];
279 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
280 AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER);
282 AUDIO_LOG_INFO("update_route_fmradio++");
284 if (!ah->device.fmradio_pcm_out) {
285 if ((audio_ret = _fmradio_pcm_open(ah))) {
286 AUDIO_LOG_ERROR("Failed to _fmradio_pcm_open(): error = 0x%x", audio_ret);
291 audio_ret = __set_devices(ah, verb, devices, num_of_devices);
293 AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret);
296 /* FIXME. If necessary, set VERB_VOIP */
297 ah->device.mode = VERB_RADIO;
299 if ((audio_ret = __connect_fm_radio(ah)))
300 AUDIO_LOG_ERROR("failed to __connect_fm_radio(), ret(0x%x)", audio_ret);
302 /* TO DO: Set modifiers */
306 static audio_return_t __update_route_reset(audio_hal_t *ah, uint32_t direction)
308 audio_return_t audio_ret = AUDIO_RET_OK;
309 const char *active_devices[MAX_DEVICES] = {NULL,};
310 int i = 0, dev_idx = 0;
312 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
314 AUDIO_LOG_INFO("update_route_reset++, direction(0x%x)", direction);
316 if (direction == AUDIO_DIRECTION_OUT) {
317 ah->device.active_out &= 0x0;
318 if (ah->device.active_in) {
319 /* check the active in devices */
320 for (i = 0; i < inDeviceTypes[i].type; i++) {
321 if (((ah->device.active_in & (~AUDIO_DEVICE_IN)) & inDeviceTypes[i].type)) {
322 active_devices[dev_idx++] = inDeviceTypes[i].name;
323 AUDIO_LOG_INFO("added for in : %s", inDeviceTypes[i].name);
328 ah->device.active_in &= 0x0;
329 if (ah->device.active_out) {
330 /* check the active out devices */
331 for (i = 0; i < outDeviceTypes[i].type; i++) {
332 if (ah->device.active_out & outDeviceTypes[i].type) {
333 active_devices[dev_idx++] = outDeviceTypes[i].name;
334 AUDIO_LOG_INFO("added for out : %s", outDeviceTypes[i].name);
339 if (ah->device.mode == VERB_VOICECALL) {
340 if ((audio_ret = _voice_pcm_close(ah, direction)))
341 AUDIO_LOG_ERROR("failed to _voice_pcm_close(), ret(0x%x)", audio_ret);
342 if (!ah->device.active_in && !ah->device.active_out)
343 ah->device.mode = VERB_NORMAL;
344 __reset_voice_devices_info(ah);
345 COND_SIGNAL(ah->device.device_cond, "device_cond");
346 } else if (ah->device.mode == VERB_RADIO) {
347 if ((audio_ret = _fmradio_pcm_close(ah)))
348 AUDIO_LOG_ERROR("failed to _fmradio_pcm_close(), ret(0x%x)", audio_ret);
349 ah->device.mode = VERB_NORMAL;
352 if (active_devices[0] == NULL) {
353 AUDIO_LOG_DEBUG("active device is NULL, no need to update.");
357 if ((audio_ret = _ucm_set_devices(ah, mode_to_verb_str[ah->device.mode], active_devices)))
358 AUDIO_LOG_ERROR("failed to _ucm_set_devices(), ret(0x%x)", audio_ret);
363 audio_return_t _audio_update_route_voicecall(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices)
365 return __update_route_voicecall(ah, devices, num_of_devices);
368 audio_return_t _audio_routing_init(audio_hal_t *ah)
370 audio_return_t audio_ret = AUDIO_RET_OK;
372 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
374 ah->device.active_in = 0x0;
375 ah->device.active_out = 0x0;
376 ah->device.mode = VERB_NORMAL;
377 ah->device.is_radio_on = 0;
379 if ((audio_ret = _ucm_init(ah)))
380 AUDIO_LOG_ERROR("failed to _ucm_init(), ret(0x%x)", audio_ret);
385 audio_return_t _audio_routing_deinit(audio_hal_t *ah)
387 audio_return_t audio_ret = AUDIO_RET_OK;
389 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
391 if ((audio_ret = _ucm_deinit(ah)))
392 AUDIO_LOG_ERROR("failed to _ucm_deinit(), ret(0x%x)", audio_ret);
397 audio_return_t audio_update_route(void *audio_handle, audio_route_info_t *info)
399 audio_return_t audio_ret = AUDIO_RET_OK;
400 audio_hal_t *ah = (audio_hal_t *)audio_handle;
401 device_info_t *devices = NULL;
406 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
407 AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
408 AUDIO_RETURN_VAL_IF_FAIL(info->role, AUDIO_ERR_PARAMETER);
410 AUDIO_LOG_INFO("role:%s", info->role);
412 devices = info->device_infos;
414 if (!strncmp("call-voice", info->role, MAX_NAME_LEN)) {
415 if (!ah->modem.is_connected) {
416 if (info->num_of_devices) {
417 if (!ah->device.num_of_call_devices) {
418 if ((ah->device.init_call_devices = (device_info_t*)calloc(info->num_of_devices, sizeof(device_info_t)))) {
419 memcpy(ah->device.init_call_devices, devices, info->num_of_devices*sizeof(device_info_t));
420 ah->device.num_of_call_devices = info->num_of_devices;
422 AUDIO_LOG_ERROR("failed to calloc");
423 audio_ret = AUDIO_ERR_RESOURCE;
426 } else if (ah->device.num_of_call_devices) {
427 prev_size = ah->device.num_of_call_devices;
428 if (prev_size == 2) {
429 /* There's a chance to be requested to change routing from user
430 * though two devices(for input/output) has already been set for call-voice routing.
431 * In this case, exchange an old device for a new device if it's direction is same as an old one's. */
432 for (i = 0; i < prev_size; i++) {
433 for (j = 0; j < info->num_of_devices; j++) {
434 if (devices[j].direction == ah->device.init_call_devices[i].direction &&
435 devices[j].id != ah->device.init_call_devices[i].id)
436 memcpy(&ah->device.init_call_devices[i], &devices[j], sizeof(device_info_t));
439 } else if (prev_size < 2) {
440 /* A device has already been added for call-voice routing,
441 * and now it is about to add a new device(input or output device). */
442 ah->device.num_of_call_devices += info->num_of_devices;
443 if ((ah->device.init_call_devices = (device_info_t*)realloc(ah->device.init_call_devices, sizeof(device_info_t)*ah->device.num_of_call_devices))) {
444 memcpy((void*)&(ah->device.init_call_devices[prev_size]), devices, info->num_of_devices*sizeof(device_info_t));
446 AUDIO_LOG_ERROR("failed to realloc");
447 audio_ret = AUDIO_ERR_RESOURCE;
451 AUDIO_LOG_ERROR("invaild previous num. of call devices");
452 audio_ret = AUDIO_ERR_INTERNAL;
457 AUDIO_LOG_ERROR("failed to update route for call-voice, num_of_devices is 0");
458 audio_ret = AUDIO_ERR_PARAMETER;
461 AUDIO_LOG_INFO("modem is not ready, skip...");
463 if ((audio_ret = __update_route_voicecall(ah, devices, info->num_of_devices)))
464 AUDIO_LOG_WARN("update voicecall route return 0x%x", audio_ret);
466 COND_SIGNAL(ah->device.device_cond, "device_cond");
468 } else if (!strncmp("voip", info->role, MAX_NAME_LEN)) {
469 if ((audio_ret = __update_route_voip(ah, devices, info->num_of_devices)))
470 AUDIO_LOG_WARN("update voip route return 0x%x", audio_ret);
472 } else if (!strncmp("radio", info->role, MAX_NAME_LEN)) {
473 if ((audio_ret = __update_route_fmradio(ah, devices, info->num_of_devices)))
474 AUDIO_LOG_WARN("update radio route return 0x%x", audio_ret);
476 } else if (!strncmp("reset", info->role, MAX_NAME_LEN)) {
477 if ((audio_ret = __update_route_reset(ah, devices->direction)))
478 AUDIO_LOG_WARN("update reset return 0x%x", audio_ret);
481 /* need to prepare for "alarm","notification","emergency","voice-information","voice-recognition","ringtone" */
482 if ((audio_ret = __update_route_ap_playback_capture(ah, info)))
483 AUDIO_LOG_WARN("update playback route return 0x%x", audio_ret);
490 audio_return_t audio_update_route_option(void *audio_handle, audio_route_option_t *option)
492 audio_return_t audio_ret = AUDIO_RET_OK;
493 audio_hal_t *ah = (audio_hal_t *)audio_handle;
495 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
496 AUDIO_RETURN_VAL_IF_FAIL(option, AUDIO_ERR_PARAMETER);
498 AUDIO_LOG_INFO("role:%s, name:%s, value:%d", option->role, option->name, option->value);
500 /* Handle RADIO MUTE due to earjack disconnection */
501 if ((option->role && strcmp(option->role, "radio") == 0) &&
502 (option->name && strcmp(option->name, "mute") == 0) &&
503 option->value == 1) {
504 AUDIO_LOG_INFO("MUTE RADIO!!!!");
505 audio_ret = _mixer_control_set_value(ah, MIXER_FMRADIO_MUTE, 0);