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