Initial copy from audio-hal-ak4953
[platform/adaptation/nexell/audio-hal-alc5658.git] / tizen-audio-impl-pcm.c
1 /*
2  * audio-hal
3  *
4  * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved.
5  *
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
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdbool.h>
28
29 #include "tizen-audio-internal.h"
30 #include "tizen-audio-impl.h"
31
32 #ifdef __USE_TINYALSA__
33 /* Convert pcm format from pulse to alsa */
34 static const uint32_t g_format_convert_table[] = {
35     [AUDIO_SAMPLE_U8]        = PCM_FORMAT_S8,
36     [AUDIO_SAMPLE_S16LE]     = PCM_FORMAT_S16_LE,
37     [AUDIO_SAMPLE_S32LE]     = PCM_FORMAT_S32_LE,
38     [AUDIO_SAMPLE_S24_32LE]  = PCM_FORMAT_S24_LE
39 };
40 #else  /* alsa-lib */
41 /* FIXME : To avoid build warning... */
42 int _snd_pcm_poll_descriptor(snd_pcm_t *pcm);
43 /* Convert pcm format from pulse to alsa */
44 static const uint32_t g_format_convert_table[] = {
45     [AUDIO_SAMPLE_U8]        = SND_PCM_FORMAT_U8,
46     [AUDIO_SAMPLE_ALAW]      = SND_PCM_FORMAT_A_LAW,
47     [AUDIO_SAMPLE_ULAW]      = SND_PCM_FORMAT_MU_LAW,
48     [AUDIO_SAMPLE_S16LE]     = SND_PCM_FORMAT_S16_LE,
49     [AUDIO_SAMPLE_S16BE]     = SND_PCM_FORMAT_S16_BE,
50     [AUDIO_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
51     [AUDIO_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
52     [AUDIO_SAMPLE_S32LE]     = SND_PCM_FORMAT_S32_LE,
53     [AUDIO_SAMPLE_S32BE]     = SND_PCM_FORMAT_S32_BE,
54     [AUDIO_SAMPLE_S24LE]     = SND_PCM_FORMAT_S24_3LE,
55     [AUDIO_SAMPLE_S24BE]     = SND_PCM_FORMAT_S24_3BE,
56     [AUDIO_SAMPLE_S24_32LE]  = SND_PCM_FORMAT_S24_LE,
57     [AUDIO_SAMPLE_S24_32BE]  = SND_PCM_FORMAT_S24_BE
58 };
59 #endif
60
61 static uint32_t __convert_format(audio_sample_format_t format)
62 {
63     return g_format_convert_table[format];
64 }
65
66 /* #define DEBUG_TIMING */
67
68 #ifdef __USE_TINYALSA__
69 static struct pcm *__tinyalsa_open_device(audio_pcm_sample_spec_t *ss, size_t period_size, size_t period_count, uint32_t direction)
70 {
71     struct pcm *pcm = NULL;
72     struct pcm_config config;
73
74     AUDIO_RETURN_NULL_IF_FAIL(ss);
75
76     config.channels          = ss->channels;
77     config.rate              = ss->rate;
78     config.period_size       = period_size;
79     config.period_count      = period_count;
80     config.format            = ss->format;
81     config.start_threshold   = period_size;
82     config.stop_threshold    = 0xFFFFFFFF;
83     config.silence_threshold = 0;
84
85     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);
86
87     pcm = pcm_open((direction == AUDIO_DIRECTION_OUT) ? PLAYBACK_CARD_ID : CAPTURE_CARD_ID,
88                    (direction == AUDIO_DIRECTION_OUT) ? PLAYBACK_PCM_DEVICE_ID : CAPTURE_PCM_DEVICE_ID,
89                    (direction == AUDIO_DIRECTION_OUT) ? PCM_OUT : PCM_IN,
90                    &config);
91     if (!pcm || !pcm_is_ready(pcm)) {
92         AUDIO_LOG_ERROR("Unable to open device (%s)", pcm_get_error(pcm));
93         pcm_close(pcm);
94         return NULL;
95     }
96
97     return pcm;
98 }
99
100 static int __tinyalsa_pcm_recover(struct pcm *pcm, int err)
101 {
102     if (err > 0)
103         err = -err;
104     if (err == -EINTR)  /* nothing to do, continue */
105         return 0;
106     if (err == -EPIPE) {
107         AUDIO_LOG_INFO("XRUN occurred");
108         err = pcm_prepare(pcm);
109         if (err < 0) {
110             AUDIO_LOG_ERROR("Could not recover from XRUN occurred, prepare failed : %d", err);
111             return err;
112         }
113         return 0;
114     }
115     if (err == -ESTRPIPE) {
116         /* tinyalsa does not support pcm resume, dont't care suspend case */
117         AUDIO_LOG_ERROR("Could not recover from suspend : %d", err);
118         return err;
119     }
120     return err;
121 }
122 #endif
123
124 audio_return_t _pcm_open(void **pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
125 {
126 #ifdef __USE_TINYALSA__
127     audio_pcm_sample_spec_t *ss;
128     int err;
129
130     ss = (audio_pcm_sample_spec_t *)sample_spec;
131     ss->format = __convert_format((audio_sample_format_t)ss->format);
132
133     *pcm_handle = __tinyalsa_open_device(ss, (size_t)period_size, (size_t)periods, direction);
134     if (*pcm_handle == NULL) {
135         AUDIO_LOG_ERROR("Error opening PCM device");
136         return AUDIO_ERR_RESOURCE;
137     }
138
139     if ((err = pcm_prepare((struct pcm *)*pcm_handle)) != 0) {
140         AUDIO_LOG_ERROR("Error prepare PCM device : %d", err);
141     }
142
143 #else  /* alsa-lib */
144     int err, mode;
145     char *device_name = NULL;
146
147     mode =  SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT;
148
149     if (direction == AUDIO_DIRECTION_OUT)
150         device_name = PLAYBACK_PCM_DEVICE;
151     else if (direction == AUDIO_DIRECTION_IN)
152         device_name = CAPTURE_PCM_DEVICE;
153     else {
154         AUDIO_LOG_ERROR("Error get device_name, direction : %d", direction);
155         return AUDIO_ERR_RESOURCE;
156     }
157
158     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) {
159         AUDIO_LOG_ERROR("Error opening PCM device %s : %s", device_name, snd_strerror(err));
160         return AUDIO_ERR_RESOURCE;
161     }
162
163     if ((err = _pcm_set_params(*pcm_handle, direction, sample_spec, period_size, periods)) != AUDIO_RET_OK) {
164         AUDIO_LOG_ERROR("Failed to set pcm parameters : %d", err);
165         return err;
166     }
167
168     AUDIO_LOG_INFO("PCM device %s", device_name);
169 #endif
170
171     return AUDIO_RET_OK;
172 }
173
174 audio_return_t _pcm_start(void *pcm_handle)
175 {
176     int err;
177
178 #ifdef __USE_TINYALSA__
179     if ((err = pcm_start(pcm_handle)) < 0) {
180         AUDIO_LOG_ERROR("Error starting PCM handle : %d", err);
181         return AUDIO_ERR_RESOURCE;
182     }
183 #else  /* alsa-lib */
184     if ((err = snd_pcm_start(pcm_handle)) < 0) {
185         AUDIO_LOG_ERROR("Error starting PCM handle : %s", snd_strerror(err));
186         return AUDIO_ERR_RESOURCE;
187     }
188 #endif
189
190     AUDIO_LOG_INFO("PCM handle 0x%x start", pcm_handle);
191     return AUDIO_RET_OK;
192 }
193
194 audio_return_t _pcm_stop(void *pcm_handle)
195 {
196     int err;
197
198 #ifdef __USE_TINYALSA__
199     if ((err = pcm_stop(pcm_handle)) < 0) {
200         AUDIO_LOG_ERROR("Error stopping PCM handle : %d", err);
201         return AUDIO_ERR_RESOURCE;
202     }
203 #else  /* alsa-lib */
204     if ((err = snd_pcm_drop(pcm_handle)) < 0) {
205         AUDIO_LOG_ERROR("Error stopping PCM handle : %s", snd_strerror(err));
206         return AUDIO_ERR_RESOURCE;
207     }
208 #endif
209
210     AUDIO_LOG_INFO("PCM handle 0x%x stop", pcm_handle);
211     return AUDIO_RET_OK;
212 }
213
214 audio_return_t _pcm_close(void *pcm_handle)
215 {
216     int err;
217
218     AUDIO_LOG_INFO("Try to close PCM handle 0x%x", pcm_handle);
219
220 #ifdef __USE_TINYALSA__
221     if ((err = pcm_close(pcm_handle)) < 0) {
222         AUDIO_LOG_ERROR("Error closing PCM handle : %d", err);
223         return AUDIO_ERR_RESOURCE;
224     }
225 #else  /* alsa-lib */
226     if ((err = snd_pcm_close(pcm_handle)) < 0) {
227         AUDIO_LOG_ERROR("Error closing PCM handle : %s", snd_strerror(err));
228         return AUDIO_ERR_RESOURCE;
229     }
230 #endif
231
232     return AUDIO_RET_OK;
233 }
234
235 audio_return_t _pcm_avail(void *pcm_handle, uint32_t *avail)
236 {
237 #ifdef __USE_TINYALSA__
238     struct timespec tspec;
239     unsigned int frames_avail = 0;
240     int err;
241
242     err = pcm_get_htimestamp(pcm_handle, &frames_avail, &tspec);
243     if (err < 0) {
244         AUDIO_LOG_ERROR("Could not get avail and timespec at PCM handle 0x%x : %d", pcm_handle, err);
245         return AUDIO_ERR_IOCTL;
246     }
247
248 #ifdef DEBUG_TIMING
249     AUDIO_LOG_DEBUG("avail = %d", frames_avail);
250 #endif
251
252     *avail = (uint32_t)frames_avail;
253 #else  /* alsa-lib */
254     snd_pcm_sframes_t frames_avail;
255
256     if ((frames_avail = snd_pcm_avail(pcm_handle)) < 0) {
257         AUDIO_LOG_ERROR("Could not get avail at PCM handle 0x%x : %d", pcm_handle, frames_avail);
258         return AUDIO_ERR_IOCTL;
259     }
260
261 #ifdef DEBUG_TIMING
262     AUDIO_LOG_DEBUG("avail = %d", frames_avail);
263 #endif
264
265     *avail = (uint32_t)frames_avail;
266 #endif
267
268     return AUDIO_RET_OK;
269 }
270
271 audio_return_t _pcm_write(void *pcm_handle, const void *buffer, uint32_t frames)
272 {
273 #ifdef __USE_TINYALSA__
274     int err;
275
276     err = pcm_write(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
277     if (err < 0) {
278         AUDIO_LOG_ERROR("Failed to write pcm : %d", err);
279         return AUDIO_ERR_IOCTL;
280     }
281
282 #ifdef DEBUG_TIMING
283     AUDIO_LOG_DEBUG("_pcm_write = %d", frames);
284 #endif
285 #else  /* alsa-lib */
286     snd_pcm_sframes_t frames_written;
287
288     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
289
290     frames_written = snd_pcm_writei(pcm_handle, buffer, (snd_pcm_uframes_t) frames);
291     if (frames_written < 0) {
292         AUDIO_LOG_ERROR("Failed to write pcm : %d", frames_written);
293         return AUDIO_ERR_IOCTL;
294     }
295
296 #ifdef DEBUG_TIMING
297     AUDIO_LOG_DEBUG("_pcm_write = (%d / %d)", frames_written, frames);
298 #endif
299 #endif
300
301     return AUDIO_RET_OK;
302 }
303
304 audio_return_t _pcm_read(void *pcm_handle, void *buffer, uint32_t frames)
305 {
306 #ifdef __USE_TINYALSA__
307     int err;
308
309     err = pcm_read(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
310     if (err < 0) {
311         AUDIO_LOG_ERROR("Failed to read pcm : %d", err);
312         return AUDIO_ERR_IOCTL;
313     }
314
315 #ifdef DEBUG_TIMING
316     AUDIO_LOG_DEBUG("audio_pcm_read = %d", frames);
317 #endif
318 #else  /* alsa-lib */
319     snd_pcm_sframes_t frames_read;
320
321     frames_read = snd_pcm_readi(pcm_handle, buffer, (snd_pcm_uframes_t)frames);
322     if (frames_read < 0) {
323         AUDIO_LOG_ERROR("Failed to read pcm : %d", frames_read);
324         return AUDIO_ERR_IOCTL;
325     }
326
327 #ifdef DEBUG_TIMING
328     AUDIO_LOG_DEBUG("_pcm_read = (%d / %d)", frames_read, frames);
329 #endif
330 #endif
331
332     return AUDIO_RET_OK;
333 }
334
335 audio_return_t _pcm_get_fd(void *pcm_handle, int *fd)
336 {
337     /* we use an internal API of the (tiny)alsa library, so it causes warning message during compile */
338 #ifdef __USE_TINYALSA__
339     *fd = _pcm_poll_descriptor((struct pcm *)pcm_handle);
340 #else  /* alsa-lib */
341     *fd = _snd_pcm_poll_descriptor((snd_pcm_t *)pcm_handle);
342 #endif
343     return AUDIO_RET_OK;
344 }
345
346 audio_return_t _pcm_recover(void *pcm_handle, int revents)
347 {
348     int state, err;
349
350     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
351
352     if (revents & POLLERR)
353         AUDIO_LOG_DEBUG("Got POLLERR from ALSA");
354     if (revents & POLLNVAL)
355         AUDIO_LOG_DEBUG("Got POLLNVAL from ALSA");
356     if (revents & POLLHUP)
357         AUDIO_LOG_DEBUG("Got POLLHUP from ALSA");
358     if (revents & POLLPRI)
359         AUDIO_LOG_DEBUG("Got POLLPRI from ALSA");
360     if (revents & POLLIN)
361         AUDIO_LOG_DEBUG("Got POLLIN from ALSA");
362     if (revents & POLLOUT)
363         AUDIO_LOG_DEBUG("Got POLLOUT from ALSA");
364
365 #ifdef __USE_TINYALSA__
366     state = pcm_state(pcm_handle);
367     AUDIO_LOG_DEBUG("PCM state is %d", state);
368
369     switch (state) {
370         case PCM_STATE_XRUN:
371             if ((err = __tinyalsa_pcm_recover(pcm_handle, -EPIPE)) != 0) {
372                 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
373                 return AUDIO_ERR_IOCTL;
374             }
375             break;
376
377         case PCM_STATE_SUSPENDED:
378             if ((err = __tinyalsa_pcm_recover(pcm_handle, -ESTRPIPE)) != 0) {
379                 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
380                 return AUDIO_ERR_IOCTL;
381             }
382             break;
383
384         default:
385             pcm_stop(pcm_handle);
386             if ((err = pcm_prepare(pcm_handle)) < 0) {
387                 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with pcm_prepare() : %d", err);
388                 return AUDIO_ERR_IOCTL;
389             }
390     }
391 #else  /* alsa-lib */
392     state = snd_pcm_state(pcm_handle);
393     AUDIO_LOG_DEBUG("PCM state is %s", snd_pcm_state_name(state));
394
395     /* Try to recover from this error */
396
397     switch (state) {
398         case SND_PCM_STATE_XRUN:
399             if ((err = snd_pcm_recover(pcm_handle, -EPIPE, 1)) != 0) {
400                 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
401                 return AUDIO_ERR_IOCTL;
402             }
403             break;
404
405         case SND_PCM_STATE_SUSPENDED:
406             if ((err = snd_pcm_recover(pcm_handle, -ESTRPIPE, 1)) != 0) {
407                 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
408                 return AUDIO_ERR_IOCTL;
409             }
410             break;
411
412         default:
413             snd_pcm_drop(pcm_handle);
414             if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
415                 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare() : %d", err);
416                 return AUDIO_ERR_IOCTL;
417             }
418             break;
419     }
420 #endif
421
422     AUDIO_LOG_DEBUG("_pcm_recover");
423     return AUDIO_RET_OK;
424 }
425
426 audio_return_t _pcm_get_params(void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods)
427 {
428 #ifdef __USE_TINYALSA__
429     audio_pcm_sample_spec_t *ss;
430     unsigned int _period_size, _buffer_size, _periods, _format, _rate, _channels;
431     unsigned int _start_threshold, _stop_threshold, _silence_threshold;
432     struct pcm_config *config;
433
434     ss = (audio_pcm_sample_spec_t *)*sample_spec;
435
436     /* we use an internal API of the tiny alsa library, so it causes warning message during compile */
437     _pcm_config(pcm_handle, &config);
438
439     *period_size = config->period_size;
440     *periods     = config->period_count;
441     _buffer_size = config->period_size * config->period_count;
442     ss->format   = config->format;
443     ss->rate     = config->rate;
444     ss->channels = config->channels;
445     _start_threshold   = config->start_threshold;
446     _stop_threshold    = config->stop_threshold;
447     _silence_threshold = config->silence_threshold;
448
449     AUDIO_LOG_DEBUG("_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);
450 #else  /* alsa-lib */
451     int err;
452     audio_pcm_sample_spec_t *ss;
453     int dir;
454     snd_pcm_uframes_t _period_size, _buffer_size;
455     snd_pcm_format_t _format;
456     unsigned int _rate, _channels;
457     snd_pcm_uframes_t _start_threshold, _stop_threshold, _silence_threshold, _avail_min;
458     unsigned int _periods;
459     snd_pcm_hw_params_t *hwparams;
460     snd_pcm_sw_params_t *swparams;
461
462     ss = (audio_pcm_sample_spec_t *)*sample_spec;
463
464     snd_pcm_hw_params_alloca(&hwparams);
465     snd_pcm_sw_params_alloca(&swparams);
466
467     if ((err = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) {
468         AUDIO_LOG_ERROR("snd_pcm_hw_params_current() failed : %d", err);
469         return AUDIO_ERR_PARAMETER;
470     }
471
472     if ((err = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
473         (err = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0 ||
474         (err = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0 ||
475         (err = snd_pcm_hw_params_get_format(hwparams, &_format)) < 0 ||
476         (err = snd_pcm_hw_params_get_rate(hwparams, &_rate, &dir)) < 0 ||
477         (err = snd_pcm_hw_params_get_channels(hwparams, &_channels)) < 0) {
478         AUDIO_LOG_ERROR("snd_pcm_hw_params_get_{period_size|buffer_size|periods|format|rate|channels}() failed : %s", err);
479         return AUDIO_ERR_PARAMETER;
480     }
481
482     *period_size = _period_size;
483     *periods     = _periods;
484     ss->format   = _format;
485     ss->rate     = _rate;
486     ss->channels = _channels;
487
488     if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
489         AUDIO_LOG_ERROR("snd_pcm_sw_params_current() failed : %d", err);
490         return AUDIO_ERR_PARAMETER;
491     }
492
493     if ((err = snd_pcm_sw_params_get_start_threshold(swparams, &_start_threshold)) < 0  ||
494         (err = snd_pcm_sw_params_get_stop_threshold(swparams, &_stop_threshold)) < 0  ||
495         (err = snd_pcm_sw_params_get_silence_threshold(swparams, &_silence_threshold)) < 0  ||
496         (err = snd_pcm_sw_params_get_avail_min(swparams, &_avail_min)) < 0) {
497         AUDIO_LOG_ERROR("snd_pcm_sw_params_get_{start_threshold|stop_threshold|silence_threshold|avail_min}() failed : %s", err);
498     }
499
500     AUDIO_LOG_DEBUG("_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);
501 #endif
502
503     return AUDIO_RET_OK;
504 }
505
506 audio_return_t _pcm_set_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
507 {
508 #ifdef __USE_TINYALSA__
509     /* Parameters are only acceptable in pcm_open() function */
510     AUDIO_LOG_DEBUG("_pcm_set_params");
511 #else  /* alsa-lib */
512     int err;
513     audio_pcm_sample_spec_t ss;
514     snd_pcm_uframes_t _buffer_size;
515     snd_pcm_hw_params_t *hwparams;
516     snd_pcm_sw_params_t *swparams;
517
518     ss = *(audio_pcm_sample_spec_t *)sample_spec;
519
520     snd_pcm_hw_params_alloca(&hwparams);
521     snd_pcm_sw_params_alloca(&swparams);
522
523     /* Set hw params */
524     if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
525         AUDIO_LOG_ERROR("snd_pcm_hw_params_any() failed : %d", err);
526         return AUDIO_ERR_PARAMETER;
527     }
528
529     if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) {
530         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate_resample() failed : %d", err);
531         return AUDIO_ERR_PARAMETER;
532     }
533
534     if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
535         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() failed : %d", err);
536         return AUDIO_ERR_PARAMETER;
537     }
538
539     ss.format = __convert_format((audio_sample_format_t)ss.format);
540     if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, ss.format)) < 0) {
541         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_format() failed : %d", err);
542         return AUDIO_ERR_PARAMETER;
543     }
544
545     if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, ss.rate, 0)) < 0) {
546         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() failed : %d", err);
547         return AUDIO_ERR_PARAMETER;
548     }
549
550     if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss.channels)) < 0) {
551         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", err);
552         return AUDIO_ERR_PARAMETER;
553     }
554
555     if ((err = snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, period_size, 0)) < 0) {
556         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size(%u) failed : %d", err);
557         return AUDIO_ERR_PARAMETER;
558     }
559
560     if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0)) < 0) {
561         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods(%u) failed : %d", periods, err);
562         return AUDIO_ERR_PARAMETER;
563     }
564
565     _buffer_size = period_size * periods;
566     if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, _buffer_size)) < 0) {
567         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size(%u) failed : %d", periods * periods, err);
568         return AUDIO_ERR_PARAMETER;
569     }
570
571     if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
572         AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err);
573         return AUDIO_ERR_IOCTL;
574     }
575
576     /* Set sw params */
577     if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
578         AUDIO_LOG_ERROR("Unable to determine current swparams : %d", err);
579         return AUDIO_ERR_PARAMETER;
580     }
581
582     if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
583         AUDIO_LOG_ERROR("Unable to enable time stamping : %d", err);
584         return AUDIO_ERR_PARAMETER;
585     }
586
587     if ((err = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xFFFFFFFF)) < 0) {
588         AUDIO_LOG_ERROR("Unable to set stop threshold : %d", err);
589         return AUDIO_ERR_PARAMETER;
590     }
591
592     if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, period_size / 2)) < 0) {
593         AUDIO_LOG_ERROR("Unable to set start threshold : %d", err);
594         return AUDIO_ERR_PARAMETER;
595     }
596
597     if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, 1024)) < 0) {
598         AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err);
599         return AUDIO_ERR_PARAMETER;
600     }
601
602     if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0) {
603         AUDIO_LOG_ERROR("Unable to set sw params : %d", err);
604         return AUDIO_ERR_IOCTL;
605     }
606
607     /* Prepare device */
608     if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
609         AUDIO_LOG_ERROR("snd_pcm_prepare() failed : %d", err);
610         return AUDIO_ERR_IOCTL;
611     }
612
613     AUDIO_LOG_DEBUG("_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);
614 #endif
615
616     return AUDIO_RET_OK;
617 }
618
619 /* Generic snd pcm interface APIs */
620 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)
621 {
622     audio_return_t ret = AUDIO_RET_OK;
623     snd_pcm_hw_params_t *hwparams;
624     int err = 0;
625     int dir;
626     unsigned int val = 0;
627     snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
628     snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0;
629     uint8_t _use_mmap = use_mmap && *use_mmap;
630     uint32_t channels = 0;
631
632     AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
633
634     snd_pcm_hw_params_alloca(&hwparams);
635
636     /* Skip parameter setting to null device. */
637     if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
638         return AUDIO_ERR_IOCTL;
639
640     /* Allocate a hardware parameters object. */
641     snd_pcm_hw_params_alloca(&hwparams);
642
643     /* Fill it in with default values. */
644     if (snd_pcm_hw_params_any(pcm, hwparams) < 0) {
645         AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
646         goto error;
647     }
648
649     /* Set the desired hardware parameters. */
650
651     if (_use_mmap) {
652
653         if (snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
654
655             /* mmap() didn't work, fall back to interleaved */
656
657             if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
658                 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
659                 goto error;
660             }
661
662             _use_mmap = 0;
663         }
664
665     } else if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
666         AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
667         goto error;
668     }
669     AUDIO_LOG_DEBUG("setting rate - %d", sample_spec->rate);
670     err = snd_pcm_hw_params_set_rate(pcm, hwparams, sample_spec->rate, 0);
671     if (err < 0) {
672         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
673     }
674
675     err = snd_pcm_hw_params(pcm, hwparams);
676     if (err < 0) {
677         AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
678         goto error;
679     }
680
681     /* Dump current param */
682
683     if ((ret = snd_pcm_hw_params_current(pcm, hwparams)) < 0) {
684         AUDIO_LOG_INFO("snd_pcm_hw_params_current() failed: %s", snd_strerror(ret));
685         goto error;
686     }
687
688     if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
689         (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) {
690         AUDIO_LOG_INFO("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", snd_strerror(ret));
691         goto error;
692     }
693
694     snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val);
695     AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
696
697     snd_pcm_hw_params_get_format(hwparams, &sample_spec->format);
698     AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
699                     snd_pcm_format_name((snd_pcm_format_t)sample_spec->format),
700                     snd_pcm_format_description((snd_pcm_format_t)sample_spec->format));
701
702     snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val);
703     AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
704                     snd_pcm_subformat_name((snd_pcm_subformat_t)val),
705                     snd_pcm_subformat_description((snd_pcm_subformat_t)val));
706
707     snd_pcm_hw_params_get_channels(hwparams, &channels);
708     sample_spec->channels = (uint8_t)channels;
709     AUDIO_LOG_DEBUG("channels = %d\n", sample_spec->channels);
710
711     if (buffer_size)
712         *buffer_size = _buffer_size;
713
714     if (period_size)
715         *period_size = _period_size;
716
717     if (use_mmap)
718         *use_mmap = _use_mmap;
719
720     return AUDIO_RET_OK;
721
722 error:
723     return AUDIO_ERR_RESOURCE;
724 }
725
726 audio_return_t _pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event)
727 {
728     snd_pcm_sw_params_t *swparams;
729     snd_pcm_uframes_t boundary;
730     int err;
731
732     AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
733
734     snd_pcm_sw_params_alloca(&swparams);
735
736     if ((err = snd_pcm_sw_params_current(pcm, swparams)) < 0) {
737         AUDIO_LOG_WARN("Unable to determine current swparams: %s\n", snd_strerror(err));
738         goto error;
739     }
740     if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, period_event)) < 0) {
741         AUDIO_LOG_WARN("Unable to disable period event: %s\n", snd_strerror(err));
742         goto error;
743     }
744     if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
745         AUDIO_LOG_WARN("Unable to enable time stamping: %s\n", snd_strerror(err));
746         goto error;
747     }
748     if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) {
749         AUDIO_LOG_WARN("Unable to get boundary: %s\n", snd_strerror(err));
750         goto error;
751     }
752     if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) {
753         AUDIO_LOG_WARN("Unable to set stop threshold: %s\n", snd_strerror(err));
754         goto error;
755     }
756     if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) avail_min)) < 0) {
757         AUDIO_LOG_WARN("Unable to set start threshold: %s\n", snd_strerror(err));
758         goto error;
759     }
760     if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) {
761         AUDIO_LOG_WARN("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err));
762         goto error;
763     }
764     if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) {
765         AUDIO_LOG_WARN("Unable to set sw params: %s\n", snd_strerror(err));
766         goto error;
767     }
768     return AUDIO_RET_OK;
769 error:
770     return err;
771 }