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 #ifndef __USE_TINYALSA__
33 #define DEVICE_NAME_MAX 32
36 #ifdef __USE_TINYALSA__
37 /* Convert pcm format from pulse to alsa */
38 static const uint32_t g_format_convert_table[] = {
39 [AUDIO_SAMPLE_U8] = PCM_FORMAT_S8,
40 [AUDIO_SAMPLE_S16LE] = PCM_FORMAT_S16_LE,
41 [AUDIO_SAMPLE_S32LE] = PCM_FORMAT_S32_LE,
42 [AUDIO_SAMPLE_S24_32LE] = PCM_FORMAT_S24_LE
45 /* FIXME : To avoid build warning... */
46 int _snd_pcm_poll_descriptor(snd_pcm_t *pcm);
47 /* Convert pcm format from pulse to alsa */
48 static const uint32_t g_format_convert_table[] = {
49 [AUDIO_SAMPLE_U8] = SND_PCM_FORMAT_U8,
50 [AUDIO_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW,
51 [AUDIO_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW,
52 [AUDIO_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE,
53 [AUDIO_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
54 [AUDIO_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
55 [AUDIO_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
56 [AUDIO_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE,
57 [AUDIO_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
58 [AUDIO_SAMPLE_S24LE] = SND_PCM_FORMAT_S24_3LE,
59 [AUDIO_SAMPLE_S24BE] = SND_PCM_FORMAT_S24_3BE,
60 [AUDIO_SAMPLE_S24_32LE] = SND_PCM_FORMAT_S24_LE,
61 [AUDIO_SAMPLE_S24_32BE] = SND_PCM_FORMAT_S24_BE
65 static uint32_t __convert_format(audio_sample_format_t format)
67 return g_format_convert_table[format];
70 /* #define DEBUG_TIMING */
72 static int __voice_pcm_set_params(audio_hal_t *ah, snd_pcm_t *pcm)
74 snd_pcm_hw_params_t *params = NULL;
78 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
79 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
81 /* Skip parameter setting to null device. */
82 if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
83 return AUDIO_ERR_IOCTL;
85 /* Allocate a hardware parameters object. */
86 snd_pcm_hw_params_alloca(¶ms);
88 /* Fill it in with default values. */
89 if (snd_pcm_hw_params_any(pcm, params) < 0) {
90 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
94 /* Set the desired hardware parameters. */
95 /* Interleaved mode */
96 err = snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED);
98 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() : failed! - %s\n", snd_strerror(err));
101 err = snd_pcm_hw_params_set_rate(pcm, params, 8000, 0);
103 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
105 err = snd_pcm_hw_params(pcm, params);
107 AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
111 /* Dump current param */
112 snd_pcm_hw_params_get_access(params, (snd_pcm_access_t *) &val);
113 AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
115 snd_pcm_hw_params_get_format(params, (snd_pcm_format_t*)&val);
116 AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
117 snd_pcm_format_name((snd_pcm_format_t)val),
118 snd_pcm_format_description((snd_pcm_format_t)val));
120 snd_pcm_hw_params_get_subformat(params, (snd_pcm_subformat_t *)&val);
121 AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
122 snd_pcm_subformat_name((snd_pcm_subformat_t)val),
123 snd_pcm_subformat_description((snd_pcm_subformat_t)val));
125 snd_pcm_hw_params_get_channels(params, &val);
126 AUDIO_LOG_DEBUG("channels = %d\n", val);
134 #ifdef __USE_TINYALSA__
135 static int __parse_card_device_number(const char *card, const char *device, unsigned int *card_u, unsigned int *device_u) {
136 AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
137 AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER);
138 AUDIO_RETURN_VAL_IF_FAIL(card_u, AUDIO_ERR_PARAMETER);
139 AUDIO_RETURN_VAL_IF_FAIL(device_u, AUDIO_ERR_PARAMETER);
141 AUDIO_LOG_DEBUG("card : %s, device : %s", card, device);
143 *card_u = (unsigned int) strtol(card, NULL, 10);
144 *device_u = (unsigned int) strtol(device, NULL, 10);
149 static struct pcm *__tinyalsa_open_device(const char *card, const char *device, audio_pcm_sample_spec_t *ss, size_t period_size, size_t period_count, uint32_t direction)
151 struct pcm *pcm = NULL;
152 struct pcm_config config;
153 unsigned int card_u, device_u;
155 AUDIO_RETURN_NULL_IF_FAIL(device);
156 AUDIO_RETURN_NULL_IF_FAIL(ss);
158 config.channels = ss->channels;
159 config.rate = ss->rate;
160 config.period_size = period_size;
161 config.period_count = period_count;
162 config.format = ss->format;
163 config.start_threshold = period_size;
164 config.stop_threshold = 0xFFFFFFFF;
165 config.silence_threshold = 0;
167 AUDIO_LOG_INFO("card %s, device %s, direction %d, channels %d, rate %d, format %d, period_size %d, period_count %d",
168 card, device, direction, ss->channels, ss->rate, ss->format, period_size, period_count);
170 if (__parse_card_device_number(card, device, &card_u, &device_u) < 0) {
171 AUDIO_LOG_ERROR("Failed to get card device number from %s", device);
175 pcm = pcm_open(card_u, device_u, (direction == AUDIO_DIRECTION_OUT) ? PCM_OUT : PCM_IN, &config);
176 if (!pcm || !pcm_is_ready(pcm)) {
177 AUDIO_LOG_ERROR("Unable to open device (%s)", pcm_get_error(pcm));
185 static int __tinyalsa_pcm_recover(struct pcm *pcm, int err)
189 if (err == -EINTR) /* nothing to do, continue */
192 AUDIO_LOG_INFO("XRUN occurred");
193 err = pcm_prepare(pcm);
195 AUDIO_LOG_ERROR("Could not recover from XRUN occurred, prepare failed : %d", err);
200 if (err == -ESTRPIPE) {
201 /* tinyalsa does not support pcm resume, dont't care suspend case */
202 AUDIO_LOG_ERROR("Could not recover from suspend : %d", err);
209 audio_return_t _fmradio_pcm_open(audio_hal_t *ah)
211 audio_return_t audio_ret = AUDIO_RET_OK;
213 const char *device_name = NULL;
214 audio_pcm_sample_spec_t sample_spec;
215 uint8_t use_mmap = 0;
216 sample_spec.rate = 44100;
217 sample_spec.channels = 2;
218 sample_spec.format = SND_PCM_FORMAT_S16_LE;
220 if ((audio_ret = _ucm_get_device_name(ah, AUDIO_USE_CASE_VERB_FMRADIO, AUDIO_DIRECTION_OUT, &device_name)))
223 #ifdef __USE_TINYALSA__
224 AUDIO_LOG_WARN("need implementation for tinyAlsa");
225 return AUDIO_ERR_NOT_IMPLEMENTED;
228 if((ret = snd_pcm_open((snd_pcm_t **)&ah->device.fmradio_pcm_out, (char *)device_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
229 AUDIO_LOG_ERROR("[%s] out pcm_open failed", device_name);
230 audio_ret = AUDIO_ERR_IOCTL;
233 AUDIO_LOG_INFO("[%s] out pcm_open success:%p", device_name, ah->device.fmradio_pcm_out);
235 if ((audio_ret = _pcm_set_hw_params(ah->device.fmradio_pcm_out, &sample_spec, &use_mmap, NULL, NULL)) < 0) {
236 AUDIO_LOG_ERROR("[%s] out __set_pcm_hw_params failed", device_name);
237 if ((audio_ret = _pcm_close(ah->device.pcm_out)))
238 AUDIO_LOG_ERROR("failed to _pcm_close(), ret(0x%x)", audio_ret);
239 ah->device.fmradio_pcm_out = NULL;
247 free((void*)device_name);
252 audio_return_t _fmradio_pcm_close(audio_hal_t *ah)
254 audio_return_t audio_ret = AUDIO_RET_OK;
256 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
258 AUDIO_LOG_INFO("close fmradio pcm handles");
260 if (ah->device.fmradio_pcm_out) {
261 if ((audio_ret = _pcm_close(ah->device.fmradio_pcm_out)))
262 AUDIO_LOG_ERROR("failed to _fmradio_pcm_close() for pcm_out, ret(0x%x)", audio_ret);
264 ah->device.fmradio_pcm_out = NULL;
265 AUDIO_LOG_INFO("fmradio pcm_out handle close success");
272 audio_return_t _voice_pcm_open(audio_hal_t *ah)
276 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
278 #ifdef __USE_TINYALSA__
279 AUDIO_LOG_WARN("need implementation for tinyAlsa");
280 return AUDIO_ERR_NOT_IMPLEMENTED;
282 AUDIO_LOG_INFO("open voice pcm handles");
284 /* Get playback voice-pcm from ucm conf. Open and set-params */
285 if ((err = snd_pcm_open((snd_pcm_t **)&ah->device.pcm_out, VOICE_PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
286 AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %s", VOICE_PCM_DEVICE, snd_strerror(err));
287 return AUDIO_ERR_IOCTL;
289 ret = __voice_pcm_set_params(ah, ah->device.pcm_out);
291 AUDIO_LOG_INFO("pcm playback device open success device(%s)", VOICE_PCM_DEVICE);
293 /* Get capture voice-pcm from ucm conf. Open and set-params */
294 if ((err = snd_pcm_open((snd_pcm_t **)&ah->device.pcm_in, VOICE_PCM_DEVICE, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
295 AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %s", VOICE_PCM_DEVICE, snd_strerror(err));
296 return AUDIO_ERR_IOCTL;
298 ret = __voice_pcm_set_params(ah, ah->device.pcm_in);
299 AUDIO_LOG_INFO("pcm captures device open success device(%s)", VOICE_PCM_DEVICE);
302 return (ret == 0 ? AUDIO_RET_OK : AUDIO_ERR_INTERNAL);
305 audio_return_t _voice_pcm_close(audio_hal_t *ah, uint32_t direction)
307 audio_return_t audio_ret = AUDIO_RET_OK;
309 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
311 AUDIO_LOG_INFO("close voice pcm handles");
313 if (ah->device.pcm_out && (direction == AUDIO_DIRECTION_OUT)) {
314 if ((audio_ret = _pcm_close(ah->device.pcm_out)))
315 AUDIO_LOG_ERROR("failed to _pcm_close() for pcm_out, ret(0x%x)", audio_ret);
317 ah->device.pcm_out = NULL;
318 AUDIO_LOG_INFO("voice pcm_out handle close success");
320 } else if (ah->device.pcm_in && (direction == AUDIO_DIRECTION_IN)) {
321 if ((audio_ret = _pcm_close(ah->device.pcm_in)))
322 AUDIO_LOG_ERROR("failed to _pcm_close() for pcm_in, ret(0x%x)", audio_ret);
324 ah->device.pcm_in = NULL;
325 AUDIO_LOG_INFO("voice pcm_in handle close success");
332 audio_return_t _reset_pcm_devices(audio_hal_t *ah)
334 audio_return_t audio_ret = AUDIO_RET_OK;
336 AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
338 if (ah->device.pcm_out) {
339 if (!(audio_ret = _pcm_close(ah->device.pcm_out))) {
340 ah->device.pcm_out = NULL;
341 AUDIO_LOG_INFO("pcm_out handle close success");
344 if (ah->device.pcm_in) {
345 if (!(audio_ret = _pcm_close(ah->device.pcm_in))) {
346 ah->device.pcm_in = NULL;
347 AUDIO_LOG_INFO("pcm_in handle close success");
350 if (ah->device.fmradio_pcm_out) {
351 if (!(audio_ret = _pcm_close(ah->device.fmradio_pcm_out))) {
352 ah->device.fmradio_pcm_out = NULL;
353 AUDIO_LOG_INFO("fmradio_pcm_out handle close success");
360 #ifndef __USE_TINYALSA__
361 static int __make_alsa_device_name(const char *card, const char *device, char device_name[])
363 AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
364 AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER);
365 AUDIO_RETURN_VAL_IF_FAIL(device_name, AUDIO_ERR_PARAMETER);
367 snprintf(device_name, DEVICE_NAME_MAX, "hw:%s,%s", card, device);
372 audio_return_t _pcm_open(const char *card, const char *device, uint32_t direction, void *sample_spec,
373 uint32_t period_size, uint32_t periods, void **pcm_handle)
377 AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
378 AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER);
379 AUDIO_RETURN_VAL_IF_FAIL((direction == AUDIO_DIRECTION_OUT) || (direction == AUDIO_DIRECTION_IN),
380 AUDIO_ERR_PARAMETER);
382 AUDIO_LOG_INFO("card(%s) device(%s) direction(%u) period_size(%u) periods(%u)",
383 card, device, direction, period_size, periods);
384 #ifdef __USE_TINYALSA__
385 audio_pcm_sample_spec_t *ss;
387 ss = (audio_pcm_sample_spec_t *)sample_spec;
388 ss->format = __convert_format((audio_sample_format_t)ss->format);
390 *pcm_handle = __tinyalsa_open_device(card, device, ss, (size_t)period_size, (size_t)periods, direction);
391 if (*pcm_handle == NULL) {
392 AUDIO_LOG_ERROR("Error opening PCM device");
393 return AUDIO_ERR_RESOURCE;
396 if ((err = pcm_prepare((struct pcm *)*pcm_handle)) != 0) {
397 AUDIO_LOG_ERROR("Error prepare PCM device : %d", err);
403 char device_name[DEVICE_NAME_MAX];
405 __make_alsa_device_name(card, device, device_name);
406 mode = SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT;
408 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) {
409 AUDIO_LOG_ERROR("Error opening PCM device %s : %s", device_name, snd_strerror(err));
410 return AUDIO_ERR_RESOURCE;
413 if ((ret = _pcm_set_params(*pcm_handle, direction, sample_spec, period_size, periods)) != AUDIO_RET_OK) {
414 AUDIO_LOG_ERROR("Failed to set pcm parameters : %d", ret);
418 AUDIO_LOG_INFO("PCM device %s", device_name);
424 audio_return_t _pcm_start(void *pcm_handle)
428 #ifdef __USE_TINYALSA__
429 if ((err = pcm_start(pcm_handle)) < 0) {
430 AUDIO_LOG_ERROR("Error starting PCM handle : %d", err);
431 return AUDIO_ERR_RESOURCE;
434 if ((err = snd_pcm_start(pcm_handle)) < 0) {
435 AUDIO_LOG_ERROR("Error starting PCM handle : %s", snd_strerror(err));
436 return AUDIO_ERR_RESOURCE;
440 AUDIO_LOG_INFO("PCM handle %p start", pcm_handle);
444 audio_return_t _pcm_stop(void *pcm_handle)
448 #ifdef __USE_TINYALSA__
449 if ((err = pcm_stop(pcm_handle)) < 0) {
450 AUDIO_LOG_ERROR("Error stopping PCM handle : %d", err);
451 return AUDIO_ERR_RESOURCE;
454 if ((err = snd_pcm_drop(pcm_handle)) < 0) {
455 AUDIO_LOG_ERROR("Error stopping PCM handle : %s", snd_strerror(err));
456 return AUDIO_ERR_RESOURCE;
460 AUDIO_LOG_INFO("PCM handle %p stop", pcm_handle);
464 audio_return_t _pcm_close(void *pcm_handle)
468 AUDIO_LOG_INFO("Try to close PCM handle %p", pcm_handle);
470 #ifdef __USE_TINYALSA__
471 if ((err = pcm_close(pcm_handle)) < 0) {
472 AUDIO_LOG_ERROR("Error closing PCM handle : %d", err);
473 return AUDIO_ERR_RESOURCE;
476 if ((err = snd_pcm_close(pcm_handle)) < 0) {
477 AUDIO_LOG_ERROR("Error closing PCM handle : %s", snd_strerror(err));
478 return AUDIO_ERR_RESOURCE;
485 audio_return_t _pcm_avail(void *pcm_handle, uint32_t *avail)
487 #ifdef __USE_TINYALSA__
488 struct timespec tspec;
489 unsigned int frames_avail = 0;
492 err = pcm_get_htimestamp(pcm_handle, &frames_avail, &tspec);
494 AUDIO_LOG_ERROR("Could not get avail and timespec at PCM handle %p : %d", pcm_handle, err);
495 return AUDIO_ERR_IOCTL;
499 AUDIO_LOG_DEBUG("avail = %d", frames_avail);
502 *avail = (uint32_t)frames_avail;
504 snd_pcm_sframes_t frames_avail;
506 if ((frames_avail = snd_pcm_avail(pcm_handle)) < 0) {
507 AUDIO_LOG_ERROR("Could not get avail at PCM handle %p : %ld", pcm_handle, frames_avail);
508 return AUDIO_ERR_IOCTL;
512 AUDIO_LOG_DEBUG("avail = %d", frames_avail);
515 *avail = (uint32_t)frames_avail;
521 audio_return_t _pcm_write(void *pcm_handle, const void *buffer, uint32_t frames)
523 #ifdef __USE_TINYALSA__
526 err = pcm_write(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
528 AUDIO_LOG_ERROR("Failed to write pcm : %d", err);
529 return AUDIO_ERR_IOCTL;
533 AUDIO_LOG_DEBUG("_pcm_write = %d", frames);
536 snd_pcm_sframes_t frames_written;
538 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
540 frames_written = snd_pcm_writei(pcm_handle, buffer, (snd_pcm_uframes_t) frames);
541 if (frames_written < 0) {
542 AUDIO_LOG_ERROR("Failed to write pcm : %ld", frames_written);
543 return AUDIO_ERR_IOCTL;
547 AUDIO_LOG_DEBUG("_pcm_write = (%d / %d)", frames_written, frames);
554 audio_return_t _pcm_read(void *pcm_handle, void *buffer, uint32_t frames)
556 #ifdef __USE_TINYALSA__
559 err = pcm_read(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
561 AUDIO_LOG_ERROR("Failed to read pcm : %d", err);
562 return AUDIO_ERR_IOCTL;
566 AUDIO_LOG_DEBUG("audio_pcm_read = %d", frames);
569 snd_pcm_sframes_t frames_read;
571 frames_read = snd_pcm_readi(pcm_handle, buffer, (snd_pcm_uframes_t)frames);
572 if (frames_read < 0) {
573 AUDIO_LOG_ERROR("Failed to read pcm : %ld", frames_read);
574 return AUDIO_ERR_IOCTL;
578 AUDIO_LOG_DEBUG("_pcm_read = (%d / %d)", frames_read, frames);
585 audio_return_t _pcm_get_fd(void *pcm_handle, int *fd)
587 /* we use an internal API of the (tiny)alsa library, so it causes warning message during compile */
588 #ifdef __USE_TINYALSA__
589 *fd = _pcm_poll_descriptor((struct pcm *)pcm_handle);
591 *fd = _snd_pcm_poll_descriptor((snd_pcm_t *)pcm_handle);
596 audio_return_t _pcm_recover(void *pcm_handle, int revents)
600 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
602 if (revents & POLLERR)
603 AUDIO_LOG_DEBUG("Got POLLERR from ALSA");
604 if (revents & POLLNVAL)
605 AUDIO_LOG_DEBUG("Got POLLNVAL from ALSA");
606 if (revents & POLLHUP)
607 AUDIO_LOG_DEBUG("Got POLLHUP from ALSA");
608 if (revents & POLLPRI)
609 AUDIO_LOG_DEBUG("Got POLLPRI from ALSA");
610 if (revents & POLLIN)
611 AUDIO_LOG_DEBUG("Got POLLIN from ALSA");
612 if (revents & POLLOUT)
613 AUDIO_LOG_DEBUG("Got POLLOUT from ALSA");
615 #ifdef __USE_TINYALSA__
616 state = pcm_state(pcm_handle);
617 AUDIO_LOG_DEBUG("PCM state is %d", state);
621 if ((err = __tinyalsa_pcm_recover(pcm_handle, -EPIPE)) != 0) {
622 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
623 return AUDIO_ERR_IOCTL;
627 case PCM_STATE_SUSPENDED:
628 if ((err = __tinyalsa_pcm_recover(pcm_handle, -ESTRPIPE)) != 0) {
629 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
630 return AUDIO_ERR_IOCTL;
635 pcm_stop(pcm_handle);
636 if ((err = pcm_prepare(pcm_handle)) < 0) {
637 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with pcm_prepare() : %d", err);
638 return AUDIO_ERR_IOCTL;
642 state = snd_pcm_state(pcm_handle);
643 AUDIO_LOG_DEBUG("PCM state is %s", snd_pcm_state_name(state));
645 /* Try to recover from this error */
648 case SND_PCM_STATE_XRUN:
649 if ((err = snd_pcm_recover(pcm_handle, -EPIPE, 1)) != 0) {
650 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
651 return AUDIO_ERR_IOCTL;
655 case SND_PCM_STATE_SUSPENDED:
656 if ((err = snd_pcm_recover(pcm_handle, -ESTRPIPE, 1)) != 0) {
657 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
658 return AUDIO_ERR_IOCTL;
663 snd_pcm_drop(pcm_handle);
664 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
665 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare() : %d", err);
666 return AUDIO_ERR_IOCTL;
672 AUDIO_LOG_DEBUG("_pcm_recover");
676 audio_return_t _pcm_get_params(void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods)
678 #ifdef __USE_TINYALSA__
679 audio_pcm_sample_spec_t *ss;
680 unsigned int _period_size, _buffer_size, _periods, _format, _rate, _channels;
681 unsigned int _start_threshold, _stop_threshold, _silence_threshold;
682 struct pcm_config *config;
684 ss = (audio_pcm_sample_spec_t *)*sample_spec;
686 /* we use an internal API of the tiny alsa library, so it causes warning message during compile */
687 _pcm_config(pcm_handle, &config);
689 *period_size = config->period_size;
690 *periods = config->period_count;
691 _buffer_size = config->period_size * config->period_count;
692 ss->format = config->format;
693 ss->rate = config->rate;
694 ss->channels = config->channels;
695 _start_threshold = config->start_threshold;
696 _stop_threshold = config->stop_threshold;
697 _silence_threshold = config->silence_threshold;
699 AUDIO_LOG_DEBUG("_pcm_get_params (handle %p, format %d, rate %u, channels %u, period_size %u, periods %u, buffer_size %u)",
700 pcm_handle, config->format, config->rate, config->channels, config->period_size, config->period_count, _buffer_size);
703 audio_pcm_sample_spec_t *ss;
705 snd_pcm_uframes_t _period_size, _buffer_size;
706 snd_pcm_format_t _format;
707 unsigned int _rate, _channels;
708 snd_pcm_uframes_t _start_threshold, _stop_threshold, _silence_threshold, _avail_min;
709 unsigned int _periods;
710 snd_pcm_hw_params_t *hwparams;
711 snd_pcm_sw_params_t *swparams;
713 ss = (audio_pcm_sample_spec_t *)*sample_spec;
715 snd_pcm_hw_params_alloca(&hwparams);
716 snd_pcm_sw_params_alloca(&swparams);
718 if ((err = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) {
719 AUDIO_LOG_ERROR("snd_pcm_hw_params_current() failed : %d", err);
720 return AUDIO_ERR_PARAMETER;
723 if ((err = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
724 (err = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0 ||
725 (err = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0 ||
726 (err = snd_pcm_hw_params_get_format(hwparams, &_format)) < 0 ||
727 (err = snd_pcm_hw_params_get_rate(hwparams, &_rate, &dir)) < 0 ||
728 (err = snd_pcm_hw_params_get_channels(hwparams, &_channels)) < 0) {
729 AUDIO_LOG_ERROR("snd_pcm_hw_params_get_{period_size|buffer_size|periods|format|rate|channels}() failed : %d", err);
730 return AUDIO_ERR_PARAMETER;
733 *period_size = _period_size;
735 ss->format = _format;
737 ss->channels = _channels;
739 if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
740 AUDIO_LOG_ERROR("snd_pcm_sw_params_current() failed : %d", err);
741 return AUDIO_ERR_PARAMETER;
744 if ((err = snd_pcm_sw_params_get_start_threshold(swparams, &_start_threshold)) < 0 ||
745 (err = snd_pcm_sw_params_get_stop_threshold(swparams, &_stop_threshold)) < 0 ||
746 (err = snd_pcm_sw_params_get_silence_threshold(swparams, &_silence_threshold)) < 0 ||
747 (err = snd_pcm_sw_params_get_avail_min(swparams, &_avail_min)) < 0) {
748 AUDIO_LOG_ERROR("snd_pcm_sw_params_get_{start_threshold|stop_threshold|silence_threshold|avail_min}() failed : %d", err);
751 AUDIO_LOG_DEBUG("_pcm_get_params (handle %p, format %d, rate %u, channels %u, period_size %lu, periods %u, buffer_size %lu)",
752 pcm_handle, _format, _rate, _channels, _period_size, _periods, _buffer_size);
758 audio_return_t _pcm_set_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
760 #ifdef __USE_TINYALSA__
761 /* Parameters are only acceptable in pcm_open() function */
762 AUDIO_LOG_DEBUG("_pcm_set_params");
765 audio_pcm_sample_spec_t ss;
766 snd_pcm_uframes_t _buffer_size;
767 snd_pcm_hw_params_t *hwparams;
768 snd_pcm_sw_params_t *swparams;
770 ss = *(audio_pcm_sample_spec_t *)sample_spec;
772 snd_pcm_hw_params_alloca(&hwparams);
773 snd_pcm_sw_params_alloca(&swparams);
776 if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
777 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() failed : %d", err);
778 return AUDIO_ERR_PARAMETER;
781 if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) {
782 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate_resample() failed : %d", err);
783 return AUDIO_ERR_PARAMETER;
786 if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
787 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() failed : %d", err);
788 return AUDIO_ERR_PARAMETER;
791 ss.format = __convert_format((audio_sample_format_t)ss.format);
792 if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, ss.format)) < 0) {
793 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_format() failed : %d", err);
794 return AUDIO_ERR_PARAMETER;
797 if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, ss.rate, 0)) < 0) {
798 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() failed : %d", err);
799 return AUDIO_ERR_PARAMETER;
802 if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss.channels)) < 0) {
803 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", ss.channels, err);
804 return AUDIO_ERR_PARAMETER;
807 if ((err = snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, period_size, 0)) < 0) {
808 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size(%u) failed : %d", period_size, err);
809 return AUDIO_ERR_PARAMETER;
812 if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0)) < 0) {
813 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods(%u) failed : %d", periods, err);
814 return AUDIO_ERR_PARAMETER;
817 _buffer_size = period_size * periods;
818 if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, _buffer_size)) < 0) {
819 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size(%lu) failed : %d", _buffer_size, err);
820 return AUDIO_ERR_PARAMETER;
823 if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
824 AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err);
825 return AUDIO_ERR_IOCTL;
829 if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
830 AUDIO_LOG_ERROR("Unable to determine current swparams : %d", err);
831 return AUDIO_ERR_PARAMETER;
834 if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
835 AUDIO_LOG_ERROR("Unable to enable time stamping : %d", err);
836 return AUDIO_ERR_PARAMETER;
839 if ((err = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xFFFFFFFF)) < 0) {
840 AUDIO_LOG_ERROR("Unable to set stop threshold : %d", err);
841 return AUDIO_ERR_PARAMETER;
844 if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, period_size / 2)) < 0) {
845 AUDIO_LOG_ERROR("Unable to set start threshold : %d", err);
846 return AUDIO_ERR_PARAMETER;
849 if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, 1024)) < 0) {
850 AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err);
851 return AUDIO_ERR_PARAMETER;
854 if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0) {
855 AUDIO_LOG_ERROR("Unable to set sw params : %d", err);
856 return AUDIO_ERR_IOCTL;
860 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
861 AUDIO_LOG_ERROR("snd_pcm_prepare() failed : %d", err);
862 return AUDIO_ERR_IOCTL;
865 AUDIO_LOG_DEBUG("_pcm_set_params (handle %p, format %d, rate %u, channels %u, period_size %u, periods %u, buffer_size %lu)",
866 pcm_handle, ss.format, ss.rate, ss.channels, period_size, periods, _buffer_size);
872 /* Generic snd pcm interface APIs */
873 audio_return_t _pcm_set_hw_params(snd_pcm_t *pcm, audio_pcm_sample_spec_t *sample_spec, uint8_t *use_mmap, snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *buffer_size)
875 audio_return_t ret = AUDIO_RET_OK;
876 snd_pcm_hw_params_t *hwparams;
879 unsigned int val = 0;
880 snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
881 snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0;
882 uint8_t _use_mmap = use_mmap && *use_mmap;
883 uint32_t channels = 0;
885 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
887 snd_pcm_hw_params_alloca(&hwparams);
889 /* Skip parameter setting to null device. */
890 if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
891 return AUDIO_ERR_IOCTL;
893 /* Allocate a hardware parameters object. */
894 snd_pcm_hw_params_alloca(&hwparams);
896 /* Fill it in with default values. */
897 if (snd_pcm_hw_params_any(pcm, hwparams) < 0) {
898 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
902 /* Set the desired hardware parameters. */
906 if (snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
908 /* mmap() didn't work, fall back to interleaved */
910 if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
911 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
918 } else if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
919 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
922 AUDIO_LOG_DEBUG("setting rate - %d", sample_spec->rate);
923 err = snd_pcm_hw_params_set_rate(pcm, hwparams, sample_spec->rate, 0);
925 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
928 err = snd_pcm_hw_params(pcm, hwparams);
930 AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
934 /* Dump current param */
936 if ((ret = snd_pcm_hw_params_current(pcm, hwparams)) < 0) {
937 AUDIO_LOG_INFO("snd_pcm_hw_params_current() failed: %s", snd_strerror(ret));
941 if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
942 (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) {
943 AUDIO_LOG_INFO("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", snd_strerror(ret));
947 snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val);
948 AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
950 snd_pcm_hw_params_get_format(hwparams, &sample_spec->format);
951 AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
952 snd_pcm_format_name((snd_pcm_format_t)sample_spec->format),
953 snd_pcm_format_description((snd_pcm_format_t)sample_spec->format));
955 snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val);
956 AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
957 snd_pcm_subformat_name((snd_pcm_subformat_t)val),
958 snd_pcm_subformat_description((snd_pcm_subformat_t)val));
960 snd_pcm_hw_params_get_channels(hwparams, &channels);
961 sample_spec->channels = (uint8_t)channels;
962 AUDIO_LOG_DEBUG("channels = %d\n", sample_spec->channels);
965 *buffer_size = _buffer_size;
968 *period_size = _period_size;
971 *use_mmap = _use_mmap;
976 return AUDIO_ERR_RESOURCE;
979 audio_return_t _pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event)
981 snd_pcm_sw_params_t *swparams;
982 snd_pcm_uframes_t boundary;
985 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
987 snd_pcm_sw_params_alloca(&swparams);
989 if ((err = snd_pcm_sw_params_current(pcm, swparams)) < 0) {
990 AUDIO_LOG_WARN("Unable to determine current swparams: %s\n", snd_strerror(err));
993 if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, period_event)) < 0) {
994 AUDIO_LOG_WARN("Unable to disable period event: %s\n", snd_strerror(err));
997 if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
998 AUDIO_LOG_WARN("Unable to enable time stamping: %s\n", snd_strerror(err));
1001 if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) {
1002 AUDIO_LOG_WARN("Unable to get boundary: %s\n", snd_strerror(err));
1005 if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) {
1006 AUDIO_LOG_WARN("Unable to set stop threshold: %s\n", snd_strerror(err));
1009 if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) avail_min)) < 0) {
1010 AUDIO_LOG_WARN("Unable to set start threshold: %s\n", snd_strerror(err));
1013 if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) {
1014 AUDIO_LOG_WARN("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err));
1017 if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) {
1018 AUDIO_LOG_WARN("Unable to set sw params: %s\n", snd_strerror(err));
1021 return AUDIO_RET_OK;