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,