hal: Support for usb audio features
Added support for usb audio feature and
related proxy device support
Change-Id: Ia64e9eff20fbbada6f08795686cdbc0ba462bafb
diff --git a/hal/Android.mk b/hal/Android.mk
index 5f5c7ae..b4527bf 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -34,6 +34,11 @@
LOCAL_SRC_FILES += audio_extn/fm.c
endif
+ifneq ($(strip $(AUDIO_FEATURE_DISABLED_USBAUDIO)),true)
+ LOCAL_CFLAGS += -DUSB_HEADSET_ENABLED
+ LOCAL_SRC_FILES += audio_extn/usb.c
+endif
+
LOCAL_SHARED_LIBRARIES := \
liblog \
libcutils \
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index b10a5e9..1500471 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -62,7 +62,7 @@
bool audio_extn_should_use_handset_anc(int in_channels)
{
- char prop_aanc[128] = "false";
+ char prop_aanc[PROPERTY_VALUE_MAX] = "false";
property_get("persist.aanc.enable", prop_aanc, "0");
if (!strncmp("true", prop_aanc, 4)) {
@@ -76,7 +76,7 @@
bool audio_extn_should_use_fb_anc(void)
{
- char prop_anc[128] = "feedforward";
+ char prop_anc[PROPERTY_VALUE_MAX] = "feedforward";
property_get("persist.headset.anc.type", prop_anc, "0");
if (!strncmp("feedback", prop_anc, sizeof("feedback"))) {
@@ -171,8 +171,10 @@
ret = str_parms_get_str(query, AUDIO_PARAMETER_CAN_OPEN_PROXY, value,
sizeof(value));
if (ret >= 0) {
- /* Todo: check if proxy is free by maintaining a state flag*/
- val = 1;
+ if (audio_extn_usb_is_proxy_inuse())
+ val = 0;
+ else
+ val = 1;
str_parms_add_int(reply, AUDIO_PARAMETER_CAN_OPEN_PROXY, val);
str = str_parms_to_str(reply);
}
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index c537b77..28c7883 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -44,4 +44,24 @@
int32_t audio_extn_set_afe_proxy_channel_mixer(struct audio_device *adev);
#endif
+#ifndef USB_HEADSET_ENABLED
+#define audio_extn_usb_init(adev) (0)
+#define audio_extn_usb_deinit() (0)
+#define audio_extn_usb_start_playback(adev) (0)
+#define audio_extn_usb_stop_playback() (0)
+#define audio_extn_usb_start_capture(adev) (0)
+#define audio_extn_usb_stop_capture() (0)
+#define audio_extn_usb_set_proxy_sound_card(sndcard_idx) (0)
+#define audio_extn_usb_is_proxy_inuse() (0)
+#else
+void audio_extn_usb_init(void *adev);
+void audio_extn_usb_deinit();
+void audio_extn_usb_start_playback(void *adev);
+void audio_extn_usb_stop_playback();
+void audio_extn_usb_start_capture(void *adev);
+void audio_extn_usb_stop_capture();
+void audio_extn_usb_set_proxy_sound_card(uint32_t sndcard_idx);
+bool audio_extn_usb_is_proxy_inuse();
+#endif
+
#endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/usb.c b/hal/audio_extn/usb.c
new file mode 100644
index 0000000..5b045ea
--- /dev/null
+++ b/hal/audio_extn/usb.c
@@ -0,0 +1,673 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_usb"
+#define LOG_NDEBUG 0
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <cutils/log.h>
+#include <cutils/str_parms.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <system/audio.h>
+#include <tinyalsa/asoundlib.h>
+
+#ifdef USB_HEADSET_ENABLED
+#define USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE 512
+#define USB_LOW_LATENCY_OUTPUT_PERIOD_COUNT 8
+#define USB_DEFAULT_OUTPUT_SAMPLING_RATE 48000
+
+#define USB_PROXY_DEFAULT_SAMPLING_RATE 48000
+#define USB_PROXY_OPEN_RETRY_COUNT 100
+#define USB_PROXY_OPEN_WAIT_TIME 20
+#define USB_PROXY_PERIOD_SIZE 3072
+#define USB_PROXY_RATE_8000 8000
+#define USB_PROXY_RATE_16000 16000
+#define USB_PROXY_RATE_48000 48000
+#define USB_PERIOD_SIZE 2048
+#define USB_BUFF_SIZE 2048
+#define AFE_PROXY_PERIOD_COUNT 32
+#define AFE_PROXY_PLAYBACK_DEVICE 8
+#define AFE_PROXY_CAPTURE_DEVICE 7
+
+struct usb_module {
+ uint32_t usb_card;
+ uint32_t proxy_card;
+ uint32_t usb_device_id;
+ uint32_t proxy_device_id;
+
+ int32_t channels_playback;
+ int32_t sample_rate_playback;
+ int32_t channels_record;
+ int32_t sample_rate_record;
+
+ bool is_playback_running;
+ bool is_record_running;
+
+ pthread_t usb_playback_thr;
+ pthread_t usb_record_thr;
+ pthread_mutex_t usb_playback_lock;
+ pthread_mutex_t usb_record_lock;
+
+ struct pcm *proxy_pcm_playback_handle;
+ struct pcm *usb_pcm_playback_handle;
+ struct pcm *proxy_pcm_record_handle;
+ struct pcm *usb_pcm_record_handle;
+ struct audio_device *adev;
+};
+
+static struct usb_module *usbmod = NULL;
+static pthread_once_t alloc_usbmod_once_ctl = PTHREAD_ONCE_INIT;
+
+struct pcm_config pcm_config_usbmod = {
+ .channels = 2,
+ .rate = USB_DEFAULT_OUTPUT_SAMPLING_RATE,
+ .period_size = USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE,
+ .period_count = USB_LOW_LATENCY_OUTPUT_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4,
+ .stop_threshold = INT_MAX,
+ .avail_min = USB_LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4,
+};
+
+static void usb_alloc()
+{
+ usbmod = calloc(1, sizeof(struct usb_module));
+}
+
+static int usb_get_numof_rates(char *rates_str)
+{
+ int i, size = 0;
+ char *next_sr_string, *temp_ptr;
+ next_sr_string = strtok_r(rates_str, " ,", &temp_ptr);
+
+ if (next_sr_string == NULL) {
+ ALOGE("%s: get_numof_rates: could not find rates string", __func__);
+ return (int)NULL;
+ }
+
+ for (i = 1; next_sr_string != NULL; i++) {
+ size ++;
+ next_sr_string = strtok_r(NULL, " ,.-", &temp_ptr);
+ }
+ return size;
+}
+
+static int usb_get_capability(char *type, int32_t *channels,
+ int32_t *sample_rate)
+{
+ ALOGD("%s: for %s", __func__, type);
+ long unsigned file_size;
+ FILE *fp;
+ char *buffer;
+ int32_t err = 1;
+ int32_t size = 0;
+ int32_t fd, i, channels_playback;
+ char *read_buf, *str_start, *channel_start, *rates_str, *rates_str_for_val,
+ *rates_str_start, *next_sr_str, *test, *next_sr_string, *temp_ptr;
+ struct stat st;
+ int rates_supported[size];
+ char path[128];
+
+ memset(&st, 0x0, sizeof(struct stat));
+ *sample_rate = 0;
+ snprintf(path, sizeof(path), "/proc/asound/card%u/stream0",
+ usbmod->usb_card);
+
+ fd = open(path, O_RDONLY);
+ if (fd <0) {
+ ALOGE("%s: error failed to open config file %s error: %d\n",
+ __func__, path, errno);
+ close(fd);
+ return -EINVAL;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ ALOGE("%s: error failed to stat %s error %d\n",
+ __func__, path, errno);
+ close(fd);
+ return -EINVAL;
+ }
+
+ file_size = st.st_size;
+
+ read_buf = (char *)calloc(1, USB_BUFF_SIZE);
+ err = read(fd, read_buf, USB_BUFF_SIZE);
+ str_start = strstr(read_buf, type);
+ if (str_start == NULL) {
+ ALOGE("%s: error %s section not found in usb config file",
+ __func__, type);
+ close(fd);
+ free(read_buf);
+ return -EINVAL;
+ }
+
+ channel_start = strstr(str_start, "Channels:");
+ if (channel_start == NULL) {
+ ALOGE("%s: error could not find Channels information", __func__);
+ close(fd);
+ free(read_buf);
+ return -EINVAL;
+ }
+
+ channel_start = strstr(channel_start, " ");
+ if (channel_start == NULL) {
+ ALOGE("%s: error channel section not found in usb config file",
+ __func__);
+ close(fd);
+ free(read_buf);
+ return -EINVAL;
+ }
+
+ channels_playback = atoi(channel_start);
+ if (channels_playback == 1) {
+ *channels = 1;
+ } else {
+ *channels = 2;
+ }
+
+ ALOGD("%s: channels supported by device: %d", __func__, *channels);
+ rates_str_start = strstr(str_start, "Rates:");
+ if (rates_str_start == NULL) {
+ ALOGE("%s: error cant find rates information", __func__);
+ close(fd);
+ free(read_buf);
+ return -EINVAL;
+ }
+
+ rates_str_start = strstr(rates_str_start, " ");
+ if (rates_str_start == NULL) {
+ ALOGE("%s: error channel section not found in usb config file",
+ __func__);
+ close(fd);
+ free(read_buf);
+ return -EINVAL;
+ }
+
+ char *target = strchr(rates_str_start, '\n');
+ if (target == NULL) {
+ ALOGE("%s: error end of line not found", __func__);
+ close(fd);
+ free(read_buf);
+ return -EINVAL;
+ }
+
+ size = target - rates_str_start;
+ if ((rates_str = (char *)malloc(size + 1)) == NULL) {
+ ALOGE("%s: error unable to allocate memory to hold sample rate strings",
+ __func__);
+ close(fd);
+ free(read_buf);
+ return -ENOMEM;
+ }
+
+ if ((rates_str_for_val = (char *)malloc(size + 1)) == NULL) {
+ ALOGE("%s: error unable to allocate memory to hold sample rate string",
+ __func__);
+ close(fd);
+ free(rates_str);
+ free(read_buf);
+ return -ENOMEM;
+ }
+
+ memcpy(rates_str, rates_str_start, size);
+ memcpy(rates_str_for_val, rates_str_start, size);
+ rates_str[size] = '\0';
+ rates_str_for_val[size] = '\0';
+
+ size = usb_get_numof_rates(rates_str);
+ if (!size) {
+ ALOGE("%s: error could not get rate size, returning", __func__);
+ close(fd);
+ free(rates_str_for_val);
+ free(rates_str);
+ free(read_buf);
+ return -EINVAL;
+ }
+
+ next_sr_string = strtok_r(rates_str_for_val, " ,", &temp_ptr);
+ if (next_sr_string == NULL) {
+ ALOGE("%s: error could not get first rate val", __func__);
+ close(fd);
+ free(rates_str_for_val);
+ free(rates_str);
+ free(read_buf);
+ return -EINVAL;
+ }
+
+ rates_supported[0] = atoi(next_sr_string);
+ ALOGD("%s: rates_supported[0] for playback: %d",
+ __func__, rates_supported[0]);
+ for (i = 1; i<size; i++) {
+ next_sr_string = strtok_r(NULL, " ,.-", &temp_ptr);
+ rates_supported[i] = atoi(next_sr_string);
+ ALOGD("rates_supported[%d] for playback: %d",i, rates_supported[i]);
+ }
+
+ for (i = 0; i<size; i++) {
+ if ((rates_supported[i] > *sample_rate) &&
+ (rates_supported[i] <= 48000)) {
+ /* Sample Rate should be one of the proxy supported rates only
+ This is because proxy port is used to read from/write to DSP */
+ if ((rates_supported[i] == USB_PROXY_RATE_8000) ||
+ (rates_supported[i] == USB_PROXY_RATE_16000) ||
+ (rates_supported[i] == USB_PROXY_RATE_48000)) {
+ *sample_rate = rates_supported[i];
+ }
+ }
+ }
+ ALOGD("%s: sample_rate: %d", __func__, *sample_rate);
+
+ close(fd);
+ free(rates_str_for_val);
+ free(rates_str);
+ free(read_buf);
+ return 0;
+}
+
+static int32_t usb_playback_entry(void *adev)
+{
+ unsigned char usbbuf[USB_PROXY_PERIOD_SIZE] = {0};
+ int32_t ret, bytes, proxy_open_retry_count;
+
+ ALOGD("%s: entry", __func__);
+ /* update audio device pointer */
+ usbmod->adev = (struct audio_device*)adev;
+ proxy_open_retry_count = USB_PROXY_OPEN_RETRY_COUNT;
+
+ /* get capabilities */
+ pthread_mutex_lock(&usbmod->usb_playback_lock);
+ ret = usb_get_capability((char *)"Playback:",
+ &usbmod->channels_playback, &usbmod->sample_rate_playback);
+ if (ret) {
+ ALOGE("%s: could not get playback capabilities from usb device",
+ __func__);
+ pthread_mutex_unlock(&usbmod->usb_playback_lock);
+ return -EINVAL;
+ }
+ /* update config for usb
+ 1 pcm frame(sample)= 4 bytes since two channels*/
+ pcm_config_usbmod.period_size = USB_PERIOD_SIZE/4;
+ pcm_config_usbmod.channels = usbmod->channels_playback;
+ pcm_config_usbmod.rate = usbmod->sample_rate_playback;
+ ALOGV("%s: usb device %u:period %u:channels %u:sample", __func__,
+ pcm_config_usbmod.period_size, pcm_config_usbmod.channels,
+ pcm_config_usbmod.rate);
+
+ usbmod->usb_pcm_playback_handle = pcm_open(usbmod->usb_card, \
+ usbmod->usb_device_id, PCM_OUT |
+ PCM_MMAP | PCM_NOIRQ , &pcm_config_usbmod);
+
+ if ((usbmod->usb_pcm_playback_handle \
+ && !pcm_is_ready(usbmod->usb_pcm_playback_handle))
+ || (!usbmod->is_playback_running)) {
+ ALOGE("%s: failed: %s", __func__,
+ pcm_get_error(usbmod->usb_pcm_playback_handle));
+ pcm_close(usbmod->usb_pcm_playback_handle);
+ usbmod->usb_pcm_playback_handle = NULL;
+ pthread_mutex_unlock(&usbmod->usb_playback_lock);
+ return -ENOMEM;
+ }
+ ALOGD("%s: USB configured for playback", __func__);
+
+ /* update config for proxy*/
+ pcm_config_usbmod.period_size = USB_PROXY_PERIOD_SIZE/3;
+ pcm_config_usbmod.rate = usbmod->sample_rate_playback;
+ pcm_config_usbmod.channels = usbmod->channels_playback;
+ pcm_config_usbmod.period_count = AFE_PROXY_PERIOD_COUNT;
+ usbmod->proxy_device_id = AFE_PROXY_PLAYBACK_DEVICE;
+ ALOGV("%s: proxy device %u:period %u:channels %u:sample", __func__,
+ pcm_config_usbmod.period_size, pcm_config_usbmod.channels,
+ pcm_config_usbmod.rate);
+
+ while(proxy_open_retry_count){
+ usbmod->proxy_pcm_playback_handle = pcm_open(usbmod->proxy_card,
+ usbmod->proxy_device_id, PCM_IN |
+ PCM_MMAP | PCM_NOIRQ, &pcm_config_usbmod);
+ if(!usbmod->proxy_pcm_playback_handle){
+ proxy_open_retry_count--;
+ usleep(USB_PROXY_OPEN_WAIT_TIME * 1000);
+ ALOGE("%s: pcm_open for proxy failed retrying = %d",
+ __func__, proxy_open_retry_count);
+ }
+ else{
+ break;
+ }
+ }
+
+ if ((usbmod->proxy_pcm_playback_handle
+ && !pcm_is_ready(usbmod->proxy_pcm_playback_handle))
+ || (!usbmod->is_playback_running)) {
+ ALOGE("%s: failed: %s", __func__,
+ pcm_get_error(usbmod->proxy_pcm_playback_handle));
+ pcm_close(usbmod->proxy_pcm_playback_handle);
+ usbmod->proxy_pcm_playback_handle = NULL;
+ pthread_mutex_unlock(&usbmod->usb_playback_lock);
+ return -ENOMEM;
+ }
+ ALOGD("%s: PROXY configured for playback", __func__);
+ pthread_mutex_unlock(&usbmod->usb_playback_lock);
+
+ /* main loop to read from proxy and write to usb */
+ while (usbmod->is_playback_running) {
+ /* read data from proxy */
+ ret = pcm_mmap_read(usbmod->proxy_pcm_playback_handle,
+ (void *)usbbuf, USB_PROXY_PERIOD_SIZE);
+ /* Write to usb */
+ ret = pcm_mmap_write(usbmod->usb_pcm_playback_handle,
+ (void *)usbbuf, USB_PROXY_PERIOD_SIZE);
+ if(!usbmod->is_playback_running)
+ break;
+
+ memset(usbbuf, 0, USB_PROXY_PERIOD_SIZE);
+ } /* main loop end */
+
+ ALOGD("%s: exiting USB playback thread",__func__);
+ return 0;
+}
+
+static void* usb_playback_launcher(void *adev)
+{
+ int32_t ret;
+
+ usbmod->is_playback_running = true;
+ ret = usb_playback_entry(adev);
+
+ if (ret) {
+ ALOGE("%s: failed with err:%d", __func__, ret);
+ usbmod->is_playback_running = false;
+ }
+ return NULL;
+}
+
+static int32_t usb_record_entry(void *adev)
+{
+ unsigned char usbbuf[USB_PROXY_PERIOD_SIZE] = {0};
+ int32_t ret, bytes, proxy_open_retry_count;
+ ALOGD("%s: entry", __func__);
+
+ /* update audio device pointer */
+ usbmod->adev = (struct audio_device*)adev;
+ proxy_open_retry_count = USB_PROXY_OPEN_RETRY_COUNT;
+
+ /* get capabilities */
+ pthread_mutex_lock(&usbmod->usb_record_lock);
+ ret = usb_get_capability((char *)"Capture:",
+ &usbmod->channels_record, &usbmod->sample_rate_record);
+ if (ret) {
+ ALOGE("%s: could not get capture capabilities from usb device",
+ __func__);
+ pthread_mutex_unlock(&usbmod->usb_record_lock);
+ return -EINVAL;
+ }
+ /* update config for usb
+ 1 pcm frame(sample)= 4 bytes since two channels*/
+ pcm_config_usbmod.period_size = USB_PERIOD_SIZE/4;
+ pcm_config_usbmod.channels = usbmod->channels_record;
+ pcm_config_usbmod.rate = usbmod->sample_rate_record;
+ ALOGV("%s: usb device %u:period %u:channels %u:sample", __func__,
+ pcm_config_usbmod.period_size, pcm_config_usbmod.channels,
+ pcm_config_usbmod.rate);
+
+ usbmod->usb_pcm_record_handle = pcm_open(usbmod->usb_card, \
+ usbmod->usb_device_id, PCM_IN |
+ PCM_MMAP | PCM_NOIRQ , &pcm_config_usbmod);
+
+ if ((usbmod->usb_pcm_record_handle \
+ && !pcm_is_ready(usbmod->usb_pcm_record_handle))
+ || (!usbmod->is_record_running)) {
+ ALOGE("%s: failed: %s", __func__,
+ pcm_get_error(usbmod->usb_pcm_record_handle));
+ pcm_close(usbmod->usb_pcm_record_handle);
+ usbmod->usb_pcm_record_handle = NULL;
+ pthread_mutex_unlock(&usbmod->usb_record_lock);
+ return -ENOMEM;
+ }
+ ALOGD("%s: USB configured for capture", __func__);
+
+ /* update config for proxy*/
+ pcm_config_usbmod.period_size = USB_PROXY_PERIOD_SIZE/4;
+ pcm_config_usbmod.rate = usbmod->sample_rate_record;
+ pcm_config_usbmod.channels = usbmod->channels_record;
+ pcm_config_usbmod.period_count = AFE_PROXY_PERIOD_COUNT * 2;
+ usbmod->proxy_device_id = AFE_PROXY_CAPTURE_DEVICE;
+ ALOGV("%s: proxy device %u:period %u:channels %u:sample", __func__,
+ pcm_config_usbmod.period_size, pcm_config_usbmod.channels,
+ pcm_config_usbmod.rate);
+
+ while(proxy_open_retry_count){
+ usbmod->proxy_pcm_record_handle = pcm_open(usbmod->proxy_card,
+ usbmod->proxy_device_id, PCM_OUT |
+ PCM_MMAP | PCM_NOIRQ, &pcm_config_usbmod);
+ if(!usbmod->proxy_pcm_record_handle){
+ proxy_open_retry_count--;
+ usleep(USB_PROXY_OPEN_WAIT_TIME * 1000);
+ ALOGE("%s: pcm_open for proxy(recording) failed retrying = %d",
+ __func__, proxy_open_retry_count);
+ }
+ else{
+ break;
+ }
+ }
+ if ((usbmod->proxy_pcm_record_handle
+ && !pcm_is_ready(usbmod->proxy_pcm_record_handle))
+ || (!usbmod->is_record_running)) {
+ ALOGE("%s: failed: %s", __func__,
+ pcm_get_error(usbmod->proxy_pcm_record_handle));
+ pcm_close(usbmod->proxy_pcm_record_handle);
+ usbmod->proxy_pcm_record_handle = NULL;
+ pthread_mutex_unlock(&usbmod->usb_record_lock);
+ return -ENOMEM;
+ }
+ ALOGD("%s: PROXY configured for capture", __func__);
+ pthread_mutex_unlock(&usbmod->usb_record_lock);
+
+ /* main loop to read from usb and write to proxy */
+ while (usbmod->is_record_running) {
+ /* read data from usb */
+ ret = pcm_mmap_read(usbmod->usb_pcm_record_handle,
+ (void *)usbbuf, USB_PROXY_PERIOD_SIZE);
+ /* Write to proxy */
+ ret = pcm_mmap_write(usbmod->proxy_pcm_record_handle,
+ (void *)usbbuf, USB_PROXY_PERIOD_SIZE);
+ if(!usbmod->is_record_running)
+ break;
+
+ memset(usbbuf, 0, USB_PROXY_PERIOD_SIZE);
+ } /* main loop end */
+
+ ALOGD("%s: exiting USB capture thread",__func__);
+ return 0;
+}
+
+static void* usb_capture_launcher(void *adev)
+{
+ int32_t ret;
+
+ usbmod->is_record_running = true;
+ ret = usb_record_entry(adev);
+
+ if (ret) {
+ ALOGE("%s: failed with err:%d", __func__, ret);
+ usbmod->is_record_running = false;
+ }
+ return NULL;
+}
+
+void audio_extn_usb_init(void *adev)
+{
+ pthread_once(&alloc_usbmod_once_ctl, usb_alloc);
+
+ usbmod->is_playback_running = false;
+ usbmod->is_record_running = false;
+
+ usbmod->usb_pcm_playback_handle = NULL;
+ usbmod->proxy_pcm_playback_handle = NULL;
+
+ usbmod->usb_pcm_record_handle = NULL;
+ usbmod->proxy_pcm_record_handle = NULL;
+
+ usbmod->usb_card = 1;
+ usbmod->usb_device_id = 0;
+ usbmod->proxy_card = 0;
+ usbmod->proxy_device_id = AFE_PROXY_PLAYBACK_DEVICE;
+ usbmod->adev = (struct audio_device*)adev;
+}
+
+void audio_extn_usb_deinit()
+{
+ if (NULL != usbmod){
+ free(usbmod);
+ usbmod = NULL;
+ }
+}
+
+void audio_extn_usb_set_proxy_sound_card(uint32_t sndcard_idx)
+{
+ /* Proxy port and USB headset are related to two different sound cards */
+ if (sndcard_idx == usbmod->usb_card) {
+ usbmod->usb_card = usbmod->proxy_card;
+ }
+
+ usbmod->proxy_card = sndcard_idx;
+}
+
+void audio_extn_usb_start_playback(void *adev)
+{
+ int32_t ret;
+
+ if (NULL == usbmod){
+ ALOGE("%s: USB device object is NULL", __func__);
+ return;
+ }
+
+ if (usbmod->is_playback_running){
+ ALOGE("%s: USB playback thread already running", __func__);
+ return;
+ }
+
+ ALOGD("%s: creating USB playback thread", __func__);
+ ret = pthread_create(&usbmod->usb_playback_thr, NULL,
+ usb_playback_launcher, (void*)adev);
+ if (ret)
+ ALOGE("%s: failed to create USB playback thread with err:%d",
+ __func__, ret);
+}
+
+void audio_extn_usb_stop_playback()
+{
+ int32_t ret;
+ ALOGD("%s: entry", __func__);
+
+ usbmod->is_playback_running = false;
+ if (NULL != usbmod->proxy_pcm_playback_handle)
+ pcm_stop(usbmod->proxy_pcm_playback_handle);
+
+ if (NULL != usbmod->usb_pcm_playback_handle)
+ pcm_stop(usbmod->usb_pcm_playback_handle);
+
+ if(usbmod->usb_playback_thr) {
+ ret = pthread_join(usbmod->usb_playback_thr,NULL);
+ ALOGE("%s: return for pthread_join = %d", __func__, ret);
+ usbmod->usb_playback_thr = (pthread_t)NULL;
+ }
+
+ pthread_mutex_lock(&usbmod->usb_playback_lock);
+ if (NULL != usbmod->usb_pcm_playback_handle){
+ pcm_close(usbmod->usb_pcm_playback_handle);
+ usbmod->usb_pcm_playback_handle = NULL;
+ }
+
+ if (NULL != usbmod->proxy_pcm_playback_handle){
+ pcm_close(usbmod->proxy_pcm_playback_handle);
+ usbmod->proxy_pcm_playback_handle = NULL;
+ }
+ pthread_mutex_unlock(&usbmod->usb_playback_lock);
+
+ ALOGD("%s: exiting",__func__);
+}
+
+void audio_extn_usb_start_capture(void *adev)
+{
+ int32_t ret;
+
+ if (NULL == usbmod){
+ ALOGE("%s: USB device object is NULL", __func__);
+ return;
+ }
+
+ if (usbmod->is_record_running){
+ ALOGE("%s: USB capture thread already running", __func__);
+ return;
+ }
+
+ ALOGD("%s: creating USB capture thread", __func__);
+ ret = pthread_create(&usbmod->usb_record_thr, NULL,
+ usb_capture_launcher, (void*)adev);
+ if (ret)
+ ALOGE("%s: failed to create USB capture thread with err:%d",
+ __func__, ret);
+}
+
+void audio_extn_usb_stop_capture()
+{
+ int32_t ret;
+ ALOGD("%s: entry", __func__);
+
+ usbmod->is_record_running = false;
+ if (NULL != usbmod->proxy_pcm_record_handle)
+ pcm_stop(usbmod->proxy_pcm_record_handle);
+
+ if (NULL != usbmod->usb_pcm_record_handle)
+ pcm_stop(usbmod->usb_pcm_record_handle);
+
+ if(usbmod->usb_record_thr) {
+ ret = pthread_join(usbmod->usb_record_thr,NULL);
+ ALOGE("%s: return for pthread_join = %d", __func__, ret);
+ usbmod->usb_record_thr = (pthread_t)NULL;
+ }
+
+ pthread_mutex_lock(&usbmod->usb_record_lock);
+ if (NULL != usbmod->usb_pcm_record_handle){
+ pcm_close(usbmod->usb_pcm_record_handle);
+ usbmod->usb_pcm_record_handle = NULL;
+ }
+
+ if (NULL != usbmod->proxy_pcm_record_handle){
+ pcm_close(usbmod->proxy_pcm_record_handle);
+ usbmod->proxy_pcm_record_handle = NULL;
+ }
+ pthread_mutex_unlock(&usbmod->usb_record_lock);
+
+ ALOGD("%s: exiting",__func__);
+}
+
+bool audio_extn_usb_is_proxy_inuse()
+{
+ if( usbmod->is_record_running || usbmod->is_playback_running)
+ return true;
+ else
+ return false;
+}
+#endif /*USB_HEADSET_ENABLED end*/
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index e80f88f..7a65925 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -186,6 +186,15 @@
return 0;
}
+ /* start usb playback thread */
+ if(SND_DEVICE_OUT_USB_HEADSET == snd_device ||
+ SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET == snd_device)
+ audio_extn_usb_start_playback(adev);
+
+ /* start usb capture thread */
+ if(SND_DEVICE_IN_USB_HEADSET_MIC == snd_device)
+ audio_extn_usb_start_capture(adev);
+
if (platform_send_audio_calibration(adev->platform, snd_device) < 0) {
adev->snd_dev_ref_cnt[snd_device]--;
return -EINVAL;
@@ -214,6 +223,16 @@
return -EINVAL;
}
adev->snd_dev_ref_cnt[snd_device]--;
+
+ /* exit usb play back thread */
+ if(SND_DEVICE_OUT_USB_HEADSET == snd_device ||
+ SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET == snd_device)
+ audio_extn_usb_stop_playback();
+
+ /* exit usb capture thread */
+ if(SND_DEVICE_IN_USB_HEADSET_MIC == snd_device)
+ audio_extn_usb_stop_capture(adev);
+
if (adev->snd_dev_ref_cnt[snd_device] == 0) {
ALOGV("%s: snd_device(%d: %s)", __func__,
snd_device, platform_get_snd_device_name(snd_device));
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 02940b4..310a048 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -115,6 +115,9 @@
[SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = "voice-tty-full-headphones",
[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones",
[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = "voice-tty-hco-handset",
+ [SND_DEVICE_OUT_AFE_PROXY] = "afe-proxy",
+ [SND_DEVICE_OUT_USB_HEADSET] = "usb-headphones",
+ [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = "speaker-and-usb-headphones",
[SND_DEVICE_OUT_TRANSMISSION_FM] = "transmission-fm",
[SND_DEVICE_OUT_ANC_HEADSET] = "anc-headphones",
[SND_DEVICE_OUT_ANC_FB_HEADSET] = "anc-fb-headphones",
@@ -144,6 +147,7 @@
[SND_DEVICE_IN_VOICE_REC_MIC] = "voice-rec-mic",
[SND_DEVICE_IN_VOICE_REC_DMIC] = "voice-rec-dmic-ef",
[SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = "voice-rec-dmic-ef-fluence",
+ [SND_DEVICE_IN_USB_HEADSET_MIC] = "usb-headset-mic",
[SND_DEVICE_IN_CAPTURE_FM] = "capture-fm",
[SND_DEVICE_IN_AANC_HANDSET_MIC] = "aanc-handset-mic",
};
@@ -166,6 +170,9 @@
[SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = 17,
[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17,
[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37,
+ [SND_DEVICE_OUT_AFE_PROXY] = 0,
+ [SND_DEVICE_OUT_USB_HEADSET] = 0,
+ [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = 14,
[SND_DEVICE_OUT_TRANSMISSION_FM] = 0,
[SND_DEVICE_OUT_ANC_HEADSET] = 26,
[SND_DEVICE_OUT_ANC_FB_HEADSET] = 26,
@@ -192,6 +199,7 @@
[SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = 36,
[SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = 16,
[SND_DEVICE_IN_VOICE_REC_MIC] = 62,
+ [SND_DEVICE_IN_USB_HEADSET_MIC] = 44,
[SND_DEVICE_IN_CAPTURE_FM] = 0,
[SND_DEVICE_IN_AANC_HANDSET_MIC] = 104,
/* TODO: Update with proper acdb ids */
@@ -263,7 +271,7 @@
{
char value[PROPERTY_VALUE_MAX];
struct platform_data *my_data;
- int retry_num = 0;
+ int retry_num = 0, ret;
adev->mixer = mixer_open(MIXER_CARD);
@@ -340,12 +348,17 @@
my_data->acdb_init();
}
+ /* init usb */
+ audio_extn_usb_init(adev);
+
return my_data;
}
void platform_deinit(void *platform)
{
free(platform);
+ /* deinit usb */
+ audio_extn_usb_deinit();
}
const char *platform_get_snd_device_name(snd_device_t snd_device)
@@ -366,6 +379,15 @@
strcat(mixer_path, " hdmi");
else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HDMI)
strcat(mixer_path, " speaker-and-hdmi");
+ else if (snd_device == SND_DEVICE_OUT_AFE_PROXY)
+ strlcat(mixer_path, " afe-proxy", MIXER_PATH_MAX_LENGTH);
+ else if (snd_device == SND_DEVICE_OUT_USB_HEADSET)
+ strlcat(mixer_path, " usb-headphones", MIXER_PATH_MAX_LENGTH);
+ else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET)
+ strlcat(mixer_path, " speaker-and-usb-headphones",
+ MIXER_PATH_MAX_LENGTH);
+ else if (snd_device == SND_DEVICE_IN_USB_HEADSET_MIC)
+ strlcat(mixer_path, " usb-headset-mic", MIXER_PATH_MAX_LENGTH);
else if (snd_device == SND_DEVICE_IN_CAPTURE_FM)
strlcat(mixer_path, " capture-fm", MIXER_PATH_MAX_LENGTH);
else if (snd_device == SND_DEVICE_OUT_TRANSMISSION_FM)
@@ -528,11 +550,6 @@
goto exit;
}
- if(devices & AUDIO_DEVICE_OUT_PROXY) {
- ALOGD("%s: setting sink capability for Proxy", __func__);
- audio_extn_set_afe_proxy_channel_mixer(adev);
- }
-
if (mode == AUDIO_MODE_IN_CALL) {
if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
@@ -554,6 +571,9 @@
snd_device = SND_DEVICE_OUT_BT_SCO;
} else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
snd_device = SND_DEVICE_OUT_VOICE_SPEAKER;
+ } else if (devices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET ||
+ devices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) {
+ snd_device = SND_DEVICE_OUT_USB_HEADSET;
} else if (devices & AUDIO_DEVICE_OUT_FM_TX) {
snd_device = SND_DEVICE_OUT_TRANSMISSION_FM;
} else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
@@ -582,6 +602,9 @@
} else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL |
AUDIO_DEVICE_OUT_SPEAKER)) {
snd_device = SND_DEVICE_OUT_SPEAKER_AND_HDMI;
+ } else if (devices == (AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
+ AUDIO_DEVICE_OUT_SPEAKER)) {
+ snd_device = SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET;
} else {
ALOGE("%s: Invalid combo device(%#x)", __func__, devices);
goto exit;
@@ -616,10 +639,17 @@
snd_device = SND_DEVICE_OUT_BT_SCO;
} else if (devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
snd_device = SND_DEVICE_OUT_HDMI ;
+ } else if (devices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET ||
+ devices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) {
+ snd_device = SND_DEVICE_OUT_USB_HEADSET;
} else if (devices & AUDIO_DEVICE_OUT_FM_TX) {
snd_device = SND_DEVICE_OUT_TRANSMISSION_FM;
} else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
snd_device = SND_DEVICE_OUT_HANDSET;
+ } else if (devices & AUDIO_DEVICE_OUT_PROXY) {
+ ALOGD("%s: setting sink capability for Proxy", __func__);
+ audio_extn_set_afe_proxy_channel_mixer(adev);
+ snd_device = SND_DEVICE_OUT_AFE_PROXY;
} else {
ALOGE("%s: Unknown device(s) %#x", __func__, devices);
}
@@ -763,6 +793,9 @@
snd_device = SND_DEVICE_IN_BT_SCO_MIC ;
} else if (in_device & AUDIO_DEVICE_IN_AUX_DIGITAL) {
snd_device = SND_DEVICE_IN_HDMI_MIC;
+ } else if (in_device & AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET ||
+ in_device & AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET) {
+ snd_device = SND_DEVICE_IN_USB_HEADSET_MIC;
} else if (in_device & AUDIO_DEVICE_IN_FM_RX) {
snd_device = SND_DEVICE_IN_CAPTURE_FM;
} else {
@@ -783,6 +816,9 @@
snd_device = SND_DEVICE_IN_BT_SCO_MIC;
} else if (out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
snd_device = SND_DEVICE_IN_HDMI_MIC;
+ } else if (out_device & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET ||
+ out_device & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) {
+ snd_device = SND_DEVICE_IN_USB_HEADSET_MIC;
} else {
ALOGE("%s: Unknown output device(s) %#x", __func__, out_device);
ALOGW("%s: Using default handset-mic", __func__);
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 1bc4fc4..65d8150 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -60,6 +60,9 @@
SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES,
SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES,
SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET,
+ SND_DEVICE_OUT_AFE_PROXY,
+ SND_DEVICE_OUT_USB_HEADSET,
+ SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET,
SND_DEVICE_OUT_TRANSMISSION_FM,
SND_DEVICE_OUT_ANC_HEADSET,
SND_DEVICE_OUT_ANC_FB_HEADSET,
@@ -96,6 +99,7 @@
SND_DEVICE_IN_VOICE_REC_MIC,
SND_DEVICE_IN_VOICE_REC_DMIC,
SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE,
+ SND_DEVICE_IN_USB_HEADSET_MIC,
SND_DEVICE_IN_CAPTURE_FM,
SND_DEVICE_IN_AANC_HANDSET_MIC,
SND_DEVICE_IN_END,