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