Card and device arguments are added to pcm open API
[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_t 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_t *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_t _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_t *ss;
171
172     ss = (audio_pcm_sample_spec_t *)sample_spec;
173     ss->format = __convert_format((audio_sample_format_t)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_t 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_t _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 0x%x start", pcm_handle);
226     return AUDIO_RET_OK;
227 }
228
229 audio_return_t _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 0x%x stop", pcm_handle);
246     return AUDIO_RET_OK;
247 }
248
249 audio_return_t _pcm_close(void *pcm_handle)
250 {
251     int err;
252
253     AUDIO_LOG_INFO("Try to close PCM handle 0x%x", 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_t _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 0x%x : %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 0x%x : %d", 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_t _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 : %d", 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_t _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 : %d", 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_t _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_t _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_t _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_t *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_t *)*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 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);
485 #else  /* alsa-lib */
486     int err;
487     audio_pcm_sample_spec_t *ss;
488     int dir;
489     snd_pcm_uframes_t _period_size, _buffer_size;
490     snd_pcm_format_t _format;
491     unsigned int _rate, _channels;
492     snd_pcm_uframes_t _start_threshold, _stop_threshold, _silence_threshold, _avail_min;
493     unsigned int _periods;
494     snd_pcm_hw_params_t *hwparams;
495     snd_pcm_sw_params_t *swparams;
496
497     ss = (audio_pcm_sample_spec_t *)*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 : %s", 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 : %s", err);
533     }
534
535     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);
536 #endif
537
538     return AUDIO_RET_OK;
539 }
540
541 audio_return_t _pcm_set_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
542 {
543 #ifdef __USE_TINYALSA__
544     /* Parameters are only acceptable in pcm_open() function */
545     AUDIO_LOG_DEBUG("_pcm_set_params");
546 #else  /* alsa-lib */
547     int err;
548     audio_pcm_sample_spec_t ss;
549     snd_pcm_uframes_t _buffer_size;
550     snd_pcm_hw_params_t *hwparams;
551     snd_pcm_sw_params_t *swparams;
552
553     ss = *(audio_pcm_sample_spec_t *)sample_spec;
554
555     snd_pcm_hw_params_alloca(&hwparams);
556     snd_pcm_sw_params_alloca(&swparams);
557
558     /* Set hw params */
559     if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
560         AUDIO_LOG_ERROR("snd_pcm_hw_params_any() failed : %d", err);
561         return AUDIO_ERR_PARAMETER;
562     }
563
564     if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) {
565         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate_resample() failed : %d", err);
566         return AUDIO_ERR_PARAMETER;
567     }
568
569     if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
570         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() failed : %d", err);
571         return AUDIO_ERR_PARAMETER;
572     }
573
574     ss.format = __convert_format((audio_sample_format_t)ss.format);
575     if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, ss.format)) < 0) {
576         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_format() failed : %d", err);
577         return AUDIO_ERR_PARAMETER;
578     }
579
580     if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, ss.rate, 0)) < 0) {
581         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() failed : %d", err);
582         return AUDIO_ERR_PARAMETER;
583     }
584
585     if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss.channels)) < 0) {
586         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", ss.channels, err);
587         return AUDIO_ERR_PARAMETER;
588     }
589
590     if ((err = snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, period_size, 0)) < 0) {
591         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size(%u) failed : %d", period_size, err);
592         return AUDIO_ERR_PARAMETER;
593     }
594
595     if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0)) < 0) {
596         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods(%u) failed : %d", periods, err);
597         return AUDIO_ERR_PARAMETER;
598     }
599
600     _buffer_size = period_size * periods;
601     if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, _buffer_size)) < 0) {
602         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size(%u) failed : %d", _buffer_size, err);
603         return AUDIO_ERR_PARAMETER;
604     }
605
606     if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
607         AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err);
608         return AUDIO_ERR_IOCTL;
609     }
610
611     /* Set sw params */
612     if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
613         AUDIO_LOG_ERROR("Unable to determine current swparams : %d", err);
614         return AUDIO_ERR_PARAMETER;
615     }
616
617     if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
618         AUDIO_LOG_ERROR("Unable to enable time stamping : %d", err);
619         return AUDIO_ERR_PARAMETER;
620     }
621
622     if ((err = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xFFFFFFFF)) < 0) {
623         AUDIO_LOG_ERROR("Unable to set stop threshold : %d", err);
624         return AUDIO_ERR_PARAMETER;
625     }
626
627     if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, period_size / 2)) < 0) {
628         AUDIO_LOG_ERROR("Unable to set start threshold : %d", err);
629         return AUDIO_ERR_PARAMETER;
630     }
631
632     if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, 1024)) < 0) {
633         AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err);
634         return AUDIO_ERR_PARAMETER;
635     }
636
637     if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0) {
638         AUDIO_LOG_ERROR("Unable to set sw params : %d", err);
639         return AUDIO_ERR_IOCTL;
640     }
641
642     /* Prepare device */
643     if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
644         AUDIO_LOG_ERROR("snd_pcm_prepare() failed : %d", err);
645         return AUDIO_ERR_IOCTL;
646     }
647
648     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);
649 #endif
650
651     return AUDIO_RET_OK;
652 }
653
654 /* Generic snd pcm interface APIs */
655 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)
656 {
657     audio_return_t ret = AUDIO_RET_OK;
658     snd_pcm_hw_params_t *hwparams;
659     int err = 0;
660     int dir;
661     unsigned int val = 0;
662     snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
663     snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0;
664     uint8_t _use_mmap = use_mmap && *use_mmap;
665     uint32_t channels = 0;
666
667     AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
668
669     snd_pcm_hw_params_alloca(&hwparams);
670
671     /* Skip parameter setting to null device. */
672     if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
673         return AUDIO_ERR_IOCTL;
674
675     /* Allocate a hardware parameters object. */
676     snd_pcm_hw_params_alloca(&hwparams);
677
678     /* Fill it in with default values. */
679     if (snd_pcm_hw_params_any(pcm, hwparams) < 0) {
680         AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
681         goto error;
682     }
683
684     /* Set the desired hardware parameters. */
685
686     if (_use_mmap) {
687
688         if (snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
689
690             /* mmap() didn't work, fall back to interleaved */
691
692             if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
693                 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
694                 goto error;
695             }
696
697             _use_mmap = 0;
698         }
699
700     } else if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
701         AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
702         goto error;
703     }
704     AUDIO_LOG_DEBUG("setting rate - %d", sample_spec->rate);
705     err = snd_pcm_hw_params_set_rate(pcm, hwparams, sample_spec->rate, 0);
706     if (err < 0) {
707         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
708     }
709
710     err = snd_pcm_hw_params(pcm, hwparams);
711     if (err < 0) {
712         AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
713         goto error;
714     }
715
716     /* Dump current param */
717
718     if ((ret = snd_pcm_hw_params_current(pcm, hwparams)) < 0) {
719         AUDIO_LOG_INFO("snd_pcm_hw_params_current() failed: %s", snd_strerror(ret));
720         goto error;
721     }
722
723     if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
724         (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) {
725         AUDIO_LOG_INFO("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", snd_strerror(ret));
726         goto error;
727     }
728
729     snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val);
730     AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
731
732     snd_pcm_hw_params_get_format(hwparams, &sample_spec->format);
733     AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
734                     snd_pcm_format_name((snd_pcm_format_t)sample_spec->format),
735                     snd_pcm_format_description((snd_pcm_format_t)sample_spec->format));
736
737     snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val);
738     AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
739                     snd_pcm_subformat_name((snd_pcm_subformat_t)val),
740                     snd_pcm_subformat_description((snd_pcm_subformat_t)val));
741
742     snd_pcm_hw_params_get_channels(hwparams, &channels);
743     sample_spec->channels = (uint8_t)channels;
744     AUDIO_LOG_DEBUG("channels = %d\n", sample_spec->channels);
745
746     if (buffer_size)
747         *buffer_size = _buffer_size;
748
749     if (period_size)
750         *period_size = _period_size;
751
752     if (use_mmap)
753         *use_mmap = _use_mmap;
754
755     return AUDIO_RET_OK;
756
757 error:
758     return AUDIO_ERR_RESOURCE;
759 }
760
761 audio_return_t _pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event)
762 {
763     snd_pcm_sw_params_t *swparams;
764     snd_pcm_uframes_t boundary;
765     int err;
766
767     AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
768
769     snd_pcm_sw_params_alloca(&swparams);
770
771     if ((err = snd_pcm_sw_params_current(pcm, swparams)) < 0) {
772         AUDIO_LOG_WARN("Unable to determine current swparams: %s\n", snd_strerror(err));
773         goto error;
774     }
775     if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, period_event)) < 0) {
776         AUDIO_LOG_WARN("Unable to disable period event: %s\n", snd_strerror(err));
777         goto error;
778     }
779     if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
780         AUDIO_LOG_WARN("Unable to enable time stamping: %s\n", snd_strerror(err));
781         goto error;
782     }
783     if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) {
784         AUDIO_LOG_WARN("Unable to get boundary: %s\n", snd_strerror(err));
785         goto error;
786     }
787     if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) {
788         AUDIO_LOG_WARN("Unable to set stop threshold: %s\n", snd_strerror(err));
789         goto error;
790     }
791     if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) avail_min)) < 0) {
792         AUDIO_LOG_WARN("Unable to set start threshold: %s\n", snd_strerror(err));
793         goto error;
794     }
795     if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) {
796         AUDIO_LOG_WARN("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err));
797         goto error;
798     }
799     if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) {
800         AUDIO_LOG_WARN("Unable to set sw params: %s\n", snd_strerror(err));
801         goto error;
802     }
803     return AUDIO_RET_OK;
804 error:
805     return err;
806 }