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