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