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