Remove build warnings
[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 0x%x 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 0x%x 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 0x%x : %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 0x%x : %d", 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 : %d", 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 : %d", 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 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);
700 #else  /* alsa-lib */
701     int err;
702     audio_pcm_sample_spec_t *ss;
703     int dir;
704     snd_pcm_uframes_t _period_size, _buffer_size;
705     snd_pcm_format_t _format;
706     unsigned int _rate, _channels;
707     snd_pcm_uframes_t _start_threshold, _stop_threshold, _silence_threshold, _avail_min;
708     unsigned int _periods;
709     snd_pcm_hw_params_t *hwparams;
710     snd_pcm_sw_params_t *swparams;
711
712     ss = (audio_pcm_sample_spec_t *)*sample_spec;
713
714     snd_pcm_hw_params_alloca(&hwparams);
715     snd_pcm_sw_params_alloca(&swparams);
716
717     if ((err = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) {
718         AUDIO_LOG_ERROR("snd_pcm_hw_params_current() failed : %d", err);
719         return AUDIO_ERR_PARAMETER;
720     }
721
722     if ((err = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
723         (err = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0 ||
724         (err = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0 ||
725         (err = snd_pcm_hw_params_get_format(hwparams, &_format)) < 0 ||
726         (err = snd_pcm_hw_params_get_rate(hwparams, &_rate, &dir)) < 0 ||
727         (err = snd_pcm_hw_params_get_channels(hwparams, &_channels)) < 0) {
728         AUDIO_LOG_ERROR("snd_pcm_hw_params_get_{period_size|buffer_size|periods|format|rate|channels}() failed : %s", err);
729         return AUDIO_ERR_PARAMETER;
730     }
731
732     *period_size = _period_size;
733     *periods     = _periods;
734     ss->format   = _format;
735     ss->rate     = _rate;
736     ss->channels = _channels;
737
738     if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
739         AUDIO_LOG_ERROR("snd_pcm_sw_params_current() failed : %d", err);
740         return AUDIO_ERR_PARAMETER;
741     }
742
743     if ((err = snd_pcm_sw_params_get_start_threshold(swparams, &_start_threshold)) < 0  ||
744         (err = snd_pcm_sw_params_get_stop_threshold(swparams, &_stop_threshold)) < 0  ||
745         (err = snd_pcm_sw_params_get_silence_threshold(swparams, &_silence_threshold)) < 0  ||
746         (err = snd_pcm_sw_params_get_avail_min(swparams, &_avail_min)) < 0) {
747         AUDIO_LOG_ERROR("snd_pcm_sw_params_get_{start_threshold|stop_threshold|silence_threshold|avail_min}() failed : %s", err);
748     }
749
750     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);
751 #endif
752
753     return AUDIO_RET_OK;
754 }
755
756 audio_return_t _pcm_set_params(void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
757 {
758 #ifdef __USE_TINYALSA__
759     /* Parameters are only acceptable in pcm_open() function */
760     AUDIO_LOG_DEBUG("_pcm_set_params");
761 #else  /* alsa-lib */
762     int err;
763     audio_pcm_sample_spec_t ss;
764     snd_pcm_uframes_t _buffer_size;
765     snd_pcm_hw_params_t *hwparams;
766     snd_pcm_sw_params_t *swparams;
767
768     ss = *(audio_pcm_sample_spec_t *)sample_spec;
769
770     snd_pcm_hw_params_alloca(&hwparams);
771     snd_pcm_sw_params_alloca(&swparams);
772
773     /* Set hw params */
774     if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
775         AUDIO_LOG_ERROR("snd_pcm_hw_params_any() failed : %d", err);
776         return AUDIO_ERR_PARAMETER;
777     }
778
779     if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) {
780         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate_resample() failed : %d", err);
781         return AUDIO_ERR_PARAMETER;
782     }
783
784     if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
785         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() failed : %d", err);
786         return AUDIO_ERR_PARAMETER;
787     }
788
789     ss.format = __convert_format((audio_sample_format_t)ss.format);
790     if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, ss.format)) < 0) {
791         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_format() failed : %d", err);
792         return AUDIO_ERR_PARAMETER;
793     }
794
795     if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, ss.rate, 0)) < 0) {
796         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() failed : %d", err);
797         return AUDIO_ERR_PARAMETER;
798     }
799
800     if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss.channels)) < 0) {
801         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", ss.channels, err);
802         return AUDIO_ERR_PARAMETER;
803     }
804
805     if ((err = snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, period_size, 0)) < 0) {
806         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size(%u) failed : %d", period_size, err);
807         return AUDIO_ERR_PARAMETER;
808     }
809
810     if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0)) < 0) {
811         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods(%u) failed : %d", periods, err);
812         return AUDIO_ERR_PARAMETER;
813     }
814
815     _buffer_size = period_size * periods;
816     if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, _buffer_size)) < 0) {
817         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size(%u) failed : %d", _buffer_size, err);
818         return AUDIO_ERR_PARAMETER;
819     }
820
821     if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
822         AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err);
823         return AUDIO_ERR_IOCTL;
824     }
825
826     /* Set sw params */
827     if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
828         AUDIO_LOG_ERROR("Unable to determine current swparams : %d", err);
829         return AUDIO_ERR_PARAMETER;
830     }
831
832     if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
833         AUDIO_LOG_ERROR("Unable to enable time stamping : %d", err);
834         return AUDIO_ERR_PARAMETER;
835     }
836
837     if ((err = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xFFFFFFFF)) < 0) {
838         AUDIO_LOG_ERROR("Unable to set stop threshold : %d", err);
839         return AUDIO_ERR_PARAMETER;
840     }
841
842     if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, period_size / 2)) < 0) {
843         AUDIO_LOG_ERROR("Unable to set start threshold : %d", err);
844         return AUDIO_ERR_PARAMETER;
845     }
846
847     if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, 1024)) < 0) {
848         AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err);
849         return AUDIO_ERR_PARAMETER;
850     }
851
852     if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0) {
853         AUDIO_LOG_ERROR("Unable to set sw params : %d", err);
854         return AUDIO_ERR_IOCTL;
855     }
856
857     /* Prepare device */
858     if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
859         AUDIO_LOG_ERROR("snd_pcm_prepare() failed : %d", err);
860         return AUDIO_ERR_IOCTL;
861     }
862
863     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);
864 #endif
865
866     return AUDIO_RET_OK;
867 }
868
869 /* Generic snd pcm interface APIs */
870 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)
871 {
872     audio_return_t ret = AUDIO_RET_OK;
873     snd_pcm_hw_params_t *hwparams;
874     int err = 0;
875     int dir;
876     unsigned int val = 0;
877     snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
878     snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0;
879     uint8_t _use_mmap = use_mmap && *use_mmap;
880     uint32_t channels = 0;
881
882     AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
883
884     snd_pcm_hw_params_alloca(&hwparams);
885
886     /* Skip parameter setting to null device. */
887     if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
888         return AUDIO_ERR_IOCTL;
889
890     /* Allocate a hardware parameters object. */
891     snd_pcm_hw_params_alloca(&hwparams);
892
893     /* Fill it in with default values. */
894     if (snd_pcm_hw_params_any(pcm, hwparams) < 0) {
895         AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
896         goto error;
897     }
898
899     /* Set the desired hardware parameters. */
900
901     if (_use_mmap) {
902
903         if (snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
904
905             /* mmap() didn't work, fall back to interleaved */
906
907             if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
908                 AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
909                 goto error;
910             }
911
912             _use_mmap = 0;
913         }
914
915     } else if ((ret = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
916         AUDIO_LOG_DEBUG("snd_pcm_hw_params_set_access() failed: %s", snd_strerror(ret));
917         goto error;
918     }
919     AUDIO_LOG_DEBUG("setting rate - %d", sample_spec->rate);
920     err = snd_pcm_hw_params_set_rate(pcm, hwparams, sample_spec->rate, 0);
921     if (err < 0) {
922         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
923     }
924
925     err = snd_pcm_hw_params(pcm, hwparams);
926     if (err < 0) {
927         AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
928         goto error;
929     }
930
931     /* Dump current param */
932
933     if ((ret = snd_pcm_hw_params_current(pcm, hwparams)) < 0) {
934         AUDIO_LOG_INFO("snd_pcm_hw_params_current() failed: %s", snd_strerror(ret));
935         goto error;
936     }
937
938     if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
939         (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) {
940         AUDIO_LOG_INFO("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", snd_strerror(ret));
941         goto error;
942     }
943
944     snd_pcm_hw_params_get_access(hwparams, (snd_pcm_access_t *) &val);
945     AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
946
947     snd_pcm_hw_params_get_format(hwparams, &sample_spec->format);
948     AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
949                     snd_pcm_format_name((snd_pcm_format_t)sample_spec->format),
950                     snd_pcm_format_description((snd_pcm_format_t)sample_spec->format));
951
952     snd_pcm_hw_params_get_subformat(hwparams, (snd_pcm_subformat_t *)&val);
953     AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
954                     snd_pcm_subformat_name((snd_pcm_subformat_t)val),
955                     snd_pcm_subformat_description((snd_pcm_subformat_t)val));
956
957     snd_pcm_hw_params_get_channels(hwparams, &channels);
958     sample_spec->channels = (uint8_t)channels;
959     AUDIO_LOG_DEBUG("channels = %d\n", sample_spec->channels);
960
961     if (buffer_size)
962         *buffer_size = _buffer_size;
963
964     if (period_size)
965         *period_size = _period_size;
966
967     if (use_mmap)
968         *use_mmap = _use_mmap;
969
970     return AUDIO_RET_OK;
971
972 error:
973     return AUDIO_ERR_RESOURCE;
974 }
975
976 audio_return_t _pcm_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min, uint8_t period_event)
977 {
978     snd_pcm_sw_params_t *swparams;
979     snd_pcm_uframes_t boundary;
980     int err;
981
982     AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
983
984     snd_pcm_sw_params_alloca(&swparams);
985
986     if ((err = snd_pcm_sw_params_current(pcm, swparams)) < 0) {
987         AUDIO_LOG_WARN("Unable to determine current swparams: %s\n", snd_strerror(err));
988         goto error;
989     }
990     if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, period_event)) < 0) {
991         AUDIO_LOG_WARN("Unable to disable period event: %s\n", snd_strerror(err));
992         goto error;
993     }
994     if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
995         AUDIO_LOG_WARN("Unable to enable time stamping: %s\n", snd_strerror(err));
996         goto error;
997     }
998     if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) {
999         AUDIO_LOG_WARN("Unable to get boundary: %s\n", snd_strerror(err));
1000         goto error;
1001     }
1002     if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) {
1003         AUDIO_LOG_WARN("Unable to set stop threshold: %s\n", snd_strerror(err));
1004         goto error;
1005     }
1006     if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) avail_min)) < 0) {
1007         AUDIO_LOG_WARN("Unable to set start threshold: %s\n", snd_strerror(err));
1008         goto error;
1009     }
1010     if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) {
1011         AUDIO_LOG_WARN("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err));
1012         goto error;
1013     }
1014     if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) {
1015         AUDIO_LOG_WARN("Unable to set sw params: %s\n", snd_strerror(err));
1016         goto error;
1017     }
1018     return AUDIO_RET_OK;
1019 error:
1020     return err;
1021 }