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