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 #ifndef __USE_TINYALSA__
36 /* FIXME : To avoid build warning... */
37 int _snd_pcm_poll_descriptor(snd_pcm_t *pcm);
40 #ifdef __USE_TINYALSA__
41 static int __parse_card_device_number(const char *card, const char *device, unsigned int *card_u, unsigned int *device_u) {
42 AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
43 AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER);
44 AUDIO_RETURN_VAL_IF_FAIL(card_u, AUDIO_ERR_PARAMETER);
45 AUDIO_RETURN_VAL_IF_FAIL(device_u, AUDIO_ERR_PARAMETER);
47 AUDIO_LOG_DEBUG("card : %s, device : %s", card, device);
49 *card_u = (unsigned int) strtol(card, NULL, 10);
50 *device_u = (unsigned int) strtol(device, NULL, 10);
55 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)
57 struct pcm *pcm = NULL;
58 struct pcm_config config;
59 unsigned int card_u, device_u;
61 AUDIO_RETURN_NULL_IF_FAIL(device);
62 AUDIO_RETURN_NULL_IF_FAIL(ss);
64 config.channels = ss->channels;
65 config.rate = ss->rate;
66 config.period_size = period_size;
67 config.period_count = period_count;
68 config.format = ss->format;
69 config.start_threshold = period_size;
70 config.stop_threshold = 0xFFFFFFFF;
71 config.silence_threshold = 0;
73 AUDIO_LOG_INFO("card %s, device %s, direction %d, channels %d, rate %d, format %d, period_size %d, period_count %d",
74 card, device, direction, ss->channels, ss->rate, ss->format, period_size, period_count);
76 if (__parse_card_device_number(card, device, &card_u, &device_u) < 0) {
77 AUDIO_LOG_ERROR("Failed to get card device number from %s", device);
81 pcm = pcm_open(card_u, device_u, (direction == AUDIO_DIRECTION_OUT) ? PCM_OUT : PCM_IN, &config);
82 if (!pcm || !pcm_is_ready(pcm)) {
83 AUDIO_LOG_ERROR("Unable to open device (%s)", pcm_get_error(pcm));
91 static int __tinyalsa_pcm_recover(struct pcm *pcm, int err)
95 if (err == -EINTR) /* nothing to do, continue */
98 AUDIO_LOG_INFO("XRUN occurred");
99 err = pcm_prepare(pcm);
101 AUDIO_LOG_ERROR("Could not recover from XRUN occurred, prepare failed : %d", err);
106 if (err == -ESTRPIPE) {
107 /* tinyalsa does not support pcm resume, dont't care suspend case */
108 AUDIO_LOG_ERROR("Could not recover from suspend : %d", err);
115 #ifndef __USE_TINYALSA__
116 static int __make_alsa_device_name(const char *card, const char *device, char device_name[])
118 AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
119 AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER);
120 AUDIO_RETURN_VAL_IF_FAIL(device_name, AUDIO_ERR_PARAMETER);
122 snprintf(device_name, DEVICE_NAME_MAX, "hw:%s,%s", card, device);
127 audio_return_e _pcm_open(const char *card, const char *device, uint32_t direction, void *sample_spec,
128 uint32_t period_size, uint32_t periods, void **pcm_handle)
132 AUDIO_RETURN_VAL_IF_FAIL(card, AUDIO_ERR_PARAMETER);
133 AUDIO_RETURN_VAL_IF_FAIL(device, AUDIO_ERR_PARAMETER);
134 AUDIO_RETURN_VAL_IF_FAIL((direction == AUDIO_DIRECTION_OUT) || (direction == AUDIO_DIRECTION_IN),
135 AUDIO_ERR_PARAMETER);
137 AUDIO_LOG_INFO("card(%s) device(%s) direction(%u) period_size(%u) periods(%u)",
138 card, device, direction, period_size, periods);
139 #ifdef __USE_TINYALSA__
140 audio_pcm_sample_spec_s *ss;
142 convert_hal_format_from_sample_spec(sample_spec, &ss);
143 *pcm_handle = __tinyalsa_open_device(card, device, ss, (size_t)period_size, (size_t)periods, direction);
144 if (*pcm_handle == NULL) {
145 AUDIO_LOG_ERROR("Error opening PCM device");
146 return AUDIO_ERR_RESOURCE;
149 if ((err = pcm_prepare((struct pcm *)*pcm_handle)) != 0) {
150 AUDIO_LOG_ERROR("Error prepare PCM device : %d", err);
156 char device_name[DEVICE_NAME_MAX];
158 __make_alsa_device_name(card, device, device_name);
159 mode = SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT;
161 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) {
162 AUDIO_LOG_ERROR("Error opening PCM device %s : %s", device_name, snd_strerror(err));
163 return AUDIO_ERR_RESOURCE;
166 if ((ret = _pcm_set_params(*pcm_handle, direction, sample_spec, period_size, periods)) != AUDIO_RET_OK) {
167 AUDIO_LOG_ERROR("Failed to set pcm parameters : %d", ret);
171 AUDIO_LOG_INFO("PCM device %s", device_name);
177 audio_return_e _pcm_start(void *pcm_handle)
181 #ifdef __USE_TINYALSA__
182 if ((err = pcm_start(pcm_handle)) < 0) {
183 AUDIO_LOG_ERROR("Error starting PCM handle : %d", err);
184 return AUDIO_ERR_RESOURCE;
187 if ((err = snd_pcm_start(pcm_handle)) < 0) {
188 AUDIO_LOG_ERROR("Error starting PCM handle : %s", snd_strerror(err));
189 return AUDIO_ERR_RESOURCE;
193 AUDIO_LOG_INFO("PCM handle %p start", pcm_handle);
197 audio_return_e _pcm_stop(void *pcm_handle)
201 #ifdef __USE_TINYALSA__
202 if ((err = pcm_stop(pcm_handle)) < 0) {
203 AUDIO_LOG_ERROR("Error stopping PCM handle : %d", err);
204 return AUDIO_ERR_RESOURCE;
207 if ((err = snd_pcm_drop(pcm_handle)) < 0) {
208 AUDIO_LOG_ERROR("Error stopping PCM handle : %s", snd_strerror(err));
209 return AUDIO_ERR_RESOURCE;
213 AUDIO_LOG_INFO("PCM handle %p stop", pcm_handle);
217 audio_return_e _pcm_close(void *pcm_handle)
221 AUDIO_LOG_INFO("Try to close PCM handle %p", pcm_handle);
223 #ifdef __USE_TINYALSA__
224 if ((err = pcm_close(pcm_handle)) < 0) {
225 AUDIO_LOG_ERROR("Error closing PCM handle : %d", err);
226 return AUDIO_ERR_RESOURCE;
229 if ((err = snd_pcm_close(pcm_handle)) < 0) {
230 AUDIO_LOG_ERROR("Error closing PCM handle : %s", snd_strerror(err));
231 return AUDIO_ERR_RESOURCE;
238 audio_return_e _pcm_avail(void *pcm_handle, uint32_t *avail)
240 #ifdef __USE_TINYALSA__
241 struct timespec tspec;
242 unsigned int frames_avail = 0;
245 err = pcm_get_htimestamp(pcm_handle, &frames_avail, &tspec);
247 AUDIO_LOG_ERROR("Could not get avail and timespec at PCM handle %p : %d", pcm_handle, err);
248 return AUDIO_ERR_IOCTL;
252 AUDIO_LOG_DEBUG("avail = %d", frames_avail);
255 *avail = (uint32_t)frames_avail;
257 snd_pcm_sframes_t frames_avail;
259 if ((frames_avail = snd_pcm_avail(pcm_handle)) < 0) {
260 AUDIO_LOG_ERROR("Could not get avail at PCM handle %p : %ld", pcm_handle, frames_avail);
261 return AUDIO_ERR_IOCTL;
265 AUDIO_LOG_DEBUG("avail = %d", frames_avail);
268 *avail = (uint32_t)frames_avail;
274 audio_return_e _pcm_write(void *pcm_handle, const void *buffer, uint32_t frames)
276 #ifdef __USE_TINYALSA__
279 err = pcm_write(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
281 AUDIO_LOG_ERROR("Failed to write pcm : %d", err);
282 return AUDIO_ERR_IOCTL;
286 AUDIO_LOG_DEBUG("_pcm_write = %d", frames);
289 snd_pcm_sframes_t frames_written;
291 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
293 frames_written = snd_pcm_writei(pcm_handle, buffer, (snd_pcm_uframes_t) frames);
294 if (frames_written < 0) {
295 AUDIO_LOG_ERROR("Failed to write pcm : %ld", frames_written);
296 return AUDIO_ERR_IOCTL;
300 AUDIO_LOG_DEBUG("_pcm_write = (%d / %d)", frames_written, frames);
307 audio_return_e _pcm_read(void *pcm_handle, void *buffer, uint32_t frames)
309 #ifdef __USE_TINYALSA__
312 err = pcm_read(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
314 AUDIO_LOG_ERROR("Failed to read pcm : %d", err);
315 return AUDIO_ERR_IOCTL;
319 AUDIO_LOG_DEBUG("audio_pcm_read = %d", frames);
322 snd_pcm_sframes_t frames_read;
324 frames_read = snd_pcm_readi(pcm_handle, buffer, (snd_pcm_uframes_t)frames);
325 if (frames_read < 0) {
326 AUDIO_LOG_ERROR("Failed to read pcm : %ld", frames_read);
327 return AUDIO_ERR_IOCTL;
331 AUDIO_LOG_DEBUG("_pcm_read = (%d / %d)", frames_read, frames);
338 audio_return_e _pcm_get_fd(void *pcm_handle, int *fd)
340 /* we use an internal API of the (tiny)alsa library, so it causes warning message during compile */
341 #ifdef __USE_TINYALSA__
342 *fd = _pcm_poll_descriptor((struct pcm *)pcm_handle);
344 *fd = _snd_pcm_poll_descriptor((snd_pcm_t *)pcm_handle);
349 audio_return_e _pcm_recover(void *pcm_handle, int revents)
353 AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
355 if (revents & POLLERR)
356 AUDIO_LOG_DEBUG("Got POLLERR from ALSA");
357 if (revents & POLLNVAL)
358 AUDIO_LOG_DEBUG("Got POLLNVAL from ALSA");
359 if (revents & POLLHUP)
360 AUDIO_LOG_DEBUG("Got POLLHUP from ALSA");
361 if (revents & POLLPRI)
362 AUDIO_LOG_DEBUG("Got POLLPRI from ALSA");
363 if (revents & POLLIN)
364 AUDIO_LOG_DEBUG("Got POLLIN from ALSA");
365 if (revents & POLLOUT)
366 AUDIO_LOG_DEBUG("Got POLLOUT from ALSA");
368 #ifdef __USE_TINYALSA__
369 state = pcm_state(pcm_handle);
370 AUDIO_LOG_DEBUG("PCM state is %d", state);
374 if ((err = __tinyalsa_pcm_recover(pcm_handle, -EPIPE)) != 0) {
375 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
376 return AUDIO_ERR_IOCTL;
380 case PCM_STATE_SUSPENDED:
381 if ((err = __tinyalsa_pcm_recover(pcm_handle, -ESTRPIPE)) != 0) {
382 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
383 return AUDIO_ERR_IOCTL;
388 pcm_stop(pcm_handle);
389 if ((err = pcm_prepare(pcm_handle)) < 0) {
390 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with pcm_prepare() : %d", err);
391 return AUDIO_ERR_IOCTL;
395 state = snd_pcm_state(pcm_handle);
396 AUDIO_LOG_DEBUG("PCM state is %s", snd_pcm_state_name(state));
398 /* Try to recover from this error */
401 case SND_PCM_STATE_XRUN:
402 if ((err = snd_pcm_recover(pcm_handle, -EPIPE, 1)) != 0) {
403 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
404 return AUDIO_ERR_IOCTL;
408 case SND_PCM_STATE_SUSPENDED:
409 if ((err = snd_pcm_recover(pcm_handle, -ESTRPIPE, 1)) != 0) {
410 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
411 return AUDIO_ERR_IOCTL;
416 snd_pcm_drop(pcm_handle);
417 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
418 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare() : %d", err);
419 return AUDIO_ERR_IOCTL;
425 AUDIO_LOG_DEBUG("_pcm_recover");
429 audio_return_e _pcm_get_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t *period_size, uint32_t *periods)
431 #ifdef __USE_TINYALSA__
432 audio_pcm_sample_spec_s *ss;
433 unsigned int _period_size, _buffer_size, _periods, _format, _rate, _channels;
434 unsigned int _start_threshold, _stop_threshold, _silence_threshold;
435 struct pcm_config *config;
437 ss = (audio_pcm_sample_spec_s *)sample_spec;
439 /* we use an internal API of the tiny alsa library, so it causes warning message during compile */
440 _pcm_config(pcm_handle, &config);
442 *period_size = config->period_size;
443 *periods = config->period_count;
444 _buffer_size = config->period_size * config->period_count;
445 ss->format = config->format;
446 ss->rate = config->rate;
447 ss->channels = config->channels;
448 _start_threshold = config->start_threshold;
449 _stop_threshold = config->stop_threshold;
450 _silence_threshold = config->silence_threshold;
452 AUDIO_LOG_DEBUG("_pcm_get_params (handle %p, format %d, rate %u, channels %u, period_size %u, periods %u, buffer_size %u)",
453 pcm_handle, config->format, config->rate, config->channels, config->period_size, config->period_count, _buffer_size);
456 audio_pcm_sample_spec_s ss;
458 snd_pcm_uframes_t _period_size = 0 , _buffer_size = 0;
459 snd_pcm_format_t _format;
460 unsigned int _rate = 0, _channels = 0;
461 snd_pcm_uframes_t _start_threshold = 0, _stop_threshold = 0, _silence_threshold = 0, _avail_min = 0;
462 unsigned int _periods = 0;
463 snd_pcm_hw_params_t *hwparams;
464 snd_pcm_sw_params_t *swparams;
466 snd_pcm_hw_params_alloca(&hwparams);
467 snd_pcm_sw_params_alloca(&swparams);
469 if ((err = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) {
470 AUDIO_LOG_ERROR("snd_pcm_hw_params_current() failed : %d", err);
471 return AUDIO_ERR_PARAMETER;
474 if ((err = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
475 (err = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0 ||
476 (err = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0 ||
477 (err = snd_pcm_hw_params_get_format(hwparams, &_format)) < 0 ||
478 (err = snd_pcm_hw_params_get_rate(hwparams, &_rate, &dir)) < 0 ||
479 (err = snd_pcm_hw_params_get_channels(hwparams, &_channels)) < 0) {
480 AUDIO_LOG_ERROR("snd_pcm_hw_params_get_{period_size|buffer_size|periods|format|rate|channels}() failed : %d", err);
481 return AUDIO_ERR_PARAMETER;
484 *period_size = _period_size;
488 ss.channels = _channels;
490 convert_hal_format_to_sample_spec(&ss, sample_spec);
492 if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
493 AUDIO_LOG_ERROR("snd_pcm_sw_params_current() failed : %d", err);
494 return AUDIO_ERR_PARAMETER;
497 if ((err = snd_pcm_sw_params_get_start_threshold(swparams, &_start_threshold)) < 0 ||
498 (err = snd_pcm_sw_params_get_stop_threshold(swparams, &_stop_threshold)) < 0 ||
499 (err = snd_pcm_sw_params_get_silence_threshold(swparams, &_silence_threshold)) < 0 ||
500 (err = snd_pcm_sw_params_get_avail_min(swparams, &_avail_min)) < 0) {
501 AUDIO_LOG_ERROR("snd_pcm_sw_params_get_{start_threshold|stop_threshold|silence_threshold|avail_min}() failed : %d", err);
504 AUDIO_LOG_DEBUG("_pcm_get_params (handle %p, format %d, rate %u, channels %u, period_size %lu, periods %u, buffer_size %lu,",
505 pcm_handle, ss.format, ss.rate, ss.channels, _period_size, _periods, _buffer_size);
512 static int __set_format(void *pcm_handle, snd_pcm_hw_params_t *hwparams, snd_pcm_format_t *format)
514 const snd_pcm_format_t formats[] = {
516 SND_PCM_FORMAT_A_LAW,
517 SND_PCM_FORMAT_MU_LAW,
518 SND_PCM_FORMAT_S16_LE,
519 SND_PCM_FORMAT_S16_BE,
520 SND_PCM_FORMAT_FLOAT_LE,
521 SND_PCM_FORMAT_FLOAT_BE,
522 SND_PCM_FORMAT_S32_LE,
523 SND_PCM_FORMAT_S32_BE,
524 SND_PCM_FORMAT_S24_3LE,
525 SND_PCM_FORMAT_S24_3BE,
526 SND_PCM_FORMAT_S24_LE,
527 SND_PCM_FORMAT_S24_BE,
531 if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, *format) >= 0)
534 /* Try to find appropriate format */
535 for (i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
536 if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, formats[i]) >= 0) {
537 *format = formats[i];
538 AUDIO_LOG_INFO("Selected proper format automatically. format(%d)", formats[i]);
546 audio_return_e _pcm_set_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
548 #ifdef __USE_TINYALSA__
549 /* Parameters are only acceptable in pcm_open() function */
550 AUDIO_LOG_DEBUG("_pcm_set_params");
553 snd_pcm_hw_params_t *hwparams;
554 snd_pcm_sw_params_t *swparams;
556 snd_pcm_uframes_t period_size_near;
557 snd_pcm_uframes_t buffer_size;
558 snd_pcm_uframes_t buffer_size_near;
559 audio_pcm_sample_spec_s requested_ss;
560 audio_pcm_sample_spec_s selected_ss;
563 snd_pcm_hw_params_alloca(&hwparams);
564 snd_pcm_sw_params_alloca(&swparams);
566 convert_hal_format_from_sample_spec(sample_spec, &requested_ss);
567 convert_hal_format_from_sample_spec(sample_spec, &selected_ss);
569 ch = selected_ss.channels;
572 if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
573 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() failed : %d", err);
574 return AUDIO_ERR_PARAMETER;
577 if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) {
578 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate_resample() failed : %d", err);
579 return AUDIO_ERR_PARAMETER;
582 if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
583 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() failed : %d", err);
584 return AUDIO_ERR_PARAMETER;
587 if ((err = __set_format(pcm_handle, hwparams, &selected_ss.format) < 0)) {
588 AUDIO_LOG_ERROR("Failed to set format.");
589 return AUDIO_ERR_PARAMETER;
592 if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &selected_ss.rate, NULL)) < 0) {
593 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() failed : %d", err);
594 return AUDIO_ERR_PARAMETER;
597 if ((err = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &ch)) < 0) {
598 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", selected_ss.channels, err);
599 return AUDIO_ERR_PARAMETER;
601 selected_ss.channels = ch;
603 if (requested_ss.rate != selected_ss.rate ||
604 requested_ss.format != selected_ss.format ||
605 requested_ss.channels != selected_ss.channels) {
607 uint32_t _period_size = selected_ss.rate * period_size / requested_ss.rate;
609 AUDIO_LOG_INFO("hwparam has been changed. rate(%d->%d), channels(%d->%d), format(%d->%d)",
610 requested_ss.rate, selected_ss.rate,
611 requested_ss.channels, selected_ss.channels,
612 requested_ss.format, selected_ss.format);
614 AUDIO_LOG_INFO("period_size must be calculated appropriately. period_size(%d->%d)",
615 period_size, _period_size);
617 period_size = _period_size;
620 if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &periods, NULL) < 0) {
621 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods_near failed : %d", err);
622 return AUDIO_ERR_PARAMETER;
625 period_size_near = period_size;
626 if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &period_size_near, NULL) < 0) {
627 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size_near(%u) failed : %d", period_size, err);
628 return AUDIO_ERR_PARAMETER;
630 AUDIO_LOG_INFO("requested period_size(%d). set period_size_near(%ld)", period_size, period_size_near);
632 period_size = period_size_near;
633 buffer_size = period_size * periods;
634 buffer_size_near = buffer_size;
636 if ((err = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size_near)) < 0) {
637 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size_near(%lu) failed : %d", buffer_size, err);
638 return AUDIO_ERR_PARAMETER;
641 AUDIO_LOG_INFO("requested buffer_size(%lu). set buffer_size(%lu)", buffer_size, buffer_size_near);
643 buffer_size = buffer_size_near;
645 if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
646 AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err);
647 return AUDIO_ERR_IOCTL;
651 if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
652 AUDIO_LOG_ERROR("Unable to determine current swparams : %d", err);
653 return AUDIO_ERR_PARAMETER;
656 if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
657 AUDIO_LOG_ERROR("Unable to enable time stamping : %d", err);
658 return AUDIO_ERR_PARAMETER;
661 if ((err = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xFFFFFFFF)) < 0) {
662 AUDIO_LOG_ERROR("Unable to set stop threshold : %d", err);
663 return AUDIO_ERR_PARAMETER;
666 if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, buffer_size)) < 0) {
667 AUDIO_LOG_ERROR("Unable to set start threshold : %d", err);
668 return AUDIO_ERR_PARAMETER;
671 if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, period_size)) < 0) {
672 AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err);
673 return AUDIO_ERR_PARAMETER;
676 if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0) {
677 AUDIO_LOG_ERROR("Unable to set sw params : %d", err);
678 return AUDIO_ERR_IOCTL;
682 if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
683 AUDIO_LOG_ERROR("snd_pcm_prepare() failed : %d", err);
684 return AUDIO_ERR_IOCTL;
687 convert_hal_format_to_sample_spec(&selected_ss, sample_spec);
689 AUDIO_LOG_DEBUG("_pcm_set_params (handle %p, format(%d), rate(%u), channels(%u), period_size(%u), periods(%u), buffer_size(%lu)",
690 pcm_handle, selected_ss.format, selected_ss.rate, selected_ss.channels, period_size, periods, buffer_size);
696 /* Generic snd pcm interface APIs */
697 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)
699 audio_return_e ret = AUDIO_RET_OK;
700 snd_pcm_hw_params_t *hwparams;
703 unsigned int val = 0;
704 snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
705 snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0;
706 uint8_t _use_mmap = use_mmap && *use_mmap;
707 uint32_t channels = 0;
709 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
711 snd_pcm_hw_params_alloca(&hwparams);
713 /* Skip parameter setting to null device. */
714 if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
715 return AUDIO_ERR_IOCTL;
717 /* Allocate a hardware parameters object. */
718 snd_pcm_hw_params_alloca(&hwparams);
720 /* Fill it in with default values. */
721 if (snd_pcm_hw_params_any(pcm, hwparams) < 0) {
722 AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
726 /* Set the desired hardware parameters. */
730 if (snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
732 /* mmap() didn't work, fall back to interleaved */
734 if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
735 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
742 } else if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
743 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
746 AUDIO_LOG_DEBUG("setting rate - %d", sample_spec->rate);
747 err = snd_pcm_hw_params_set_rate(pcm, hwparams, sample_spec->rate, 0);
749 AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
752 err = snd_pcm_hw_params(pcm, hwparams);
754 AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
758 /* Dump current param */
760 if ((ret = snd_pcm_hw_params_current(pcm, hwparams)) < 0) {
761 AUDIO_LOG_INFO("snd_pcm_hw_params_current() failed: %s", snd_strerror(ret));
765 if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
766 (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) {
767 AUDIO_LOG_INFO("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", snd_strerror(ret));
771 snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val);
772 AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
774 snd_pcm_hw_params_get_format(hwparams, &sample_spec->format);
775 AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
776 snd_pcm_format_name((snd_pcm_format_t)sample_spec->format),
777 snd_pcm_format_description((snd_pcm_format_t)sample_spec->format));
779 snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val);
780 AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
781 snd_pcm_subformat_name((snd_pcm_subformat_t)val),
782 snd_pcm_subformat_description((snd_pcm_subformat_t)val));
784 snd_pcm_hw_params_get_channels(hwparams, &channels);
785 sample_spec->channels = (uint8_t)channels;
786 AUDIO_LOG_DEBUG("channels = %d\n", sample_spec->channels);
789 *buffer_size = _buffer_size;
792 *period_size = _period_size;
795 *use_mmap = _use_mmap;
800 return AUDIO_ERR_RESOURCE;
803 audio_return_e _pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event)
805 snd_pcm_sw_params_t *swparams;
806 snd_pcm_uframes_t boundary;
809 AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
811 snd_pcm_sw_params_alloca(&swparams);
813 if ((err = snd_pcm_sw_params_current(pcm, swparams)) < 0) {
814 AUDIO_LOG_WARN("Unable to determine current swparams: %s\n", snd_strerror(err));
817 if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, period_event)) < 0) {
818 AUDIO_LOG_WARN("Unable to disable period event: %s\n", snd_strerror(err));
821 if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
822 AUDIO_LOG_WARN("Unable to enable time stamping: %s\n", snd_strerror(err));
825 if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) {
826 AUDIO_LOG_WARN("Unable to get boundary: %s\n", snd_strerror(err));
829 if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) {
830 AUDIO_LOG_WARN("Unable to set stop threshold: %s\n", snd_strerror(err));
833 if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) avail_min)) < 0) {
834 AUDIO_LOG_WARN("Unable to set start threshold: %s\n", snd_strerror(err));
837 if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) {
838 AUDIO_LOG_WARN("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err));
841 if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) {
842 AUDIO_LOG_WARN("Unable to set sw params: %s\n", snd_strerror(err));