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.
28 #include "tizen-audio-internal.h"
29 #include "tizen-audio-impl.h"
31 #ifndef __USE_TINYALSA__
32 #define DEVICE_NAME_MAX 32
35 #ifdef __USE_TINYALSA__
36 /* Convert pcm format from pulse to alsa */
37 static const uint32_t g_format_convert_table[] = {
38 [AUDIO_SAMPLE_U8] = PCM_FORMAT_S8,
39 [AUDIO_SAMPLE_S16LE] = PCM_FORMAT_S16_LE,
40 [AUDIO_SAMPLE_S32LE] = PCM_FORMAT_S32_LE,
41 [AUDIO_SAMPLE_S24_32LE] = PCM_FORMAT_S24_LE
44 /* FIXME : To avoid build warning... */
45 int _snd_pcm_poll_descriptor(snd_pcm_t *pcm);
46 /* Convert pcm format from pulse to alsa */
47 static const uint32_t g_format_convert_table[] = {
48 [AUDIO_SAMPLE_U8] = SND_PCM_FORMAT_U8,
49 [AUDIO_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW,
50 [AUDIO_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW,
51 [AUDIO_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE,
52 [AUDIO_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
53 [AUDIO_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
54 [AUDIO_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
55 [AUDIO_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE,
56 [AUDIO_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
57 [AUDIO_SAMPLE_S24LE] = SND_PCM_FORMAT_S24_3LE,
58 [AUDIO_SAMPLE_S24BE] = SND_PCM_FORMAT_S24_3BE,
59 [AUDIO_SAMPLE_S24_32LE] = SND_PCM_FORMAT_S24_LE,
60 [AUDIO_SAMPLE_S24_32BE] = SND_PCM_FORMAT_S24_BE
64 static uint32_t __convert_format(audio_sample_format_s format)
66 return g_format_convert_table[format];
69 /* #define DEBUG_TIMING */
71 static int __voice_pcm_set_params(audio_hal_s *ah, snd_pcm_t *pcm)
73 snd_pcm_hw_params_t *params = NULL;
77 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
78 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
80 /* Skip parameter setting to null device. */
81 if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
82 return AUDIO_ERR_IOCTL;
84 /* Allocate a hardware parameters object. */
85 snd_pcm_hw_params_alloca(¶ms);
87 /* Fill it in with default values. */
88 if (snd_pcm_hw_params_any(pcm, params) < 0) {
89 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
93 /* Set the desired hardware parameters. */
94 /* Interleaved mode */
95 err = snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED);
97 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() : failed! - %s\n", snd_strerror(err));
100 err = snd_pcm_hw_params_set_rate(pcm, params, 8000, 0);
102 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
104 err = snd_pcm_hw_params(pcm, params);
106 AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
110 /* Dump current param */
111 snd_pcm_hw_params_get_access(params, (snd_pcm_access_t *) &val);
112 AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
114 snd_pcm_hw_params_get_format(params, (snd_pcm_format_t*)&val);
115 AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
116 snd_pcm_format_name((snd_pcm_format_t)val),
117 snd_pcm_format_description((snd_pcm_format_t)val));
119 snd_pcm_hw_params_get_subformat(params, (snd_pcm_subformat_t *)&val);
120 AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
121 snd_pcm_subformat_name((snd_pcm_subformat_t)val),
122 snd_pcm_subformat_description((snd_pcm_subformat_t)val));
124 snd_pcm_hw_params_get_channels(params, &val);
125 AUDIO_LOG_DEBUG("channels = %d\n", val);
133 #ifdef __USE_TINYALSA__
134 static int __parse_card_device_number(const char *card, const char *device, unsigned int *card_u, unsigned int *device_u) {
135 AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
136 AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER);
137 AUDIO_RETURN_VAL_IF_FAIL(card_u, AUDIO_ERR_PARAMETER);
138 AUDIO_RETURN_VAL_IF_FAIL(device_u, AUDIO_ERR_PARAMETER);
140 AUDIO_LOG_DEBUG("card : %s, device : %s", card, device);
142 *card_u = (unsigned int) strtol(card, NULL, 10);
143 *device_u = (unsigned int) strtol(device, NULL, 10);
148 static struct pcm *__tinyalsa_open_device(const char *card, const char *device, audio_pcm_sample_spec_s *ss, size_t period_size, size_t period_count, uint32_t direction)
150 struct pcm *pcm = NULL;
151 struct pcm_config config;
152 unsigned int card_u, device_u;
154 AUDIO_RETURN_NULL_IF_FAIL(device);
155 AUDIO_RETURN_NULL_IF_FAIL(ss);
157 config.channels = ss->channels;
158 config.rate = ss->rate;
159 config.period_size = period_size;
160 config.period_count = period_count;
161 config.format = ss->format;
162 config.start_threshold = period_size;
163 config.stop_threshold = 0xFFFFFFFF;
164 config.silence_threshold = 0;
166 AUDIO_LOG_INFO("card %s, device %s, direction %d, channels %d, rate %d, format %d, period_size %d, period_count %d",
167 card, device, direction, ss->channels, ss->rate, ss->format, period_size, period_count);
169 if (__parse_card_device_number(card, device, &card_u, &device_u) < 0) {
170 AUDIO_LOG_ERROR("Failed to get card device number from %s", device);
174 pcm = pcm_open(card_u, device_u, (direction == AUDIO_DIRECTION_OUT) ? PCM_OUT : PCM_IN, &config);
175 if (!pcm || !pcm_is_ready(pcm)) {
176 AUDIO_LOG_ERROR("Unable to open device (%s)", pcm_get_error(pcm));
184 static int __tinyalsa_pcm_recover(struct pcm *pcm, int err)
188 if (err == -EINTR) /* nothing to do, continue */
191 AUDIO_LOG_INFO("XRUN occurred");
192 err = pcm_prepare(pcm);
194 AUDIO_LOG_ERROR("Could not recover from XRUN occurred, prepare failed : %d", err);
199 if (err == -ESTRPIPE) {
200 /* tinyalsa does not support pcm resume, dont't care suspend case */
201 AUDIO_LOG_ERROR("Could not recover from suspend : %d", err);
208 audio_return_e _fmradio_pcm_open(audio_hal_s *ah)
210 audio_return_e audio_ret = AUDIO_RET_OK;
212 const char *device_name = NULL;
213 audio_pcm_sample_spec_s sample_spec;
214 uint8_t use_mmap = 0;
215 sample_spec.rate = 44100;
216 sample_spec.channels = 2;
217 sample_spec.format = SND_PCM_FORMAT_S16_LE;
219 if ((audio_ret = _ucm_get_device_name(ah, AUDIO_USE_CASE_VERB_FMRADIO, AUDIO_DIRECTION_OUT, &device_name)))
222 #ifdef __USE_TINYALSA__
223 AUDIO_LOG_WARN("need implementation for tinyAlsa");
224 return AUDIO_ERR_NOT_IMPLEMENTED;
227 if((ret = snd_pcm_open((snd_pcm_t **)&ah->device.fmradio_pcm_out, (char *)device_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
228 AUDIO_LOG_ERROR("[%s] out pcm_open failed", device_name);
229 audio_ret = AUDIO_ERR_IOCTL;
232 AUDIO_LOG_INFO("[%s] out pcm_open success:%p", device_name, ah->device.fmradio_pcm_out);
234 if ((audio_ret = _pcm_set_hw_params(ah->device.fmradio_pcm_out, &sample_spec, &use_mmap, NULL, NULL)) < 0) {
235 AUDIO_LOG_ERROR("[%s] out __set_pcm_hw_params failed", device_name);
236 if ((audio_ret = _pcm_close(ah->device.pcm_out)))
237 AUDIO_LOG_ERROR("failed to _pcm_close(), ret(0x%x)", audio_ret);
238 ah->device.fmradio_pcm_out = NULL;
246 free((void*)device_name);
251 audio_return_e _fmradio_pcm_close(audio_hal_s *ah)
253 audio_return_e audio_ret = AUDIO_RET_OK;
255 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
257 AUDIO_LOG_INFO("close fmradio pcm handles");
259 if (ah->device.fmradio_pcm_out) {
260 if ((audio_ret = _pcm_close(ah->device.fmradio_pcm_out)))
261 AUDIO_LOG_ERROR("failed to _fmradio_pcm_close() for pcm_out, ret(0x%x)", audio_ret);
263 ah->device.fmradio_pcm_out = NULL;
264 AUDIO_LOG_INFO("fmradio pcm_out handle close success");
271 audio_return_e _voice_pcm_open(audio_hal_s *ah)
275 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
277 #ifdef __USE_TINYALSA__
278 AUDIO_LOG_WARN("need implementation for tinyAlsa");
279 return AUDIO_ERR_NOT_IMPLEMENTED;
281 AUDIO_LOG_INFO("open voice pcm handles");
283 /* Get playback voice-pcm from ucm conf. Open and set-params */
284 if ((err = snd_pcm_open((snd_pcm_t **)&ah->device.pcm_out, VOICE_PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
285 AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %s", VOICE_PCM_DEVICE, snd_strerror(err));
286 return AUDIO_ERR_IOCTL;
288 ret = __voice_pcm_set_params(ah, ah->device.pcm_out);
290 AUDIO_LOG_INFO("pcm playback device open success device(%s)", VOICE_PCM_DEVICE);
292 /* Get capture voice-pcm from ucm conf. Open and set-params */
293 if ((err = snd_pcm_open((snd_pcm_t **)&ah->device.pcm_in, VOICE_PCM_DEVICE, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
294 AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %s", VOICE_PCM_DEVICE, snd_strerror(err));
295 return AUDIO_ERR_IOCTL;
297 ret = __voice_pcm_set_params(ah, ah->device.pcm_in);
298 AUDIO_LOG_INFO("pcm captures device open success device(%s)", VOICE_PCM_DEVICE);
301 return (ret == 0 ? AUDIO_RET_OK : AUDIO_ERR_INTERNAL);
304 audio_return_e _voice_pcm_close(audio_hal_s *ah, uint32_t direction)
306 audio_return_e audio_ret = AUDIO_RET_OK;
308 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
310 AUDIO_LOG_INFO("close voice pcm handles");
312 if (ah->device.pcm_out && (direction == AUDIO_DIRECTION_OUT)) {
313 if ((audio_ret = _pcm_close(ah->device.pcm_out)))
314 AUDIO_LOG_ERROR("failed to _pcm_close() for pcm_out, ret(0x%x)", audio_ret);
316 ah->device.pcm_out = NULL;
317 AUDIO_LOG_INFO("voice pcm_out handle close success");
319 } else if (ah->device.pcm_in && (direction == AUDIO_DIRECTION_IN)) {
320 if ((audio_ret = _pcm_close(ah->device.pcm_in)))
321 AUDIO_LOG_ERROR("failed to _pcm_close() for pcm_in, ret(0x%x)", audio_ret);
323 ah->device.pcm_in = NULL;
324 AUDIO_LOG_INFO("voice pcm_in handle close success");
331 audio_return_e _reset_pcm_devices(audio_hal_s *ah)
333 audio_return_e audio_ret = AUDIO_RET_OK;
335 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
337 if (ah->device.pcm_out) {
338 if (!(audio_ret = _pcm_close(ah->device.pcm_out))) {
339 ah->device.pcm_out = NULL;
340 AUDIO_LOG_INFO("pcm_out handle close success");
343 if (ah->device.pcm_in) {
344 if (!(audio_ret = _pcm_close(ah->device.pcm_in))) {
345 ah->device.pcm_in = NULL;
346 AUDIO_LOG_INFO("pcm_in handle close success");
349 if (ah->device.fmradio_pcm_out) {
350 if (!(audio_ret = _pcm_close(ah->device.fmradio_pcm_out))) {
351 ah->device.fmradio_pcm_out = NULL;
352 AUDIO_LOG_INFO("fmradio_pcm_out handle close success");
359 #ifndef __USE_TINYALSA__
360 static int __make_alsa_device_name(const char *card, const char *device, char device_name[])
362 AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
363 AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER);
364 AUDIO_RETURN_VAL_IF_FAIL(device_name, AUDIO_ERR_PARAMETER);
366 snprintf(device_name, DEVICE_NAME_MAX, "hw:%s,%s", card, device);
371 audio_return_e _pcm_open(const char *card, const char *device, uint32_t direction, void *sample_spec,
372 uint32_t period_size, uint32_t periods, void **pcm_handle)
376 AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
377 AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER);
378 AUDIO_RETURN_VAL_IF_FAIL((direction == AUDIO_DIRECTION_OUT) || (direction == AUDIO_DIRECTION_IN),
379 AUDIO_ERR_PARAMETER);
381 AUDIO_LOG_INFO("card(%s) device(%s) direction(%u) period_size(%u) periods(%u)",
382 card, device, direction, period_size, periods);
383 #ifdef __USE_TINYALSA__
384 audio_pcm_sample_spec_s *ss;
386 ss = (audio_pcm_sample_spec_s *)sample_spec;
387 ss->format = __convert_format((audio_sample_format_s)ss->format);
389 *pcm_handle = __tinyalsa_open_device(card, device, ss, (size_t)period_size, (size_t)periods, direction);
390 if (*pcm_handle == NULL) {
391 AUDIO_LOG_ERROR("Error opening PCM device");
392 return AUDIO_ERR_RESOURCE;
395 if ((err = pcm_prepare((struct pcm *)*pcm_handle)) != 0) {
396 AUDIO_LOG_ERROR("Error prepare PCM device : %d", err);
402 char device_name[DEVICE_NAME_MAX];
404 __make_alsa_device_name(card, device, device_name);
405 mode = SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT;
407 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) {
408 AUDIO_LOG_ERROR("Error opening PCM device %s : %s", device_name, snd_strerror(err));
409 return AUDIO_ERR_RESOURCE;
412 if ((ret = _pcm_set_params(*pcm_handle, direction, sample_spec, period_size, periods)) != AUDIO_RET_OK) {
413 AUDIO_LOG_ERROR("Failed to set pcm parameters : %d", ret);
417 AUDIO_LOG_INFO("PCM device %s", device_name);
423 audio_return_e _pcm_start(void *pcm_handle)
427 #ifdef __USE_TINYALSA__
428 if ((err = pcm_start(pcm_handle)) < 0) {
429 AUDIO_LOG_ERROR("Error starting PCM handle : %d", err);
430 return AUDIO_ERR_RESOURCE;
433 if ((err = snd_pcm_start(pcm_handle)) < 0) {
434 AUDIO_LOG_ERROR("Error starting PCM handle : %s", snd_strerror(err));
435 return AUDIO_ERR_RESOURCE;
439 AUDIO_LOG_INFO("PCM handle %p start", pcm_handle);
443 audio_return_e _pcm_stop(void *pcm_handle)
447 #ifdef __USE_TINYALSA__
448 if ((err = pcm_stop(pcm_handle)) < 0) {
449 AUDIO_LOG_ERROR("Error stopping PCM handle : %d", err);
450 return AUDIO_ERR_RESOURCE;
453 if ((err = snd_pcm_drop(pcm_handle)) < 0) {
454 AUDIO_LOG_ERROR("Error stopping PCM handle : %s", snd_strerror(err));
455 return AUDIO_ERR_RESOURCE;
459 AUDIO_LOG_INFO("PCM handle %p stop", pcm_handle);
463 audio_return_e _pcm_close(void *pcm_handle)
467 AUDIO_LOG_INFO("Try to close PCM handle %p", pcm_handle);
469 #ifdef __USE_TINYALSA__
470 if ((err = pcm_close(pcm_handle)) < 0) {
471 AUDIO_LOG_ERROR("Error closing PCM handle : %d", err);
472 return AUDIO_ERR_RESOURCE;
475 if ((err = snd_pcm_close(pcm_handle)) < 0) {
476 AUDIO_LOG_ERROR("Error closing PCM handle : %s", snd_strerror(err));
477 return AUDIO_ERR_RESOURCE;
484 audio_return_e _pcm_avail(void *pcm_handle, uint32_t *avail)
486 #ifdef __USE_TINYALSA__
487 struct timespec tspec;
488 unsigned int frames_avail = 0;
491 err = pcm_get_htimestamp(pcm_handle, &frames_avail, &tspec);
493 AUDIO_LOG_ERROR("Could not get avail and timespec at PCM handle %p : %d", pcm_handle, err);
494 return AUDIO_ERR_IOCTL;
498 AUDIO_LOG_DEBUG("avail = %d", frames_avail);
501 *avail = (uint32_t)frames_avail;
503 snd_pcm_sframes_t frames_avail;
505 if ((frames_avail = snd_pcm_avail(pcm_handle)) < 0) {
506 AUDIO_LOG_ERROR("Could not get avail at PCM handle %p : %ld", pcm_handle, frames_avail);
507 return AUDIO_ERR_IOCTL;
511 AUDIO_LOG_DEBUG("avail = %d", frames_avail);
514 *avail = (uint32_t)frames_avail;
520 audio_return_e _pcm_write(void *pcm_handle, const void *buffer, uint32_t frames)
522 #ifdef __USE_TINYALSA__
525 err = pcm_write(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
527 AUDIO_LOG_ERROR("Failed to write pcm : %d", err);
528 return AUDIO_ERR_IOCTL;
532 AUDIO_LOG_DEBUG("_pcm_write = %d", frames);
535 snd_pcm_sframes_t frames_written;
537 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
539 frames_written = snd_pcm_writei(pcm_handle, buffer, (snd_pcm_uframes_t) frames);
540 if (frames_written < 0) {
541 AUDIO_LOG_ERROR("Failed to write pcm : %ld", frames_written);
542 return AUDIO_ERR_IOCTL;
546 AUDIO_LOG_DEBUG("_pcm_write = (%d / %d)", frames_written, frames);
553 audio_return_e _pcm_read(void *pcm_handle, void *buffer, uint32_t frames)
555 #ifdef __USE_TINYALSA__
558 err = pcm_read(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
560 AUDIO_LOG_ERROR("Failed to read pcm : %d", err);
561 return AUDIO_ERR_IOCTL;
565 AUDIO_LOG_DEBUG("audio_pcm_read = %d", frames);
568 snd_pcm_sframes_t frames_read;
570 frames_read = snd_pcm_readi(pcm_handle, buffer, (snd_pcm_uframes_t)frames);
571 if (frames_read < 0) {
572 AUDIO_LOG_ERROR("Failed to read pcm : %ld", frames_read);
573 return AUDIO_ERR_IOCTL;
577 AUDIO_LOG_DEBUG("_pcm_read = (%d / %d)", frames_read, frames);
584 audio_return_e _pcm_get_fd(void *pcm_handle, int *fd)
586 /* we use an internal API of the (tiny)alsa library, so it causes warning message during compile */
587 #ifdef __USE_TINYALSA__
588 *fd = _pcm_poll_descriptor((struct pcm *)pcm_handle);
590 *fd = _snd_pcm_poll_descriptor((snd_pcm_t *)pcm_handle);
595 audio_return_e _pcm_recover(void *pcm_handle, int revents)
599 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
601 if (revents & POLLERR)
602 AUDIO_LOG_DEBUG("Got POLLERR from ALSA");
603 if (revents & POLLNVAL)
604 AUDIO_LOG_DEBUG("Got POLLNVAL from ALSA");
605 if (revents & POLLHUP)
606 AUDIO_LOG_DEBUG("Got POLLHUP from ALSA");
607 if (revents & POLLPRI)
608 AUDIO_LOG_DEBUG("Got POLLPRI from ALSA");
609 if (revents & POLLIN)
610 AUDIO_LOG_DEBUG("Got POLLIN from ALSA");
611 if (revents & POLLOUT)
612 AUDIO_LOG_DEBUG("Got POLLOUT from ALSA");
614 #ifdef __USE_TINYALSA__
615 state = pcm_state(pcm_handle);
616 AUDIO_LOG_DEBUG("PCM state is %d", state);
620 if ((err = __tinyalsa_pcm_recover(pcm_handle, -EPIPE)) != 0) {
621 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
622 return AUDIO_ERR_IOCTL;
626 case PCM_STATE_SUSPENDED:
627 if ((err = __tinyalsa_pcm_recover(pcm_handle, -ESTRPIPE)) != 0) {
628 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
629 return AUDIO_ERR_IOCTL;
634 pcm_stop(pcm_handle);
635 if ((err = pcm_prepare(pcm_handle)) < 0) {
636 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with pcm_prepare() : %d", err);
637 return AUDIO_ERR_IOCTL;
641 state = snd_pcm_state(pcm_handle);
642 AUDIO_LOG_DEBUG("PCM state is %s", snd_pcm_state_name(state));
644 /* Try to recover from this error */
647 case SND_PCM_STATE_XRUN:
648 if ((err = snd_pcm_recover(pcm_handle, -EPIPE, 1)) != 0) {
649 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
650 return AUDIO_ERR_IOCTL;
654 case SND_PCM_STATE_SUSPENDED:
655 if ((err = snd_pcm_recover(pcm_handle, -ESTRPIPE, 1)) != 0) {
656 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
657 return AUDIO_ERR_IOCTL;
662 snd_pcm_drop(pcm_handle);
663 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
664 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare() : %d", err);
665 return AUDIO_ERR_IOCTL;
671 AUDIO_LOG_DEBUG("_pcm_recover");
675 audio_return_e _pcm_get_params(void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods)
677 #ifdef __USE_TINYALSA__
678 audio_pcm_sample_spec_s *ss;
679 unsigned int _period_size, _buffer_size, _periods, _format, _rate, _channels;
680 unsigned int _start_threshold, _stop_threshold, _silence_threshold;
681 struct pcm_config *config;
683 ss = (audio_pcm_sample_spec_s *)*sample_spec;
685 /* we use an internal API of the tiny alsa library, so it causes warning message during compile */
686 _pcm_config(pcm_handle, &config);
688 *period_size = config->period_size;
689 *periods = config->period_count;
690 _buffer_size = config->period_size * config->period_count;
691 ss->format = config->format;
692 ss->rate = config->rate;
693 ss->channels = config->channels;
694 _start_threshold = config->start_threshold;
695 _stop_threshold = config->stop_threshold;
696 _silence_threshold = config->silence_threshold;
698 AUDIO_LOG_DEBUG("_pcm_get_params (handle %p, format %d, rate %u, channels %u, period_size %u, periods %u, buffer_size %u)",
699 pcm_handle, config->format, config->rate, config->channels, config->period_size, config->period_count, _buffer_size);
702 audio_pcm_sample_spec_s *ss;
704 snd_pcm_uframes_t _period_size, _buffer_size;
705 snd_pcm_format_t _format;
706 unsigned int _rate, _channels;
707 snd_pcm_uframes_t _start_threshold, _stop_threshold, _silence_threshold, _avail_min;
708 unsigned int _periods;
709 snd_pcm_hw_params_t *hwparams;
710 snd_pcm_sw_params_t *swparams;
712 ss = (audio_pcm_sample_spec_s *)*sample_spec;
714 snd_pcm_hw_params_alloca(&hwparams);
715 snd_pcm_sw_params_alloca(&swparams);
717 if ((err = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) {
718 AUDIO_LOG_ERROR("snd_pcm_hw_params_current() failed : %d", err);
719 return AUDIO_ERR_PARAMETER;
722 if ((err = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
723 (err = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0 ||
724 (err = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0 ||
725 (err = snd_pcm_hw_params_get_format(hwparams, &_format)) < 0 ||
726 (err = snd_pcm_hw_params_get_rate(hwparams, &_rate, &dir)) < 0 ||
727 (err = snd_pcm_hw_params_get_channels(hwparams, &_channels)) < 0) {
728 AUDIO_LOG_ERROR("snd_pcm_hw_params_get_{period_size|buffer_size|periods|format|rate|channels}() failed : %d", err);
729 return AUDIO_ERR_PARAMETER;
732 *period_size = _period_size;
734 ss->format = _format;
736 ss->channels = _channels;
738 if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
739 AUDIO_LOG_ERROR("snd_pcm_sw_params_current() failed : %d", err);
740 return AUDIO_ERR_PARAMETER;
743 if ((err = snd_pcm_sw_params_get_start_threshold(swparams, &_start_threshold)) < 0 ||
744 (err = snd_pcm_sw_params_get_stop_threshold(swparams, &_stop_threshold)) < 0 ||
745 (err = snd_pcm_sw_params_get_silence_threshold(swparams, &_silence_threshold)) < 0 ||
746 (err = snd_pcm_sw_params_get_avail_min(swparams, &_avail_min)) < 0) {
747 AUDIO_LOG_ERROR("snd_pcm_sw_params_get_{start_threshold|stop_threshold|silence_threshold|avail_min}() failed : %d", err);
750 AUDIO_LOG_DEBUG("_pcm_get_params (handle %p, format %d, rate %u, channels %u, period_size %lu, periods %u, buffer_size %lu)",
751 pcm_handle, _format, _rate, _channels, _period_size, _periods, _buffer_size);
757 audio_return_e _pcm_set_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
759 #ifdef __USE_TINYALSA__
760 /* Parameters are only acceptable in pcm_open() function */
761 AUDIO_LOG_DEBUG("_pcm_set_params");
764 audio_pcm_sample_spec_s ss;
765 snd_pcm_uframes_t _buffer_size;
766 snd_pcm_hw_params_t *hwparams;
767 snd_pcm_sw_params_t *swparams;
769 ss = *(audio_pcm_sample_spec_s *)sample_spec;
771 snd_pcm_hw_params_alloca(&hwparams);
772 snd_pcm_sw_params_alloca(&swparams);
775 if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
776 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() failed : %d", err);
777 return AUDIO_ERR_PARAMETER;
780 if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) {
781 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate_resample() failed : %d", err);
782 return AUDIO_ERR_PARAMETER;
785 if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
786 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() failed : %d", err);
787 return AUDIO_ERR_PARAMETER;
790 ss.format = __convert_format((audio_sample_format_s)ss.format);
791 if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, ss.format)) < 0) {
792 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_format() failed : %d", err);
793 return AUDIO_ERR_PARAMETER;
796 if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, ss.rate, 0)) < 0) {
797 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() failed : %d", err);
798 return AUDIO_ERR_PARAMETER;
801 if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss.channels)) < 0) {
802 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", ss.channels, err);
803 return AUDIO_ERR_PARAMETER;
806 if ((err = snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, period_size, 0)) < 0) {
807 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size(%u) failed : %d", period_size, err);
808 return AUDIO_ERR_PARAMETER;
811 if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0)) < 0) {
812 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods(%u) failed : %d", periods, err);
813 return AUDIO_ERR_PARAMETER;
816 _buffer_size = period_size * periods;
817 if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, _buffer_size)) < 0) {
818 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size(%lu) failed : %d", _buffer_size, err);
819 return AUDIO_ERR_PARAMETER;
822 if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
823 AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err);
824 return AUDIO_ERR_IOCTL;
828 if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
829 AUDIO_LOG_ERROR("Unable to determine current swparams : %d", err);
830 return AUDIO_ERR_PARAMETER;
833 if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
834 AUDIO_LOG_ERROR("Unable to enable time stamping : %d", err);
835 return AUDIO_ERR_PARAMETER;
838 if ((err = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xFFFFFFFF)) < 0) {
839 AUDIO_LOG_ERROR("Unable to set stop threshold : %d", err);
840 return AUDIO_ERR_PARAMETER;
843 if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, period_size / 2)) < 0) {
844 AUDIO_LOG_ERROR("Unable to set start threshold : %d", err);
845 return AUDIO_ERR_PARAMETER;
848 if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, 1024)) < 0) {
849 AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err);
850 return AUDIO_ERR_PARAMETER;
853 if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0) {
854 AUDIO_LOG_ERROR("Unable to set sw params : %d", err);
855 return AUDIO_ERR_IOCTL;
859 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
860 AUDIO_LOG_ERROR("snd_pcm_prepare() failed : %d", err);
861 return AUDIO_ERR_IOCTL;
864 AUDIO_LOG_DEBUG("_pcm_set_params (handle %p, format %d, rate %u, channels %u, period_size %u, periods %u, buffer_size %lu)",
865 pcm_handle, ss.format, ss.rate, ss.channels, period_size, periods, _buffer_size);
871 /* Generic snd pcm interface APIs */
872 audio_return_e _pcm_set_hw_params(snd_pcm_t *pcm, audio_pcm_sample_spec_s *sample_spec, uint8_t *use_mmap, snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *buffer_size)
874 audio_return_e ret = AUDIO_RET_OK;
875 snd_pcm_hw_params_t *hwparams;
878 unsigned int val = 0;
879 snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
880 snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0;
881 uint8_t _use_mmap = use_mmap && *use_mmap;
882 uint32_t channels = 0;
884 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
886 snd_pcm_hw_params_alloca(&hwparams);
888 /* Skip parameter setting to null device. */
889 if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
890 return AUDIO_ERR_IOCTL;
892 /* Allocate a hardware parameters object. */
893 snd_pcm_hw_params_alloca(&hwparams);
895 /* Fill it in with default values. */
896 if (snd_pcm_hw_params_any(pcm, hwparams) < 0) {
897 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
901 /* Set the desired hardware parameters. */
905 if (snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
907 /* mmap() didn't work, fall back to interleaved */
909 if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
910 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
917 } else if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
918 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
921 AUDIO_LOG_DEBUG("setting rate - %d", sample_spec->rate);
922 err = snd_pcm_hw_params_set_rate(pcm, hwparams, sample_spec->rate, 0);
924 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
927 err = snd_pcm_hw_params(pcm, hwparams);
929 AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
933 /* Dump current param */
935 if ((ret = snd_pcm_hw_params_current(pcm, hwparams)) < 0) {
936 AUDIO_LOG_INFO("snd_pcm_hw_params_current() failed: %s", snd_strerror(ret));
940 if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
941 (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) {
942 AUDIO_LOG_INFO("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", snd_strerror(ret));
946 snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val);
947 AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
949 snd_pcm_hw_params_get_format(hwparams, &sample_spec->format);
950 AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
951 snd_pcm_format_name((snd_pcm_format_t)sample_spec->format),
952 snd_pcm_format_description((snd_pcm_format_t)sample_spec->format));
954 snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val);
955 AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
956 snd_pcm_subformat_name((snd_pcm_subformat_t)val),
957 snd_pcm_subformat_description((snd_pcm_subformat_t)val));
959 snd_pcm_hw_params_get_channels(hwparams, &channels);
960 sample_spec->channels = (uint8_t)channels;
961 AUDIO_LOG_DEBUG("channels = %d\n", sample_spec->channels);
964 *buffer_size = _buffer_size;
967 *period_size = _period_size;
970 *use_mmap = _use_mmap;
975 return AUDIO_ERR_RESOURCE;
978 audio_return_e _pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event)
980 snd_pcm_sw_params_t *swparams;
981 snd_pcm_uframes_t boundary;
984 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
986 snd_pcm_sw_params_alloca(&swparams);
988 if ((err = snd_pcm_sw_params_current(pcm, swparams)) < 0) {
989 AUDIO_LOG_WARN("Unable to determine current swparams: %s\n", snd_strerror(err));
992 if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, period_event)) < 0) {
993 AUDIO_LOG_WARN("Unable to disable period event: %s\n", snd_strerror(err));
996 if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
997 AUDIO_LOG_WARN("Unable to enable time stamping: %s\n", snd_strerror(err));
1000 if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) {
1001 AUDIO_LOG_WARN("Unable to get boundary: %s\n", snd_strerror(err));
1004 if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) {
1005 AUDIO_LOG_WARN("Unable to set stop threshold: %s\n", snd_strerror(err));
1008 if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) avail_min)) < 0) {
1009 AUDIO_LOG_WARN("Unable to set start threshold: %s\n", snd_strerror(err));
1012 if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) {
1013 AUDIO_LOG_WARN("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err));
1016 if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) {
1017 AUDIO_LOG_WARN("Unable to set sw params: %s\n", snd_strerror(err));
1020 return AUDIO_RET_OK;