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