4 * Copyright (c) 2000 - 2013 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"
31 /* #define DEBUG_TIMING */
33 static device_type_t outDeviceTypes[] = {
34 { AUDIO_DEVICE_OUT_SPEAKER, "Speaker" },
35 { AUDIO_DEVICE_OUT_RECEIVER, "Earpiece" },
36 { AUDIO_DEVICE_OUT_JACK, "Headphones" },
37 { AUDIO_DEVICE_OUT_BT_SCO, "Bluetooth" },
38 { AUDIO_DEVICE_OUT_HDMI, "HDMI" },
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 uint32_t convert_device_string_to_enum(const char* device_str, uint32_t direction)
54 if (!strncmp(device_str,"builtin-speaker", MAX_NAME_LEN)) {
55 device = AUDIO_DEVICE_OUT_SPEAKER;
56 } else if (!strncmp(device_str,"builtin-receiver", MAX_NAME_LEN)) {
57 device = AUDIO_DEVICE_OUT_RECEIVER;
58 } else if ((!strncmp(device_str,"audio-jack", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_OUT)) {
59 device = AUDIO_DEVICE_OUT_JACK;
60 } else if ((!strncmp(device_str,"bt", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_OUT)) {
61 device = AUDIO_DEVICE_OUT_BT_SCO;
62 } else if (!strncmp(device_str,"hdmi", MAX_NAME_LEN)) {
63 device = AUDIO_DEVICE_OUT_HDMI;
64 } else if ((!strncmp(device_str,"builtin-mic", MAX_NAME_LEN))) {
65 device = AUDIO_DEVICE_IN_MAIN_MIC;
67 } else if ((!strncmp(device_str,"audio-jack", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_IN)) {
68 device = AUDIO_DEVICE_IN_JACK;
69 } else if ((!strncmp(device_str,"bt", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_IN)) {
70 device = AUDIO_DEVICE_IN_BT_SCO;
72 device = AUDIO_DEVICE_NONE;
74 AUDIO_LOG_INFO("device type(%s), enum(0x%x)", device_str, device);
78 static audio_return_t set_devices(audio_hal_t *ah, const char *verb, device_info_t *devices, uint32_t num_of_devices)
80 audio_return_t audio_ret = AUDIO_RET_OK;
81 uint32_t new_device = 0;
82 const char *active_devices[MAX_DEVICES] = {NULL,};
83 int i = 0, j = 0, dev_idx = 0;
85 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
86 AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER);
88 if (num_of_devices > MAX_DEVICES) {
89 num_of_devices = MAX_DEVICES;
90 AUDIO_LOG_ERROR("error: num_of_devices");
91 return AUDIO_ERR_PARAMETER;
94 if ((devices[0].direction == AUDIO_DIRECTION_OUT) && ah->device.active_in) {
95 /* check the active in devices */
96 for (j = 0; j < inDeviceTypes[j].type; j++) {
97 if (((ah->device.active_in & (~AUDIO_DEVICE_IN)) & inDeviceTypes[j].type))
98 active_devices[dev_idx++] = inDeviceTypes[j].name;
100 } else if ((devices[0].direction == AUDIO_DIRECTION_IN) && ah->device.active_out) {
101 /* check the active out devices */
102 for (j = 0; j < outDeviceTypes[j].type; j++) {
103 if (ah->device.active_out & outDeviceTypes[j].type)
104 active_devices[dev_idx++] = outDeviceTypes[j].name;
108 for (i = 0; i < num_of_devices; i++) {
109 new_device = convert_device_string_to_enum(devices[i].type, devices[i].direction);
110 if (new_device & AUDIO_DEVICE_IN) {
111 for (j = 0; j < inDeviceTypes[j].type; j++) {
112 if (new_device == inDeviceTypes[j].type) {
113 active_devices[dev_idx++] = inDeviceTypes[j].name;
114 ah->device.active_in |= new_device;
118 for (j = 0; j < outDeviceTypes[j].type; j++) {
119 if (new_device == outDeviceTypes[j].type) {
120 active_devices[dev_idx++] = outDeviceTypes[j].name;
121 ah->device.active_out |= new_device;
127 if (active_devices[0] == NULL) {
128 AUDIO_LOG_ERROR("Failed to set device: active device is NULL");
129 return AUDIO_ERR_PARAMETER;
132 audio_ret = _audio_ucm_set_devices(ah, verb, active_devices);
134 AUDIO_LOG_ERROR("Failed to set device: error = %d", audio_ret);
141 audio_return_t _audio_device_init(audio_hal_t *ah)
143 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
145 ah->device.active_in = 0x0;
146 ah->device.active_out = 0x0;
147 ah->device.pcm_in = NULL;
148 ah->device.pcm_out = NULL;
149 ah->device.mode = VERB_NORMAL;
150 pthread_mutex_init(&ah->device.pcm_lock, NULL);
151 ah->device.pcm_count = 0;
156 audio_return_t _audio_device_deinit(audio_hal_t *ah)
158 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
163 static audio_return_t _do_route_ap_playback_capture(audio_hal_t *ah, audio_route_info_t *route_info)
165 audio_return_t audio_ret = AUDIO_RET_OK;
166 device_info_t *devices = NULL;
167 const char *verb = NULL;
169 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
170 AUDIO_RETURN_VAL_IF_FAIL(route_info, AUDIO_ERR_PARAMETER);
172 devices = route_info->device_infos;
174 /* To Do: Set modifiers */
175 /* int mod_idx = 0; */
176 /* const char *modifiers[MAX_MODIFIERS] = {NULL,}; */
178 verb = AUDIO_USE_CASE_VERB_HIFI;
179 AUDIO_LOG_INFO("do_route_ap_playback_capture++ ");
181 audio_ret = set_devices(ah, verb, devices, route_info->num_of_devices);
183 AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret);
186 ah->device.mode = VERB_NORMAL;
188 /* To Do: Set modifiers */
190 if (!strncmp("voice_recognition", route_info->role, MAX_NAME_LEN)) {
191 modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_VOICESEARCH;
192 } else if ((!strncmp("alarm", route_info->role, MAX_NAME_LEN))||(!strncmp("notifiication", route_info->role, MAX_NAME_LEN))) {
193 if (ah->device.active_out &= AUDIO_DEVICE_OUT_JACK)
194 modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_DUAL_MEDIA;
196 modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_MEDIA;
197 } else if (!strncmp("ringtone", route_info->role, MAX_NAME_LEN)) {
198 if (ah->device.active_out &= AUDIO_DEVICE_OUT_JACK)
199 modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_DUAL_RINGTONE;
201 modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_RINGTONE;
203 if (ah->device.active_in)
204 modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_CAMCORDING;
206 modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_MEDIA;
208 audio_ret = _audio_ucm_set_modifiers (ah, verb, modifiers);
213 audio_return_t _do_route_voicecall(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices)
215 audio_return_t audio_ret = AUDIO_RET_OK;
216 const char *verb = NULL;
217 verb = AUDIO_USE_CASE_VERB_VOICECALL;
219 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
220 AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER);
222 AUDIO_LOG_INFO("do_route_voicecall++");
224 audio_ret = set_devices(ah, verb, devices, num_of_devices);
226 AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret);
229 /* FIXME. Get network info and configure rate in pcm device */
230 ah->device.mode = VERB_CALL;
231 if (ah->device.active_out && ah->device.active_in)
236 audio_return_t _do_route_voip(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices)
238 audio_return_t audio_ret = AUDIO_RET_OK;
239 const char *verb = NULL;
240 const char *active_devices[MAX_DEVICES] = {NULL,};
241 verb = AUDIO_USE_CASE_VERB_HIFI;
243 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
244 AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER);
246 AUDIO_LOG_INFO("do_route_voip++");
248 audio_ret = set_devices(ah, verb, devices, num_of_devices);
250 AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret);
253 /* FIXME. If necessary, set VERB_VOIP */
254 ah->device.mode = VERB_NORMAL;
255 if (active_devices == NULL) {
256 AUDIO_LOG_ERROR("Failed to set device: active device is NULL");
257 return AUDIO_ERR_PARAMETER;
260 /* TO DO: Set modifiers */
264 audio_return_t _do_route_reset(audio_hal_t *ah, uint32_t direction)
266 audio_return_t audio_ret = AUDIO_RET_OK;
268 /* FIXME: If you need to reset, set verb inactive */
269 /* const char *verb = NULL; */
270 /* verb = AUDIO_USE_CASE_VERB_INACTIVE; */
272 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
274 AUDIO_LOG_INFO("do_route_reset++, direction(%p)", direction);
276 if (direction == AUDIO_DIRECTION_OUT) {
277 ah->device.active_out &= 0x0;
279 ah->device.active_in &= 0x0;
281 if (ah->device.mode == VERB_CALL) {
282 _voice_pcm_close(ah, direction);
284 /* TO DO: Set Inactive */
288 audio_return_t audio_do_route(void *audio_handle, audio_route_info_t *info)
290 audio_return_t audio_ret = AUDIO_RET_OK;
291 audio_hal_t *ah = (audio_hal_t *)audio_handle;
292 device_info_t *devices = NULL;
294 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
295 AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
297 AUDIO_LOG_INFO("role:%s", info->role);
299 devices = info->device_infos;
301 if (!strncmp("call-voice", info->role, MAX_NAME_LEN)) {
302 audio_ret = _do_route_voicecall(ah, devices, info->num_of_devices);
303 if (AUDIO_IS_ERROR(audio_ret)) {
304 AUDIO_LOG_WARN("set voicecall route return 0x%x", audio_ret);
306 } else if (!strncmp("voip", info->role, MAX_NAME_LEN)) {
307 audio_ret = _do_route_voip(ah, devices, info->num_of_devices);
308 if (AUDIO_IS_ERROR(audio_ret)) {
309 AUDIO_LOG_WARN("set voip route return 0x%x", audio_ret);
311 } else if (!strncmp("reset", info->role, MAX_NAME_LEN)) {
312 audio_ret = _do_route_reset(ah, devices->direction);
313 if (AUDIO_IS_ERROR(audio_ret)) {
314 AUDIO_LOG_WARN("set reset return 0x%x", audio_ret);
317 /* need to prepare for "alarm","notification","emergency","voice-information","voice-recognition","ringtone" */
318 audio_ret = _do_route_ap_playback_capture(ah, info);
320 if (AUDIO_IS_ERROR(audio_ret)) {
321 AUDIO_LOG_WARN("set playback route return 0x%x", audio_ret);
327 audio_return_t audio_update_stream_connection_info(void *audio_handle, audio_stream_info_t *info, uint32_t is_connected)
329 audio_return_t audio_ret = AUDIO_RET_OK;
330 audio_hal_t *ah = (audio_hal_t *)audio_handle;
332 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
333 AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
335 AUDIO_LOG_INFO("role:%s, direction:%u, idx:%u, is_connected:%d", info->role, info->direction, info->idx, is_connected);
340 audio_return_t audio_update_route_option(void *audio_handle, audio_route_option_t *option)
342 audio_return_t audio_ret = AUDIO_RET_OK;
343 audio_hal_t *ah = (audio_hal_t *)audio_handle;
345 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
346 AUDIO_RETURN_VAL_IF_FAIL(option, AUDIO_ERR_PARAMETER);
348 AUDIO_LOG_INFO("role:%s, name:%s, value:%d", option->role, option->name, option->value);
353 static int __voice_pcm_set_params(audio_hal_t *ah, snd_pcm_t *pcm)
355 snd_pcm_hw_params_t *params = NULL;
357 unsigned int val = 0;
359 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
360 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
362 /* Skip parameter setting to null device. */
363 if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
364 return AUDIO_ERR_IOCTL;
366 /* Allocate a hardware parameters object. */
367 snd_pcm_hw_params_alloca(¶ms);
369 /* Fill it in with default values. */
370 if (snd_pcm_hw_params_any(pcm, params) < 0) {
371 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
375 /* Set the desired hardware parameters. */
376 /* Interleaved mode */
377 err = snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED);
379 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() : failed! - %s\n", snd_strerror(err));
382 err = snd_pcm_hw_params_set_rate(pcm, params, 16000, 0);
384 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
386 err = snd_pcm_hw_params(pcm, params);
388 AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
392 /* Dump current param */
393 snd_pcm_hw_params_get_access(params, (snd_pcm_access_t *) &val);
394 AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
396 snd_pcm_hw_params_get_format(params, (snd_pcm_format_t*)&val);
397 AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
398 snd_pcm_format_name((snd_pcm_format_t)val),
399 snd_pcm_format_description((snd_pcm_format_t)val));
401 snd_pcm_hw_params_get_subformat(params, (snd_pcm_subformat_t *)&val);
402 AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
403 snd_pcm_subformat_name((snd_pcm_subformat_t)val),
404 snd_pcm_subformat_description((snd_pcm_subformat_t)val));
406 snd_pcm_hw_params_get_channels(params, &val);
407 AUDIO_LOG_DEBUG("channels = %d\n", val);
415 int _voice_pcm_open(audio_hal_t *ah)
419 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
421 AUDIO_LOG_INFO("open voice pcm handles");
423 /* Get playback voice-pcm from ucm conf. Open and set-params */
424 if ((err = snd_pcm_open((snd_pcm_t **)&ah->device.pcm_out, VOICE_PCM_DEVICE, AUDIO_DIRECTION_OUT, 0)) < 0) {
425 AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %s", VOICE_PCM_DEVICE, snd_strerror(err));
426 return AUDIO_ERR_IOCTL;
428 ret = __voice_pcm_set_params(ah, ah->device.pcm_out);
430 AUDIO_LOG_INFO("pcm playback device open success device(%s)", VOICE_PCM_DEVICE);
432 /* Get capture voice-pcm from ucm conf. Open and set-params */
433 if ((err = snd_pcm_open((snd_pcm_t **)&ah->device.pcm_in, VOICE_PCM_DEVICE, AUDIO_DIRECTION_IN, 0)) < 0) {
434 AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %s", VOICE_PCM_DEVICE, snd_strerror(err));
435 return AUDIO_ERR_IOCTL;
437 ret = __voice_pcm_set_params(ah, ah->device.pcm_in);
438 AUDIO_LOG_INFO("pcm captures device open success device(%s)", VOICE_PCM_DEVICE);
443 int _voice_pcm_close(audio_hal_t *ah, uint32_t direction)
445 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
447 AUDIO_LOG_INFO("close voice pcm handles");
449 if (ah->device.pcm_out && (direction == AUDIO_DIRECTION_OUT)) {
450 audio_pcm_close((void *)ah, ah->device.pcm_out);
451 ah->device.pcm_out = NULL;
452 AUDIO_LOG_INFO("voice pcm_out handle close success");
453 } else if (ah->device.pcm_in && (direction == AUDIO_DIRECTION_IN)) {
454 audio_pcm_close((void *)ah, ah->device.pcm_in);
455 ah->device.pcm_in = NULL;
456 AUDIO_LOG_INFO("voice pcm_in handle close success");
462 #ifdef __USE_TINYALSA__
463 static struct pcm *__tinyalsa_open_device(audio_pcm_sample_spec_t *ss, size_t period_size, size_t period_count, uint32_t direction)
465 struct pcm *pcm = NULL;
466 struct pcm_config config;
468 AUDIO_RETURN_NULL_IF_FAIL(ss);
470 config.channels = ss->channels;
471 config.rate = ss->rate;
472 config.period_size = period_size;
473 config.period_count = period_count;
474 config.format = ss->format;
475 config.start_threshold = period_size;
476 config.stop_threshold = 0xFFFFFFFF;
477 config.silence_threshold = 0;
479 AUDIO_LOG_INFO("direction %d, channels %d, rate %d, format %d, period_size %d, period_count %d", direction, ss->channels, ss->rate, ss->format, period_size, period_count);
481 pcm = pcm_open((direction == AUDIO_DIRECTION_OUT) ? PLAYBACK_CARD_ID : CAPTURE_CARD_ID,
482 (direction == AUDIO_DIRECTION_OUT) ? PLAYBACK_PCM_DEVICE_ID : CAPTURE_PCM_DEVICE_ID,
483 (direction == AUDIO_DIRECTION_OUT) ? PCM_OUT : PCM_IN,
485 if (!pcm || !pcm_is_ready(pcm)) {
486 AUDIO_LOG_ERROR("Unable to open device (%s)", pcm_get_error(pcm));
495 audio_return_t audio_pcm_open(void *audio_handle, void **pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
497 #ifdef __USE_TINYALSA__
499 audio_pcm_sample_spec_t *ss;
502 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
503 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
504 AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER);
505 AUDIO_RETURN_VAL_IF_FAIL((period_size > 0), AUDIO_ERR_PARAMETER);
506 AUDIO_RETURN_VAL_IF_FAIL((periods > 0), AUDIO_ERR_PARAMETER);
508 ah = (audio_hal_t *)audio_handle;
509 ss = (audio_pcm_sample_spec_t *)sample_spec;
510 ss->format = _convert_format((audio_sample_format_t)ss->format);
512 *pcm_handle = __tinyalsa_open_device(ss, (size_t)period_size, (size_t)periods, direction);
513 if (*pcm_handle == NULL) {
514 AUDIO_LOG_ERROR("Error opening PCM device");
515 return AUDIO_ERR_RESOURCE;
518 if ((err = pcm_prepare((struct pcm *)*pcm_handle)) != 0) {
519 AUDIO_LOG_ERROR("Error prepare PCM device : %d", err);
522 ah->device.pcm_count++;
523 AUDIO_LOG_INFO("Opening PCM handle 0x%x", *pcm_handle);
527 char *device_name = NULL;
529 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
530 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
531 AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER);
532 AUDIO_RETURN_VAL_IF_FAIL((period_size > 0), AUDIO_ERR_PARAMETER);
533 AUDIO_RETURN_VAL_IF_FAIL((periods > 0), AUDIO_ERR_PARAMETER);
535 ah = (audio_hal_t *)audio_handle;
536 mode = SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT;
538 if(direction == AUDIO_DIRECTION_OUT)
539 device_name = PLAYBACK_PCM_DEVICE;
540 else if (direction == AUDIO_DIRECTION_IN)
541 device_name = CAPTURE_PCM_DEVICE;
543 AUDIO_LOG_ERROR("Error get device_name, direction : %d", direction);
544 return AUDIO_ERR_RESOURCE;
547 if ((err = snd_pcm_open((snd_pcm_t **)pcm_handle, device_name, (direction == AUDIO_DIRECTION_OUT) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, mode)) < 0) {
548 AUDIO_LOG_ERROR("Error opening PCM device %s : %s", device_name, snd_strerror(err));
549 return AUDIO_ERR_RESOURCE;
552 if ((err = audio_pcm_set_params(audio_handle, *pcm_handle, direction, sample_spec, period_size, periods)) != AUDIO_RET_OK) {
553 AUDIO_LOG_ERROR("Failed to set pcm parameters : %d", err);
557 ah->device.pcm_count++;
558 AUDIO_LOG_INFO("Opening PCM handle 0x%x, PCM device %s", *pcm_handle, device_name);
564 audio_return_t audio_pcm_start(void *audio_handle, void *pcm_handle)
568 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
569 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
571 #ifdef __USE_TINYALSA__
572 if ((err = pcm_start(pcm_handle)) < 0) {
573 AUDIO_LOG_ERROR("Error starting PCM handle : %d", err);
574 return AUDIO_ERR_RESOURCE;
577 if ((err = snd_pcm_start(pcm_handle)) < 0) {
578 AUDIO_LOG_ERROR("Error starting PCM handle : %s", snd_strerror(err));
579 return AUDIO_ERR_RESOURCE;
583 AUDIO_LOG_INFO("PCM handle 0x%x start", pcm_handle);
587 audio_return_t audio_pcm_stop(void *audio_handle, void *pcm_handle)
591 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
592 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
594 #ifdef __USE_TINYALSA__
595 if ((err = pcm_stop(pcm_handle)) < 0) {
596 AUDIO_LOG_ERROR("Error stopping PCM handle : %d", err);
597 return AUDIO_ERR_RESOURCE;
600 if ((err = snd_pcm_drop(pcm_handle)) < 0) {
601 AUDIO_LOG_ERROR("Error stopping PCM handle : %s", snd_strerror(err));
602 return AUDIO_ERR_RESOURCE;
606 AUDIO_LOG_INFO("PCM handle 0x%x stop", pcm_handle);
610 audio_return_t audio_pcm_close(void *audio_handle, void *pcm_handle)
612 audio_hal_t *ah = (audio_hal_t *)audio_handle;
615 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
616 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
618 AUDIO_LOG_INFO("Try to close PCM handle 0x%x", pcm_handle);
620 #ifdef __USE_TINYALSA__
621 if ((err = pcm_close(pcm_handle)) < 0) {
622 AUDIO_LOG_ERROR("Error closing PCM handle : %d", err);
623 return AUDIO_ERR_RESOURCE;
626 if ((err = snd_pcm_close(pcm_handle)) < 0) {
627 AUDIO_LOG_ERROR("Error closing PCM handle : %s", snd_strerror(err));
628 return AUDIO_ERR_RESOURCE;
633 ah->device.pcm_count--;
634 AUDIO_LOG_INFO("PCM handle close success (count:%d)", ah->device.pcm_count);
639 audio_return_t audio_pcm_avail(void *audio_handle, void *pcm_handle, uint32_t *avail)
641 #ifdef __USE_TINYALSA__
642 struct timespec tspec;
643 unsigned int frames_avail = 0;
646 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
647 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
648 AUDIO_RETURN_VAL_IF_FAIL(avail, AUDIO_ERR_PARAMETER);
650 err = pcm_get_htimestamp(pcm_handle, &frames_avail, &tspec);
652 AUDIO_LOG_ERROR("Could not get avail and timespec at PCM handle 0x%x : %d", pcm_handle, err);
653 return AUDIO_ERR_IOCTL;
657 AUDIO_LOG_DEBUG("avail = %d", frames_avail);
660 *avail = (uint32_t)frames_avail;
662 snd_pcm_sframes_t frames_avail;
664 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
665 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
666 AUDIO_RETURN_VAL_IF_FAIL(avail, AUDIO_ERR_PARAMETER);
668 if ((frames_avail = snd_pcm_avail(pcm_handle)) < 0) {
669 AUDIO_LOG_ERROR("Could not get avail at PCM handle 0x%x : %d", pcm_handle, frames_avail);
670 return AUDIO_ERR_IOCTL;
674 AUDIO_LOG_DEBUG("avail = %d", frames_avail);
677 *avail = (uint32_t)frames_avail;
683 audio_return_t audio_pcm_write(void *audio_handle, void *pcm_handle, const void *buffer, uint32_t frames)
685 #ifdef __USE_TINYALSA__
688 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
689 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
691 err = pcm_write(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
693 AUDIO_LOG_ERROR("Failed to write pcm : %d", err);
694 return AUDIO_ERR_IOCTL;
698 AUDIO_LOG_DEBUG("audio_pcm_write = %d", frames);
701 snd_pcm_sframes_t frames_written;
703 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
705 frames_written = snd_pcm_writei(pcm_handle, buffer, (snd_pcm_uframes_t) frames);
706 if (frames_written < 0) {
707 AUDIO_LOG_ERROR("Failed to write pcm : %d", frames_written);
708 return AUDIO_ERR_IOCTL;
712 AUDIO_LOG_DEBUG("audio_pcm_write = (%d / %d)", frames_written, frames);
719 audio_return_t audio_pcm_read(void *audio_handle, void *pcm_handle, void *buffer, uint32_t frames)
721 #ifdef __USE_TINYALSA__
724 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
725 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
727 err = pcm_read(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
729 AUDIO_LOG_ERROR("Failed to read pcm : %d", err);
730 return AUDIO_ERR_IOCTL;
734 AUDIO_LOG_DEBUG("audio_pcm_read = %d", frames);
737 snd_pcm_sframes_t frames_read;
739 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
740 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
742 frames_read = snd_pcm_readi(pcm_handle, buffer, (snd_pcm_uframes_t)frames);
743 if (frames_read < 0) {
744 AUDIO_LOG_ERROR("Failed to read pcm : %d", frames_read);
745 return AUDIO_ERR_IOCTL;
749 AUDIO_LOG_DEBUG("audio_pcm_read = (%d / %d)", frames_read, frames);
756 audio_return_t audio_pcm_get_fd(void *audio_handle, void *pcm_handle, int *fd)
758 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
759 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
760 AUDIO_RETURN_VAL_IF_FAIL(fd, AUDIO_ERR_PARAMETER);
761 /* we use an internal API of the (tiny)alsa library, so it causes warning message during compile */
762 #ifdef __USE_TINYALSA__
763 *fd = _pcm_poll_descriptor((struct pcm *)pcm_handle);
765 *fd = _snd_pcm_poll_descriptor((snd_pcm_t *)pcm_handle);
770 #ifdef __USE_TINYALSA__
771 static int __tinyalsa_pcm_recover(struct pcm *pcm, int err)
775 if (err == -EINTR) /* nothing to do, continue */
778 AUDIO_LOG_INFO("XRUN occurred");
779 err = pcm_prepare(pcm);
781 AUDIO_LOG_ERROR("Could not recover from XRUN occurred, prepare failed : %d", err);
786 if (err == -ESTRPIPE) {
787 /* tinyalsa does not support pcm resume, dont't care suspend case */
788 AUDIO_LOG_ERROR("Could not recover from suspend : %d", err);
795 audio_return_t audio_pcm_recover(void *audio_handle, void *pcm_handle, int revents)
799 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
800 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
802 if (revents & POLLERR)
803 AUDIO_LOG_DEBUG("Got POLLERR from ALSA");
804 if (revents & POLLNVAL)
805 AUDIO_LOG_DEBUG("Got POLLNVAL from ALSA");
806 if (revents & POLLHUP)
807 AUDIO_LOG_DEBUG("Got POLLHUP from ALSA");
808 if (revents & POLLPRI)
809 AUDIO_LOG_DEBUG("Got POLLPRI from ALSA");
810 if (revents & POLLIN)
811 AUDIO_LOG_DEBUG("Got POLLIN from ALSA");
812 if (revents & POLLOUT)
813 AUDIO_LOG_DEBUG("Got POLLOUT from ALSA");
815 #ifdef __USE_TINYALSA__
816 state = pcm_state(pcm_handle);
817 AUDIO_LOG_DEBUG("PCM state is %d", state);
821 if ((err = __tinyalsa_pcm_recover(pcm_handle, -EPIPE)) != 0) {
822 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
823 return AUDIO_ERR_IOCTL;
827 case PCM_STATE_SUSPENDED:
828 if ((err = __tinyalsa_pcm_recover(pcm_handle, -ESTRPIPE)) != 0) {
829 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
830 return AUDIO_ERR_IOCTL;
835 pcm_stop(pcm_handle);
836 if ((err = pcm_prepare(pcm_handle)) < 0) {
837 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with pcm_prepare() : %d", err);
838 return AUDIO_ERR_IOCTL;
842 state = snd_pcm_state(pcm_handle);
843 AUDIO_LOG_DEBUG("PCM state is %s", snd_pcm_state_name(state));
845 /* Try to recover from this error */
848 case SND_PCM_STATE_XRUN:
849 if ((err = snd_pcm_recover(pcm_handle, -EPIPE, 1)) != 0) {
850 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
851 return AUDIO_ERR_IOCTL;
855 case SND_PCM_STATE_SUSPENDED:
856 if ((err = snd_pcm_recover(pcm_handle, -ESTRPIPE, 1)) != 0) {
857 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
858 return AUDIO_ERR_IOCTL;
863 snd_pcm_drop(pcm_handle);
864 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
865 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare() : %d", err);
866 return AUDIO_ERR_IOCTL;
872 AUDIO_LOG_DEBUG("audio_pcm_recover");
876 audio_return_t audio_pcm_get_params(void *audio_handle, void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods)
878 #ifdef __USE_TINYALSA__
879 audio_pcm_sample_spec_t *ss;
880 unsigned int _period_size, _buffer_size, _periods, _format, _rate, _channels;
881 unsigned int _start_threshold, _stop_threshold, _silence_threshold;
882 struct pcm_config *config;
884 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
885 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
886 AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER);
887 AUDIO_RETURN_VAL_IF_FAIL(period_size, AUDIO_ERR_PARAMETER);
888 AUDIO_RETURN_VAL_IF_FAIL(periods, AUDIO_ERR_PARAMETER);
889 ss = (audio_pcm_sample_spec_t *)*sample_spec;
891 /* we use an internal API of the tiny alsa library, so it causes warning message during compile */
892 _pcm_config(pcm_handle, &config);
894 *period_size = config->period_size;
895 *periods = config->period_count;
896 _buffer_size = config->period_size * config->period_count;
897 ss->format = config->format;
898 ss->rate = config->rate;
899 ss->channels = config->channels;
900 _start_threshold = config->start_threshold;
901 _stop_threshold = config->stop_threshold;
902 _silence_threshold = config->silence_threshold;
904 AUDIO_LOG_DEBUG("audio_pcm_get_params (handle 0x%x, format %d, rate %d, channels %d, period_size %d, periods %d, buffer_size %d)", pcm_handle, config->format, config->rate, config->channels, config->period_size, config->period_count, _buffer_size);
907 audio_pcm_sample_spec_t *ss;
909 snd_pcm_uframes_t _period_size, _buffer_size;
910 snd_pcm_format_t _format;
911 unsigned int _rate, _channels;
912 snd_pcm_uframes_t _start_threshold, _stop_threshold, _silence_threshold, _avail_min;
913 unsigned int _periods;
914 snd_pcm_hw_params_t *hwparams;
915 snd_pcm_sw_params_t *swparams;
917 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
918 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
919 AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER);
920 AUDIO_RETURN_VAL_IF_FAIL(period_size, AUDIO_ERR_PARAMETER);
921 AUDIO_RETURN_VAL_IF_FAIL(periods, AUDIO_ERR_PARAMETER);
922 ss = (audio_pcm_sample_spec_t *)*sample_spec;
924 snd_pcm_hw_params_alloca(&hwparams);
925 snd_pcm_sw_params_alloca(&swparams);
927 if ((err = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) {
928 AUDIO_LOG_ERROR("snd_pcm_hw_params_current() failed : %d", err);
929 return AUDIO_ERR_PARAMETER;
932 if ((err = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
933 (err = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0 ||
934 (err = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0 ||
935 (err = snd_pcm_hw_params_get_format(hwparams, &_format)) < 0 ||
936 (err = snd_pcm_hw_params_get_rate(hwparams, &_rate, &dir)) < 0 ||
937 (err = snd_pcm_hw_params_get_channels(hwparams, &_channels)) < 0) {
938 AUDIO_LOG_ERROR("snd_pcm_hw_params_get_{period_size|buffer_size|periods|format|rate|channels}() failed : %s", err);
939 return AUDIO_ERR_PARAMETER;
942 *period_size = _period_size;
944 ss->format = _format;
946 ss->channels = _channels;
948 if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
949 AUDIO_LOG_ERROR("snd_pcm_sw_params_current() failed : %d", err);
950 return AUDIO_ERR_PARAMETER;
953 if ((err = snd_pcm_sw_params_get_start_threshold(swparams, &_start_threshold)) < 0 ||
954 (err = snd_pcm_sw_params_get_stop_threshold(swparams, &_stop_threshold)) < 0 ||
955 (err = snd_pcm_sw_params_get_silence_threshold(swparams, &_silence_threshold)) < 0 ||
956 (err = snd_pcm_sw_params_get_avail_min(swparams, &_avail_min)) < 0) {
957 AUDIO_LOG_ERROR("snd_pcm_sw_params_get_{start_threshold|stop_threshold|silence_threshold|avail_min}() failed : %s", err);
960 AUDIO_LOG_DEBUG("audio_pcm_get_params (handle 0x%x, format %d, rate %d, channels %d, period_size %d, periods %d, buffer_size %d)", pcm_handle, _format, _rate, _channels, _period_size, _periods, _buffer_size);
966 audio_return_t audio_pcm_set_params(void *audio_handle, void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
968 #ifdef __USE_TINYALSA__
969 /* Parameters are only acceptable in pcm_open() function */
970 AUDIO_LOG_DEBUG("audio_pcm_set_params");
973 audio_pcm_sample_spec_t ss;
974 snd_pcm_uframes_t _buffer_size;
975 snd_pcm_hw_params_t *hwparams;
976 snd_pcm_sw_params_t *swparams;
978 AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
979 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
980 AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER);
981 AUDIO_RETURN_VAL_IF_FAIL(period_size, AUDIO_ERR_PARAMETER);
982 AUDIO_RETURN_VAL_IF_FAIL(periods, AUDIO_ERR_PARAMETER);
983 ss = *(audio_pcm_sample_spec_t *)sample_spec;
985 snd_pcm_hw_params_alloca(&hwparams);
986 snd_pcm_sw_params_alloca(&swparams);
989 if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
990 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() failed : %d", err);
991 return AUDIO_ERR_PARAMETER;
994 if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) {
995 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate_resample() failed : %d", err);
996 return AUDIO_ERR_PARAMETER;
999 if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
1000 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() failed : %d", err);
1001 return AUDIO_ERR_PARAMETER;
1004 ss.format = _convert_format((audio_sample_format_t)ss.format);
1005 if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, ss.format)) < 0) {
1006 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_format() failed : %d", err);
1007 return AUDIO_ERR_PARAMETER;
1010 if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, ss.rate, 0)) < 0) {
1011 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() failed : %d", err);
1012 return AUDIO_ERR_PARAMETER;
1015 if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss.channels)) < 0) {
1016 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", err);
1017 return AUDIO_ERR_PARAMETER;
1020 if ((err = snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, period_size, 0)) < 0) {
1021 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size(%u) failed : %d", err);
1022 return AUDIO_ERR_PARAMETER;
1025 if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0)) < 0) {
1026 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods(%u) failed : %d", periods, err);
1027 return AUDIO_ERR_PARAMETER;
1030 _buffer_size = period_size * periods;
1031 if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, _buffer_size)) < 0) {
1032 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size(%u) failed : %d", periods * periods, err);
1033 return AUDIO_ERR_PARAMETER;
1036 if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
1037 AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err);
1038 return AUDIO_ERR_IOCTL;
1042 if ((err = snd_pcm_sw_params_current(pcm_handle, swparams) < 0)) {
1043 AUDIO_LOG_ERROR("Unable to determine current swparams : %d", err);
1044 return AUDIO_ERR_PARAMETER;
1047 if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
1048 AUDIO_LOG_ERROR("Unable to enable time stamping : %d", err);
1049 return AUDIO_ERR_PARAMETER;
1052 if ((err = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xFFFFFFFF)) < 0) {
1053 AUDIO_LOG_ERROR("Unable to set stop threshold : %d", err);
1054 return AUDIO_ERR_PARAMETER;
1057 if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, period_size / 2)) < 0) {
1058 AUDIO_LOG_ERROR("Unable to set start threshold : %d", err);
1059 return AUDIO_ERR_PARAMETER;
1062 if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, 1024)) < 0) {
1063 AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err);
1064 return AUDIO_ERR_PARAMETER;
1067 if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0) {
1068 AUDIO_LOG_ERROR("Unable to set sw params : %d", err);
1069 return AUDIO_ERR_IOCTL;
1072 /* Prepare device */
1073 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
1074 AUDIO_LOG_ERROR("snd_pcm_prepare() failed : %d", err);
1075 return AUDIO_ERR_IOCTL;
1078 AUDIO_LOG_DEBUG("audio_pcm_set_params (handle 0x%x, format %d, rate %d, channels %d, period_size %d, periods %d, buffer_size %d)", pcm_handle, ss.format, ss.rate, ss.channels, period_size, periods, _buffer_size);
1081 return AUDIO_RET_OK;