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