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