e8872ea4b3e422b28ae68e28ff207d6d376f878c
[platform/adaptation/spreadtrum/audio-hal-sc7727.git] / tizen-audio-device.c
1 /*
2  * audio-hal
3  *
4  * Copyright (c) 2000 - 2013 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
31 /* #define DEBUG_TIMING */
32
33 static device_type_t outDeviceTypes[] = {
34     { AUDIO_DEVICE_OUT_SPEAKER, "Speaker" },
35     { AUDIO_DEVICE_OUT_RECEIVER, "Earpiece" },
36     { AUDIO_DEVICE_OUT_JACK, "Headphones" },
37     { AUDIO_DEVICE_OUT_BT_SCO, "Bluetooth" },
38     { 0, 0 },
39 };
40
41 static device_type_t inDeviceTypes[] = {
42     { AUDIO_DEVICE_IN_MAIN_MIC, "MainMic" },
43     { AUDIO_DEVICE_IN_SUB_MIC, "SubMic" },
44     { AUDIO_DEVICE_IN_JACK, "HeadsetMic" },
45     { AUDIO_DEVICE_IN_BT_SCO, "BT Mic" },
46     { 0, 0 },
47 };
48
49 static int _voice_pcm_open(audio_hal_t *ah);
50 static int _voice_pcm_close(audio_hal_t *ah, uint32_t direction);
51 static void _reset_pcm_devices(audio_hal_t *ah);
52 static void _reset_voice_devices_info(audio_hal_t *ah);
53
54 static uint32_t convert_device_string_to_enum(const char* device_str, uint32_t direction)
55 {
56     uint32_t device = 0;
57
58     if (!strncmp(device_str, "builtin-speaker", MAX_NAME_LEN)) {
59         device = AUDIO_DEVICE_OUT_SPEAKER;
60     } else if (!strncmp(device_str, "builtin-receiver", MAX_NAME_LEN)) {
61         device = AUDIO_DEVICE_OUT_RECEIVER;
62     } else if ((!strncmp(device_str, "audio-jack", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_OUT)) {
63         device = AUDIO_DEVICE_OUT_JACK;
64     } else if ((!strncmp(device_str, "bt", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_OUT)) {
65         device = AUDIO_DEVICE_OUT_BT_SCO;
66     } else if ((!strncmp(device_str, "builtin-mic", MAX_NAME_LEN))) {
67         device = AUDIO_DEVICE_IN_MAIN_MIC;
68     /* To Do : SUB_MIC */
69     } else if ((!strncmp(device_str, "audio-jack", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_IN)) {
70         device = AUDIO_DEVICE_IN_JACK;
71     } else if ((!strncmp(device_str, "bt", MAX_NAME_LEN)) && (direction == AUDIO_DIRECTION_IN)) {
72         device = AUDIO_DEVICE_IN_BT_SCO;
73     } else {
74         device = AUDIO_DEVICE_NONE;
75     }
76     AUDIO_LOG_INFO("device type(%s), enum(0x%x)", device_str, device);
77     return device;
78 }
79
80 static audio_return_t set_devices(audio_hal_t *ah, const char *verb, device_info_t *devices, uint32_t num_of_devices)
81 {
82     audio_return_t audio_ret = AUDIO_RET_OK;
83     uint32_t new_device = 0;
84     const char *active_devices[MAX_DEVICES] = {NULL,};
85     int i = 0, j = 0, dev_idx = 0;
86
87     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
88     AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER);
89     AUDIO_RETURN_VAL_IF_FAIL(num_of_devices, AUDIO_ERR_PARAMETER);
90
91     if (num_of_devices > MAX_DEVICES) {
92         num_of_devices = MAX_DEVICES;
93         AUDIO_LOG_ERROR("error: num_of_devices");
94         return AUDIO_ERR_PARAMETER;
95     }
96
97     if ((devices[0].direction == AUDIO_DIRECTION_OUT) && ah->device.active_in) {
98         /* check the active in devices */
99         for (j = 0; j < inDeviceTypes[j].type; j++) {
100             if (((ah->device.active_in & (~AUDIO_DEVICE_IN)) & inDeviceTypes[j].type))
101                 active_devices[dev_idx++] = inDeviceTypes[j].name;
102         }
103     } else if ((devices[0].direction == AUDIO_DIRECTION_IN) && ah->device.active_out) {
104         /* check the active out devices */
105         for (j = 0; j < outDeviceTypes[j].type; j++) {
106             if (ah->device.active_out & outDeviceTypes[j].type)
107                 active_devices[dev_idx++] = outDeviceTypes[j].name;
108         }
109     }
110
111     for (i = 0; i < num_of_devices; i++) {
112         new_device = convert_device_string_to_enum(devices[i].type, devices[i].direction);
113         if (new_device & AUDIO_DEVICE_IN) {
114             for (j = 0; j < inDeviceTypes[j].type; j++) {
115                 if (new_device == inDeviceTypes[j].type) {
116                     active_devices[dev_idx++] = inDeviceTypes[j].name;
117                     ah->device.active_in |= new_device;
118                 }
119             }
120         } else {
121             for (j = 0; j < outDeviceTypes[j].type; j++) {
122                 if (new_device == outDeviceTypes[j].type) {
123                     active_devices[dev_idx++] = outDeviceTypes[j].name;
124                     ah->device.active_out |= new_device;
125                 }
126             }
127         }
128     }
129
130     if (active_devices[0] == NULL) {
131         AUDIO_LOG_ERROR("Failed to set device: active device is NULL");
132         return AUDIO_ERR_PARAMETER;
133     }
134
135     audio_ret = _audio_ucm_set_devices(ah, verb, active_devices);
136     if (audio_ret)
137         AUDIO_LOG_ERROR("Failed to set device: error = %d", audio_ret);
138
139     return audio_ret;
140 }
141
142 audio_return_t _audio_device_init(audio_hal_t *ah)
143 {
144     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
145
146     ah->device.active_in = 0x0;
147     ah->device.active_out = 0x0;
148     ah->device.pcm_in = NULL;
149     ah->device.pcm_out = NULL;
150     ah->device.mode = VERB_NORMAL;
151     pthread_mutex_init(&ah->device.pcm_lock, NULL);
152     pthread_mutex_init(&ah->device.device_lock, NULL);
153     pthread_cond_init(&ah->device.device_cond, NULL);
154     ah->device.pcm_count = 0;
155
156     return AUDIO_RET_OK;
157 }
158
159 audio_return_t _audio_device_deinit(audio_hal_t *ah)
160 {
161     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
162
163     pthread_mutex_destroy(&ah->device.pcm_lock);
164     pthread_mutex_destroy(&ah->device.device_lock);
165     pthread_cond_destroy(&ah->device.device_cond);
166     return AUDIO_RET_OK;
167 }
168
169 static audio_return_t _do_route_ap_playback_capture(audio_hal_t *ah, audio_route_info_t *route_info)
170 {
171     audio_return_t audio_ret = AUDIO_RET_OK;
172     device_info_t *devices = NULL;
173     const char *verb = NULL;
174 #if 0  /* Disable setting modifiers, because driver does not support it yet */
175     int mod_idx = 0;
176     const char *modifiers[MAX_MODIFIERS] = {NULL,};
177 #endif
178
179     if (ah->device.mode != VERB_NORMAL) {
180         if (ah->device.mode == VERB_CALL) {
181             _reset_voice_devices_info(ah);
182             COND_SIGNAL(ah->device.device_cond, "device_cond");
183         }
184         _reset_pcm_devices(ah);
185         ah->device.mode = VERB_NORMAL;
186     }
187
188     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
189     AUDIO_RETURN_VAL_IF_FAIL(route_info, AUDIO_ERR_PARAMETER);
190
191     devices = route_info->device_infos;
192
193     verb = AUDIO_USE_CASE_VERB_HIFI;
194     AUDIO_LOG_INFO("do_route_ap_playback_capture++ ");
195
196     audio_ret = set_devices(ah, verb, devices, route_info->num_of_devices);
197     if (audio_ret) {
198         AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret);
199         return audio_ret;
200     }
201     ah->device.mode = VERB_NORMAL;
202
203 #if 0 /* Disable setting modifiers, because driver does not support it yet */
204     /* Set modifiers */
205     if (!strncmp("voice_recognition", route_info->role, MAX_NAME_LEN)) {
206         modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_VOICESEARCH;
207     } else if ((!strncmp("alarm", route_info->role, MAX_NAME_LEN))||(!strncmp("notifiication", route_info->role, MAX_NAME_LEN))) {
208         if (ah->device.active_out &= AUDIO_DEVICE_OUT_JACK)
209             modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_DUAL_MEDIA;
210         else
211             modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_MEDIA;
212     } else if (!strncmp("ringtone", route_info->role, MAX_NAME_LEN)) {
213         if (ah->device.active_out)
214             modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_RINGTONE;
215     } else {
216         if (ah->device.active_in)
217             modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_CAMCORDING;
218         else
219             modifiers[mod_idx++] = AUDIO_USE_CASE_MODIFIER_MEDIA;
220     }
221     audio_ret = _audio_ucm_set_modifiers (ah, verb, modifiers);
222 #endif
223     return audio_ret;
224 }
225
226 static audio_return_t _do_route_voicecall(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices)
227 {
228     audio_return_t audio_ret = AUDIO_RET_OK;
229     const char *verb = AUDIO_USE_CASE_VERB_VOICECALL;
230
231     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
232     /* if both params are 0, return error for invalid state,
233          * this error would be used to tizen-audio-modem.c */
234     AUDIO_RETURN_VAL_IF_FAIL((devices||num_of_devices), AUDIO_ERR_INVALID_STATE);
235     AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER);
236
237     AUDIO_LOG_INFO("do_route_voicecall++");
238
239     audio_ret = set_devices(ah, verb, devices, num_of_devices);
240     if (audio_ret) {
241         AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret);
242         return audio_ret;
243     }
244
245     if (ah->device.mode != VERB_CALL) {
246         _voice_pcm_open(ah);
247         ah->device.mode = VERB_CALL;
248         /* FIXME. Get network info and configure rate in pcm device */
249     }
250
251     return audio_ret;
252 }
253
254 static audio_return_t _do_route_voip(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices)
255 {
256     audio_return_t audio_ret = AUDIO_RET_OK;
257     const char *verb = NULL;
258     verb = AUDIO_USE_CASE_VERB_HIFI;
259
260     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
261     AUDIO_RETURN_VAL_IF_FAIL(devices, AUDIO_ERR_PARAMETER);
262
263     AUDIO_LOG_INFO("do_route_voip++");
264
265     audio_ret = set_devices(ah, verb, devices, num_of_devices);
266     if (audio_ret) {
267         AUDIO_LOG_ERROR("Failed to set devices: error = 0x%x", audio_ret);
268         return audio_ret;
269     }
270     /* FIXME. If necessary, set VERB_VOIP */
271     ah->device.mode = VERB_NORMAL;
272
273     /* TO DO: Set modifiers */
274     return audio_ret;
275 }
276
277 static audio_return_t _do_route_reset(audio_hal_t *ah, uint32_t direction)
278 {
279     audio_return_t audio_ret = AUDIO_RET_OK;
280
281     /* FIXME: If you need to reset, set verb inactive */
282     /* const char *verb = NULL; */
283     /* verb = AUDIO_USE_CASE_VERB_INACTIVE; */
284
285     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
286
287     AUDIO_LOG_INFO("do_route_reset++, direction(0x%x)", direction);
288
289     if (direction == AUDIO_DIRECTION_OUT) {
290         ah->device.active_out &= 0x0;
291     } else {
292         ah->device.active_in &= 0x0;
293     }
294     if (ah->device.mode == VERB_CALL) {
295         _voice_pcm_close(ah, direction);
296         if (!ah->device.active_in && !ah->device.active_out)
297             ah->device.mode = VERB_NORMAL;
298         _reset_voice_devices_info(ah);
299         COND_SIGNAL(ah->device.device_cond, "device_cond");
300     }
301
302     /* TO DO: Set Inactive */
303     return audio_ret;
304 }
305
306 audio_return_t audio_do_route(void *audio_handle, audio_route_info_t *info)
307 {
308     audio_return_t audio_ret = AUDIO_RET_OK;
309     audio_hal_t *ah = (audio_hal_t *)audio_handle;
310     device_info_t *devices = NULL;
311     uint32_t prev_size;
312
313     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
314     AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
315
316     AUDIO_LOG_INFO("role:%s", info->role);
317
318     devices = info->device_infos;
319
320     if (!strncmp("call-voice", info->role, MAX_NAME_LEN)) {
321         if (!ah->modem.is_connected) {
322             if (info->num_of_devices) {
323                 if (!ah->device.num_of_call_devices) {
324                     if ((ah->device.init_call_devices = (device_info_t*)calloc(info->num_of_devices, sizeof(device_info_t)))) {
325                         memcpy(ah->device.init_call_devices, devices, info->num_of_devices*sizeof(device_info_t));
326                         ah->device.num_of_call_devices = info->num_of_devices;
327                     } else {
328                         AUDIO_LOG_ERROR("failed to calloc");
329                         audio_ret = AUDIO_ERR_RESOURCE;
330                         goto ERROR;
331                     }
332                 } else if (ah->device.num_of_call_devices) {
333                     prev_size = ah->device.num_of_call_devices;
334                     ah->device.num_of_call_devices += info->num_of_devices;
335                     if ((ah->device.init_call_devices = (device_info_t*)realloc(ah->device.init_call_devices, sizeof(device_info_t)*ah->device.num_of_call_devices))) {
336                         memcpy((void*)&(ah->device.init_call_devices[prev_size]), devices, info->num_of_devices*sizeof(device_info_t));
337                     } else {
338                         AUDIO_LOG_ERROR("failed to realloc");
339                         audio_ret = AUDIO_ERR_RESOURCE;
340                         goto ERROR;
341                     }
342                 }
343             } else {
344                 AUDIO_LOG_ERROR("failed to do route for call-voice, num_of_devices is 0");
345                 audio_ret = AUDIO_ERR_PARAMETER;
346                 goto ERROR;
347         }
348             AUDIO_LOG_INFO("modem is not ready, skip...");
349         } else {
350             audio_ret = _do_route_voicecall(ah, devices, info->num_of_devices);
351             if (AUDIO_IS_ERROR(audio_ret)) {
352                 AUDIO_LOG_WARN("set voicecall route return 0x%x", audio_ret);
353             }
354             COND_SIGNAL(ah->device.device_cond, "device_cond");
355         }
356     } else if (!strncmp("voip", info->role, MAX_NAME_LEN)) {
357         audio_ret = _do_route_voip(ah, devices, info->num_of_devices);
358         if (AUDIO_IS_ERROR(audio_ret)) {
359             AUDIO_LOG_WARN("set voip route return 0x%x", audio_ret);
360         }
361     } else if (!strncmp("reset", info->role, MAX_NAME_LEN)) {
362         audio_ret = _do_route_reset(ah, devices->direction);
363         if (AUDIO_IS_ERROR(audio_ret)) {
364             AUDIO_LOG_WARN("set reset return 0x%x", audio_ret);
365         }
366     } else {
367         /* need to prepare for "alarm","notification","emergency","voice-information","voice-recognition","ringtone" */
368         audio_ret = _do_route_ap_playback_capture(ah, info);
369         if (AUDIO_IS_ERROR(audio_ret)) {
370             AUDIO_LOG_WARN("set playback route return 0x%x", audio_ret);
371         }
372     }
373
374 ERROR:
375     return audio_ret;
376 }
377
378 audio_return_t audio_update_stream_connection_info(void *audio_handle, audio_stream_info_t *info, uint32_t is_connected)
379 {
380     audio_return_t audio_ret = AUDIO_RET_OK;
381     audio_hal_t *ah = (audio_hal_t *)audio_handle;
382
383     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
384     AUDIO_RETURN_VAL_IF_FAIL(info, AUDIO_ERR_PARAMETER);
385
386     AUDIO_LOG_INFO("role:%s, direction:%u, idx:%u, is_connected:%d", info->role, info->direction, info->idx, is_connected);
387
388     return audio_ret;
389 }
390
391 audio_return_t audio_update_route_option(void *audio_handle, audio_route_option_t *option)
392 {
393     audio_return_t audio_ret = AUDIO_RET_OK;
394     audio_hal_t *ah = (audio_hal_t *)audio_handle;
395
396     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
397     AUDIO_RETURN_VAL_IF_FAIL(option, AUDIO_ERR_PARAMETER);
398
399     AUDIO_LOG_INFO("role:%s, name:%s, value:%d", option->role, option->name, option->value);
400
401     return audio_ret;
402 }
403
404 static int __voice_pcm_set_params(audio_hal_t *ah, snd_pcm_t *pcm)
405 {
406     snd_pcm_hw_params_t *params = NULL;
407     int err = 0;
408     unsigned int val = 0;
409
410     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
411     AUDIO_RETURN_VAL_IF_FAIL(pcm, AUDIO_ERR_PARAMETER);
412
413     /* Skip parameter setting to null device. */
414     if (snd_pcm_type(pcm) == SND_PCM_TYPE_NULL)
415         return AUDIO_ERR_IOCTL;
416
417     /* Allocate a hardware parameters object. */
418     snd_pcm_hw_params_alloca(&params);
419
420     /* Fill it in with default values. */
421     if (snd_pcm_hw_params_any(pcm, params) < 0) {
422         AUDIO_LOG_ERROR("snd_pcm_hw_params_any() : failed! - %s\n", snd_strerror(err));
423         goto error;
424     }
425
426     /* Set the desired hardware parameters. */
427     /* Interleaved mode */
428     err = snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED);
429     if (err < 0) {
430         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() : failed! - %s\n", snd_strerror(err));
431         goto error;
432     }
433     err = snd_pcm_hw_params_set_rate(pcm, params, 8000, 0);
434     if (err < 0) {
435         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() : failed! - %s\n", snd_strerror(err));
436     }
437     err = snd_pcm_hw_params(pcm, params);
438     if (err < 0) {
439         AUDIO_LOG_ERROR("snd_pcm_hw_params() : failed! - %s\n", snd_strerror(err));
440         goto error;
441     }
442
443     /* Dump current param */
444     snd_pcm_hw_params_get_access(params, (snd_pcm_access_t *) &val);
445     AUDIO_LOG_DEBUG("access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));
446
447     snd_pcm_hw_params_get_format(params, (snd_pcm_format_t*)&val);
448     AUDIO_LOG_DEBUG("format = '%s' (%s)\n",
449                     snd_pcm_format_name((snd_pcm_format_t)val),
450                     snd_pcm_format_description((snd_pcm_format_t)val));
451
452     snd_pcm_hw_params_get_subformat(params, (snd_pcm_subformat_t *)&val);
453     AUDIO_LOG_DEBUG("subformat = '%s' (%s)\n",
454                     snd_pcm_subformat_name((snd_pcm_subformat_t)val),
455                     snd_pcm_subformat_description((snd_pcm_subformat_t)val));
456
457     snd_pcm_hw_params_get_channels(params, &val);
458     AUDIO_LOG_DEBUG("channels = %d\n", val);
459
460     return 0;
461
462 error:
463     return -1;
464 }
465
466 static int _voice_pcm_open(audio_hal_t *ah)
467 {
468     int err, ret = 0;
469
470     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
471
472     AUDIO_LOG_INFO("open voice pcm handles");
473
474     /* Get playback voice-pcm from ucm conf. Open and set-params */
475     if ((err = snd_pcm_open((snd_pcm_t **)&ah->device.pcm_out, VOICE_PCM_DEVICE, AUDIO_DIRECTION_OUT, 0)) < 0) {
476         AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %s", VOICE_PCM_DEVICE, snd_strerror(err));
477         return AUDIO_ERR_IOCTL;
478     }
479     ret = __voice_pcm_set_params(ah, ah->device.pcm_out);
480
481     AUDIO_LOG_INFO("pcm playback device open success device(%s)", VOICE_PCM_DEVICE);
482
483     /* Get capture voice-pcm from ucm conf. Open and set-params */
484     if ((err = snd_pcm_open((snd_pcm_t **)&ah->device.pcm_in, VOICE_PCM_DEVICE, AUDIO_DIRECTION_IN, 0)) < 0) {
485         AUDIO_LOG_ERROR("snd_pcm_open for %s failed. %s", VOICE_PCM_DEVICE, snd_strerror(err));
486         return AUDIO_ERR_IOCTL;
487     }
488     ret = __voice_pcm_set_params(ah, ah->device.pcm_in);
489     AUDIO_LOG_INFO("pcm captures device open success device(%s)", VOICE_PCM_DEVICE);
490
491     return ret;
492 }
493
494 static int _voice_pcm_close(audio_hal_t *ah, uint32_t direction)
495 {
496     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
497
498     AUDIO_LOG_INFO("close voice pcm handles");
499
500     if (ah->device.pcm_out && (direction == AUDIO_DIRECTION_OUT)) {
501         audio_pcm_close((void *)ah, ah->device.pcm_out);
502         ah->device.pcm_out = NULL;
503         AUDIO_LOG_INFO("voice pcm_out handle close success");
504     } else if (ah->device.pcm_in && (direction == AUDIO_DIRECTION_IN)) {
505         audio_pcm_close((void *)ah, ah->device.pcm_in);
506         ah->device.pcm_in = NULL;
507         AUDIO_LOG_INFO("voice pcm_in handle close success");
508     }
509
510     return AUDIO_RET_OK;
511 }
512
513 static void _reset_pcm_devices(audio_hal_t *ah)
514 {
515     AUDIO_RETURN_IF_FAIL(ah);
516
517     if (ah->device.pcm_out) {
518         audio_pcm_close((void *)ah, ah->device.pcm_out);
519         ah->device.pcm_out = NULL;
520         AUDIO_LOG_INFO("pcm_out handle close success");
521     }
522     if (ah->device.pcm_in) {
523         audio_pcm_close((void *)ah, ah->device.pcm_in);
524         ah->device.pcm_in = NULL;
525         AUDIO_LOG_INFO("pcm_in handle close success");
526     }
527
528     return;
529 }
530
531 static void _reset_voice_devices_info(audio_hal_t *ah)
532 {
533     AUDIO_RETURN_IF_FAIL(ah);
534
535     AUDIO_LOG_INFO("reset voice device info");
536     if (ah->device.init_call_devices) {
537         free(ah->device.init_call_devices);
538         ah->device.init_call_devices = NULL;
539         ah->device.num_of_call_devices = 0;
540     }
541
542     return;
543 }
544
545 #ifdef __USE_TINYALSA__
546 static struct pcm *__tinyalsa_open_device(audio_pcm_sample_spec_t *ss, size_t period_size, size_t period_count, uint32_t direction)
547 {
548     struct pcm *pcm = NULL;
549     struct pcm_config config;
550
551     AUDIO_RETURN_NULL_IF_FAIL(ss);
552
553     config.channels          = ss->channels;
554     config.rate              = ss->rate;
555     config.period_size       = period_size;
556     config.period_count      = period_count;
557     config.format            = ss->format;
558     config.start_threshold   = period_size;
559     config.stop_threshold    = 0xFFFFFFFF;
560     config.silence_threshold = 0;
561
562     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);
563
564     pcm = pcm_open((direction == AUDIO_DIRECTION_OUT) ? PLAYBACK_CARD_ID : CAPTURE_CARD_ID,
565                    (direction == AUDIO_DIRECTION_OUT) ? PLAYBACK_PCM_DEVICE_ID : CAPTURE_PCM_DEVICE_ID,
566                    (direction == AUDIO_DIRECTION_OUT) ? PCM_OUT : PCM_IN,
567                    &config);
568     if (!pcm || !pcm_is_ready(pcm)) {
569         AUDIO_LOG_ERROR("Unable to open device (%s)", pcm_get_error(pcm));
570         pcm_close(pcm);
571         return NULL;
572     }
573
574     return pcm;
575 }
576 #endif
577
578 audio_return_t _audio_do_route_voicecall(audio_hal_t *ah, device_info_t *devices, int32_t num_of_devices)
579 {
580     return _do_route_voicecall(ah, devices, num_of_devices);
581 }
582
583 audio_return_t audio_pcm_open(void *audio_handle, void **pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
584 {
585 #ifdef __USE_TINYALSA__
586     audio_hal_t *ah;
587     audio_pcm_sample_spec_t *ss;
588     int err;
589
590     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
591     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
592     AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER);
593     AUDIO_RETURN_VAL_IF_FAIL((period_size > 0), AUDIO_ERR_PARAMETER);
594     AUDIO_RETURN_VAL_IF_FAIL((periods > 0), AUDIO_ERR_PARAMETER);
595
596     ah = (audio_hal_t *)audio_handle;
597     ss = (audio_pcm_sample_spec_t *)sample_spec;
598     ss->format = _convert_format((audio_sample_format_t)ss->format);
599
600     *pcm_handle = __tinyalsa_open_device(ss, (size_t)period_size, (size_t)periods, direction);
601     if (*pcm_handle == NULL) {
602         AUDIO_LOG_ERROR("Error opening PCM device");
603         return AUDIO_ERR_RESOURCE;
604     }
605
606     if ((err = pcm_prepare((struct pcm *)*pcm_handle)) != 0) {
607         AUDIO_LOG_ERROR("Error prepare PCM device : %d", err);
608     }
609
610     ah->device.pcm_count++;
611     AUDIO_LOG_INFO("Opening PCM handle 0x%x", *pcm_handle);
612 #else  /* alsa-lib */
613     audio_hal_t *ah;
614     int err, mode;
615     char *device_name = NULL;
616
617     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
618     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
619     AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER);
620     AUDIO_RETURN_VAL_IF_FAIL((period_size > 0), AUDIO_ERR_PARAMETER);
621     AUDIO_RETURN_VAL_IF_FAIL((periods > 0), AUDIO_ERR_PARAMETER);
622
623     ah = (audio_hal_t *)audio_handle;
624     mode =  SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT;
625
626     if (direction == AUDIO_DIRECTION_OUT)
627         device_name = PLAYBACK_PCM_DEVICE;
628     else if (direction == AUDIO_DIRECTION_IN)
629         device_name = CAPTURE_PCM_DEVICE;
630     else {
631         AUDIO_LOG_ERROR("Error get device_name, direction : %d", direction);
632         return AUDIO_ERR_RESOURCE;
633     }
634
635     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) {
636         AUDIO_LOG_ERROR("Error opening PCM device %s : %s", device_name, snd_strerror(err));
637         return AUDIO_ERR_RESOURCE;
638     }
639
640     if ((err = audio_pcm_set_params(audio_handle, *pcm_handle, direction, sample_spec, period_size, periods)) != AUDIO_RET_OK) {
641         AUDIO_LOG_ERROR("Failed to set pcm parameters : %d", err);
642         return err;
643     }
644
645     ah->device.pcm_count++;
646     AUDIO_LOG_INFO("Opening PCM handle 0x%x, PCM device %s", *pcm_handle, device_name);
647 #endif
648
649     return AUDIO_RET_OK;
650 }
651
652 audio_return_t audio_pcm_start(void *audio_handle, void *pcm_handle)
653 {
654     int err;
655
656     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
657     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
658
659 #ifdef __USE_TINYALSA__
660     if ((err = pcm_start(pcm_handle)) < 0) {
661         AUDIO_LOG_ERROR("Error starting PCM handle : %d", err);
662         return AUDIO_ERR_RESOURCE;
663     }
664 #else  /* alsa-lib */
665     if ((err = snd_pcm_start(pcm_handle)) < 0) {
666         AUDIO_LOG_ERROR("Error starting PCM handle : %s", snd_strerror(err));
667         return AUDIO_ERR_RESOURCE;
668     }
669 #endif
670
671     AUDIO_LOG_INFO("PCM handle 0x%x start", pcm_handle);
672     return AUDIO_RET_OK;
673 }
674
675 audio_return_t audio_pcm_stop(void *audio_handle, void *pcm_handle)
676 {
677     int err;
678
679     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
680     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
681
682 #ifdef __USE_TINYALSA__
683     if ((err = pcm_stop(pcm_handle)) < 0) {
684         AUDIO_LOG_ERROR("Error stopping PCM handle : %d", err);
685         return AUDIO_ERR_RESOURCE;
686     }
687 #else  /* alsa-lib */
688     if ((err = snd_pcm_drop(pcm_handle)) < 0) {
689         AUDIO_LOG_ERROR("Error stopping PCM handle : %s", snd_strerror(err));
690         return AUDIO_ERR_RESOURCE;
691     }
692 #endif
693
694     AUDIO_LOG_INFO("PCM handle 0x%x stop", pcm_handle);
695     return AUDIO_RET_OK;
696 }
697
698 audio_return_t audio_pcm_close(void *audio_handle, void *pcm_handle)
699 {
700     audio_hal_t *ah = (audio_hal_t *)audio_handle;
701     int err;
702
703     AUDIO_RETURN_VAL_IF_FAIL(ah, AUDIO_ERR_PARAMETER);
704     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
705
706     AUDIO_LOG_INFO("Try to close PCM handle 0x%x", pcm_handle);
707
708 #ifdef __USE_TINYALSA__
709     if ((err = pcm_close(pcm_handle)) < 0) {
710         AUDIO_LOG_ERROR("Error closing PCM handle : %d", err);
711         return AUDIO_ERR_RESOURCE;
712     }
713 #else  /* alsa-lib */
714     if ((err = snd_pcm_close(pcm_handle)) < 0) {
715         AUDIO_LOG_ERROR("Error closing PCM handle : %s", snd_strerror(err));
716         return AUDIO_ERR_RESOURCE;
717     }
718 #endif
719
720     pcm_handle = NULL;
721     ah->device.pcm_count--;
722     AUDIO_LOG_INFO("PCM handle close success (count:%d)", ah->device.pcm_count);
723
724     return AUDIO_RET_OK;
725 }
726
727 audio_return_t audio_pcm_avail(void *audio_handle, void *pcm_handle, uint32_t *avail)
728 {
729 #ifdef __USE_TINYALSA__
730     struct timespec tspec;
731     unsigned int frames_avail = 0;
732     int err;
733
734     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
735     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
736     AUDIO_RETURN_VAL_IF_FAIL(avail, AUDIO_ERR_PARAMETER);
737
738     err = pcm_get_htimestamp(pcm_handle, &frames_avail, &tspec);
739     if (err < 0) {
740         AUDIO_LOG_ERROR("Could not get avail and timespec at PCM handle 0x%x : %d", pcm_handle, err);
741         return AUDIO_ERR_IOCTL;
742     }
743
744 #ifdef DEBUG_TIMING
745     AUDIO_LOG_DEBUG("avail = %d", frames_avail);
746 #endif
747
748     *avail = (uint32_t)frames_avail;
749 #else  /* alsa-lib */
750     snd_pcm_sframes_t frames_avail;
751
752     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
753     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
754     AUDIO_RETURN_VAL_IF_FAIL(avail, AUDIO_ERR_PARAMETER);
755
756     if ((frames_avail = snd_pcm_avail(pcm_handle)) < 0) {
757         AUDIO_LOG_ERROR("Could not get avail at PCM handle 0x%x : %d", pcm_handle, frames_avail);
758         return AUDIO_ERR_IOCTL;
759     }
760
761 #ifdef DEBUG_TIMING
762     AUDIO_LOG_DEBUG("avail = %d", frames_avail);
763 #endif
764
765     *avail = (uint32_t)frames_avail;
766 #endif
767
768     return AUDIO_RET_OK;
769 }
770
771 audio_return_t audio_pcm_write(void *audio_handle, void *pcm_handle, const void *buffer, uint32_t frames)
772 {
773 #ifdef __USE_TINYALSA__
774     int err;
775
776     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
777     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
778
779     err = pcm_write(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
780     if (err < 0) {
781         AUDIO_LOG_ERROR("Failed to write pcm : %d", err);
782         return AUDIO_ERR_IOCTL;
783     }
784
785 #ifdef DEBUG_TIMING
786     AUDIO_LOG_DEBUG("audio_pcm_write = %d", frames);
787 #endif
788 #else  /* alsa-lib */
789     snd_pcm_sframes_t frames_written;
790
791     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
792
793     frames_written = snd_pcm_writei(pcm_handle, buffer, (snd_pcm_uframes_t) frames);
794     if (frames_written < 0) {
795         AUDIO_LOG_ERROR("Failed to write pcm : %d", frames_written);
796         return AUDIO_ERR_IOCTL;
797     }
798
799 #ifdef DEBUG_TIMING
800     AUDIO_LOG_DEBUG("audio_pcm_write = (%d / %d)", frames_written, frames);
801 #endif
802 #endif
803
804     return AUDIO_RET_OK;
805 }
806
807 audio_return_t audio_pcm_read(void *audio_handle, void *pcm_handle, void *buffer, uint32_t frames)
808 {
809 #ifdef __USE_TINYALSA__
810     int err;
811
812     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
813     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
814
815     err = pcm_read(pcm_handle, buffer, pcm_frames_to_bytes(pcm_handle, (unsigned int)frames));
816     if (err < 0) {
817         AUDIO_LOG_ERROR("Failed to read pcm : %d", err);
818         return AUDIO_ERR_IOCTL;
819     }
820
821 #ifdef DEBUG_TIMING
822     AUDIO_LOG_DEBUG("audio_pcm_read = %d", frames);
823 #endif
824 #else  /* alsa-lib */
825     snd_pcm_sframes_t frames_read;
826
827     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
828     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
829
830     frames_read = snd_pcm_readi(pcm_handle, buffer, (snd_pcm_uframes_t)frames);
831     if (frames_read < 0) {
832         AUDIO_LOG_ERROR("Failed to read pcm : %d", frames_read);
833         return AUDIO_ERR_IOCTL;
834     }
835
836 #ifdef DEBUG_TIMING
837     AUDIO_LOG_DEBUG("audio_pcm_read = (%d / %d)", frames_read, frames);
838 #endif
839 #endif
840
841     return AUDIO_RET_OK;
842 }
843
844 audio_return_t audio_pcm_get_fd(void *audio_handle, void *pcm_handle, int *fd)
845 {
846     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
847     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
848     AUDIO_RETURN_VAL_IF_FAIL(fd, AUDIO_ERR_PARAMETER);
849     /* we use an internal API of the (tiny)alsa library, so it causes warning message during compile */
850 #ifdef __USE_TINYALSA__
851     *fd = _pcm_poll_descriptor((struct pcm *)pcm_handle);
852 #else  /* alsa-lib */
853     *fd = _snd_pcm_poll_descriptor((snd_pcm_t *)pcm_handle);
854 #endif
855     return AUDIO_RET_OK;
856 }
857
858 #ifdef __USE_TINYALSA__
859 static int __tinyalsa_pcm_recover(struct pcm *pcm, int err)
860 {
861     if (err > 0)
862         err = -err;
863     if (err == -EINTR)  /* nothing to do, continue */
864         return 0;
865     if (err == -EPIPE) {
866         AUDIO_LOG_INFO("XRUN occurred");
867         err = pcm_prepare(pcm);
868         if (err < 0) {
869             AUDIO_LOG_ERROR("Could not recover from XRUN occurred, prepare failed : %d", err);
870             return err;
871         }
872         return 0;
873     }
874     if (err == -ESTRPIPE) {
875         /* tinyalsa does not support pcm resume, dont't care suspend case */
876         AUDIO_LOG_ERROR("Could not recover from suspend : %d", err);
877         return err;
878     }
879     return err;
880 }
881 #endif
882
883 audio_return_t audio_pcm_recover(void *audio_handle, void *pcm_handle, int revents)
884 {
885     int state, err;
886
887     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
888     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
889
890     if (revents & POLLERR)
891         AUDIO_LOG_DEBUG("Got POLLERR from ALSA");
892     if (revents & POLLNVAL)
893         AUDIO_LOG_DEBUG("Got POLLNVAL from ALSA");
894     if (revents & POLLHUP)
895         AUDIO_LOG_DEBUG("Got POLLHUP from ALSA");
896     if (revents & POLLPRI)
897         AUDIO_LOG_DEBUG("Got POLLPRI from ALSA");
898     if (revents & POLLIN)
899         AUDIO_LOG_DEBUG("Got POLLIN from ALSA");
900     if (revents & POLLOUT)
901         AUDIO_LOG_DEBUG("Got POLLOUT from ALSA");
902
903 #ifdef __USE_TINYALSA__
904     state = pcm_state(pcm_handle);
905     AUDIO_LOG_DEBUG("PCM state is %d", state);
906
907     switch (state) {
908         case PCM_STATE_XRUN:
909             if ((err = __tinyalsa_pcm_recover(pcm_handle, -EPIPE)) != 0) {
910                 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
911                 return AUDIO_ERR_IOCTL;
912             }
913             break;
914
915         case PCM_STATE_SUSPENDED:
916             if ((err = __tinyalsa_pcm_recover(pcm_handle, -ESTRPIPE)) != 0) {
917                 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
918                 return AUDIO_ERR_IOCTL;
919             }
920             break;
921
922         default:
923             pcm_stop(pcm_handle);
924             if ((err = pcm_prepare(pcm_handle)) < 0) {
925                 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with pcm_prepare() : %d", err);
926                 return AUDIO_ERR_IOCTL;
927             }
928     }
929 #else  /* alsa-lib */
930     state = snd_pcm_state(pcm_handle);
931     AUDIO_LOG_DEBUG("PCM state is %s", snd_pcm_state_name(state));
932
933     /* Try to recover from this error */
934
935     switch (state) {
936         case SND_PCM_STATE_XRUN:
937             if ((err = snd_pcm_recover(pcm_handle, -EPIPE, 1)) != 0) {
938                 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN : %d", err);
939                 return AUDIO_ERR_IOCTL;
940             }
941             break;
942
943         case SND_PCM_STATE_SUSPENDED:
944             if ((err = snd_pcm_recover(pcm_handle, -ESTRPIPE, 1)) != 0) {
945                 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED : %d", err);
946                 return AUDIO_ERR_IOCTL;
947             }
948             break;
949
950         default:
951             snd_pcm_drop(pcm_handle);
952             if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
953                 AUDIO_LOG_ERROR("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare() : %d", err);
954                 return AUDIO_ERR_IOCTL;
955             }
956             break;
957     }
958 #endif
959
960     AUDIO_LOG_DEBUG("audio_pcm_recover");
961     return AUDIO_RET_OK;
962 }
963
964 audio_return_t audio_pcm_get_params(void *audio_handle, void *pcm_handle, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods)
965 {
966 #ifdef __USE_TINYALSA__
967     audio_pcm_sample_spec_t *ss;
968     unsigned int _period_size, _buffer_size, _periods, _format, _rate, _channels;
969     unsigned int _start_threshold, _stop_threshold, _silence_threshold;
970     struct pcm_config *config;
971
972     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
973     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
974     AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER);
975     AUDIO_RETURN_VAL_IF_FAIL(period_size, AUDIO_ERR_PARAMETER);
976     AUDIO_RETURN_VAL_IF_FAIL(periods, AUDIO_ERR_PARAMETER);
977     ss = (audio_pcm_sample_spec_t *)*sample_spec;
978
979     /* we use an internal API of the tiny alsa library, so it causes warning message during compile */
980     _pcm_config(pcm_handle, &config);
981
982     *period_size = config->period_size;
983     *periods     = config->period_count;
984     _buffer_size = config->period_size * config->period_count;
985     ss->format   = config->format;
986     ss->rate     = config->rate;
987     ss->channels = config->channels;
988     _start_threshold   = config->start_threshold;
989     _stop_threshold    = config->stop_threshold;
990     _silence_threshold = config->silence_threshold;
991
992     AUDIO_LOG_DEBUG("audio_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);
993 #else  /* alsa-lib */
994     int err;
995     audio_pcm_sample_spec_t *ss;
996     int dir;
997     snd_pcm_uframes_t _period_size, _buffer_size;
998     snd_pcm_format_t _format;
999     unsigned int _rate, _channels;
1000     snd_pcm_uframes_t _start_threshold, _stop_threshold, _silence_threshold, _avail_min;
1001     unsigned int _periods;
1002     snd_pcm_hw_params_t *hwparams;
1003     snd_pcm_sw_params_t *swparams;
1004
1005     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
1006     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
1007     AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER);
1008     AUDIO_RETURN_VAL_IF_FAIL(period_size, AUDIO_ERR_PARAMETER);
1009     AUDIO_RETURN_VAL_IF_FAIL(periods, AUDIO_ERR_PARAMETER);
1010     ss = (audio_pcm_sample_spec_t *)*sample_spec;
1011
1012     snd_pcm_hw_params_alloca(&hwparams);
1013     snd_pcm_sw_params_alloca(&swparams);
1014
1015     if ((err = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) {
1016         AUDIO_LOG_ERROR("snd_pcm_hw_params_current() failed : %d", err);
1017         return AUDIO_ERR_PARAMETER;
1018     }
1019
1020     if ((err = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
1021         (err = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0 ||
1022         (err = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0 ||
1023         (err = snd_pcm_hw_params_get_format(hwparams, &_format)) < 0 ||
1024         (err = snd_pcm_hw_params_get_rate(hwparams, &_rate, &dir)) < 0 ||
1025         (err = snd_pcm_hw_params_get_channels(hwparams, &_channels)) < 0) {
1026         AUDIO_LOG_ERROR("snd_pcm_hw_params_get_{period_size|buffer_size|periods|format|rate|channels}() failed : %s", err);
1027         return AUDIO_ERR_PARAMETER;
1028     }
1029
1030     *period_size = _period_size;
1031     *periods     = _periods;
1032     ss->format   = _format;
1033     ss->rate     = _rate;
1034     ss->channels = _channels;
1035
1036     if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
1037         AUDIO_LOG_ERROR("snd_pcm_sw_params_current() failed : %d", err);
1038         return AUDIO_ERR_PARAMETER;
1039     }
1040
1041     if ((err = snd_pcm_sw_params_get_start_threshold(swparams, &_start_threshold)) < 0  ||
1042         (err = snd_pcm_sw_params_get_stop_threshold(swparams, &_stop_threshold)) < 0  ||
1043         (err = snd_pcm_sw_params_get_silence_threshold(swparams, &_silence_threshold)) < 0  ||
1044         (err = snd_pcm_sw_params_get_avail_min(swparams, &_avail_min)) < 0) {
1045         AUDIO_LOG_ERROR("snd_pcm_sw_params_get_{start_threshold|stop_threshold|silence_threshold|avail_min}() failed : %s", err);
1046     }
1047
1048     AUDIO_LOG_DEBUG("audio_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);
1049 #endif
1050
1051     return AUDIO_RET_OK;
1052 }
1053
1054 audio_return_t audio_pcm_set_params(void *audio_handle, void *pcm_handle, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods)
1055 {
1056 #ifdef __USE_TINYALSA__
1057     /* Parameters are only acceptable in pcm_open() function */
1058     AUDIO_LOG_DEBUG("audio_pcm_set_params");
1059 #else  /* alsa-lib */
1060     int err;
1061     audio_pcm_sample_spec_t ss;
1062     snd_pcm_uframes_t _buffer_size;
1063     snd_pcm_hw_params_t *hwparams;
1064     snd_pcm_sw_params_t *swparams;
1065
1066     AUDIO_RETURN_VAL_IF_FAIL(audio_handle, AUDIO_ERR_PARAMETER);
1067     AUDIO_RETURN_VAL_IF_FAIL(pcm_handle, AUDIO_ERR_PARAMETER);
1068     AUDIO_RETURN_VAL_IF_FAIL(sample_spec, AUDIO_ERR_PARAMETER);
1069     AUDIO_RETURN_VAL_IF_FAIL(period_size, AUDIO_ERR_PARAMETER);
1070     AUDIO_RETURN_VAL_IF_FAIL(periods, AUDIO_ERR_PARAMETER);
1071     ss = *(audio_pcm_sample_spec_t *)sample_spec;
1072
1073     snd_pcm_hw_params_alloca(&hwparams);
1074     snd_pcm_sw_params_alloca(&swparams);
1075
1076     /* Set hw params */
1077     if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
1078         AUDIO_LOG_ERROR("snd_pcm_hw_params_any() failed : %d", err);
1079         return AUDIO_ERR_PARAMETER;
1080     }
1081
1082     if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) {
1083         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate_resample() failed : %d", err);
1084         return AUDIO_ERR_PARAMETER;
1085     }
1086
1087     if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
1088         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_access() failed : %d", err);
1089         return AUDIO_ERR_PARAMETER;
1090     }
1091
1092     ss.format = _convert_format((audio_sample_format_t)ss.format);
1093     if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, ss.format)) < 0) {
1094         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_format() failed : %d", err);
1095         return AUDIO_ERR_PARAMETER;
1096     }
1097
1098     if ((err = snd_pcm_hw_params_set_rate(pcm_handle, hwparams, ss.rate, 0)) < 0) {
1099         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_rate() failed : %d", err);
1100         return AUDIO_ERR_PARAMETER;
1101     }
1102
1103     if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss.channels)) < 0) {
1104         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_channels(%u) failed : %d", err);
1105         return AUDIO_ERR_PARAMETER;
1106     }
1107
1108     if ((err = snd_pcm_hw_params_set_period_size(pcm_handle, hwparams, period_size, 0)) < 0) {
1109         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_period_size(%u) failed : %d", err);
1110         return AUDIO_ERR_PARAMETER;
1111     }
1112
1113     if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0)) < 0) {
1114         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_periods(%u) failed : %d", periods, err);
1115         return AUDIO_ERR_PARAMETER;
1116     }
1117
1118     _buffer_size = period_size * periods;
1119     if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, _buffer_size)) < 0) {
1120         AUDIO_LOG_ERROR("snd_pcm_hw_params_set_buffer_size(%u) failed : %d", periods * periods, err);
1121         return AUDIO_ERR_PARAMETER;
1122     }
1123
1124     if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
1125         AUDIO_LOG_ERROR("snd_pcm_hw_params failed : %d", err);
1126         return AUDIO_ERR_IOCTL;
1127     }
1128
1129     /* Set sw params */
1130     if ((err = snd_pcm_sw_params_current(pcm_handle, swparams)) < 0) {
1131         AUDIO_LOG_ERROR("Unable to determine current swparams : %d", err);
1132         return AUDIO_ERR_PARAMETER;
1133     }
1134
1135     if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) {
1136         AUDIO_LOG_ERROR("Unable to enable time stamping : %d", err);
1137         return AUDIO_ERR_PARAMETER;
1138     }
1139
1140     if ((err = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xFFFFFFFF)) < 0) {
1141         AUDIO_LOG_ERROR("Unable to set stop threshold : %d", err);
1142         return AUDIO_ERR_PARAMETER;
1143     }
1144
1145     if ((err = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, period_size / 2)) < 0) {
1146         AUDIO_LOG_ERROR("Unable to set start threshold : %d", err);
1147         return AUDIO_ERR_PARAMETER;
1148     }
1149
1150     if ((err = snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, 1024)) < 0) {
1151         AUDIO_LOG_ERROR("snd_pcm_sw_params_set_avail_min() failed : %d", err);
1152         return AUDIO_ERR_PARAMETER;
1153     }
1154
1155     if ((err = snd_pcm_sw_params(pcm_handle, swparams)) < 0) {
1156         AUDIO_LOG_ERROR("Unable to set sw params : %d", err);
1157         return AUDIO_ERR_IOCTL;
1158     }
1159
1160     /* Prepare device */
1161     if ((err = snd_pcm_prepare(pcm_handle)) < 0) {
1162         AUDIO_LOG_ERROR("snd_pcm_prepare() failed : %d", err);
1163         return AUDIO_ERR_IOCTL;
1164     }
1165
1166     AUDIO_LOG_DEBUG("audio_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);
1167 #endif
1168
1169     return AUDIO_RET_OK;
1170 }