audio: support custom acdb for external device

add the specific external device setting in audio_platform_info.xml

For example:
<external_specific_dev>
    <ext_device name="SND_DEVICE_OUT_USB_HEADSET" usbid="18d1:5034" acdb_id="10" />
</external_specific_dev>

Bug: 117336751
Test: manual test ok
Change-Id: Ie9abcadd9bcd6d06c5d8f0a9807c3935ee4a1d35
Signed-off-by: Carter Hsu <carterhsu@google.com>
Signed-off-by: Robert Lee <lerobert@google.com>
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 1c28b6b..54ea017 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -86,6 +86,7 @@
 #define audio_extn_usb_alive(adev)                                     (false)
 #define audio_extn_usb_find_service_interval(m, p)      ((m), (p), 0) /* fix unused warn */
 #define audio_extn_usb_altset_for_service_interval(p, si, bw, sr, ch) (-1)
+#define audio_extn_usb_usbid()                                         (NULL)
 #else
 void audio_extn_usb_init(void *adev);
 void audio_extn_usb_deinit();
@@ -109,6 +110,7 @@
                                                uint32_t *bit_width,
                                                uint32_t *sample_rate,
                                                uint32_t *channel_count);
+char *audio_extn_usb_usbid(void);
 #endif
 
 
diff --git a/hal/audio_extn/usb.c b/hal/audio_extn/usb.c
index 71e1aed..b0b5049 100644
--- a/hal/audio_extn/usb.c
+++ b/hal/audio_extn/usb.c
@@ -43,6 +43,7 @@
 #define SAMPLE_RATE_8000          8000
 #define SAMPLE_RATE_11025         11025
 #define DEFAULT_SERVICE_INTERVAL_US    1000
+#define USBID_SIZE                16
 
 /* TODO: dynamically populate supported sample rates */
 static uint32_t supported_sample_rates[] =
@@ -83,6 +84,7 @@
     int usb_sidetone_index[USB_SIDETONE_MAX_INDEX];
     int usb_sidetone_vol_min;
     int usb_sidetone_vol_max;
+    char usbid[USBID_SIZE];
 };
 
 struct usb_module {
@@ -510,6 +512,48 @@
     return ret;
 }
 
+static int usb_get_usbid(struct usb_card_config *usb_card_info,
+                              int card)
+{
+    int32_t fd=-1;
+    char path[128];
+    int ret = 0;
+
+    memset(usb_card_info->usbid, 0, sizeof(usb_card_info->usbid));
+
+    ret = snprintf(path, sizeof(path), "/proc/asound/card%u/usbid",
+             card);
+
+    if (ret < 0) {
+        ALOGE("%s: failed on snprintf (%d) to path %s\n",
+          __func__, ret, path);
+        goto done;
+    }
+
+    fd = open(path, O_RDONLY);
+    if (fd < 0) {
+        ALOGE("%s: error failed to open file %s error: %d\n",
+              __func__, path, errno);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    if (read(fd, usb_card_info->usbid, USBID_SIZE - 1) < 0) {
+        ALOGE("file read error\n");
+        ret = -EINVAL;
+        usb_card_info->usbid[0] = '\0';
+        goto done;
+    }
+
+    strtok(usb_card_info->usbid, "\n");
+
+done:
+    if (fd >= 0)
+        close(fd);
+
+    return ret;
+}
+
 static int usb_get_device_playback_config(struct usb_card_config *usb_card_info,
                                     int card)
 {
@@ -1082,6 +1126,10 @@
     }
     list_init(&usb_card_info->usb_device_conf_list);
     if (usb_output_device(device)) {
+        if (usb_get_usbid(usb_card_info, card) < 0) {
+            ALOGE("parse card %d usbid fail", card);
+        }
+
         if (!usb_get_device_playback_config(usb_card_info, card)){
             usb_card_info->usb_card = card;
             usb_card_info->usb_device_type = device;
@@ -1090,6 +1138,10 @@
             goto exit;
         }
     } else if (usb_input_device(device)) {
+        if (usb_get_usbid(usb_card_info, card) < 0) {
+            ALOGE("parse card %d usbid fail", card);
+        }
+
         if (!usb_get_device_capture_config(usb_card_info, card)) {
             usb_card_info->usb_card = card;
             usb_card_info->usb_device_type = device;
@@ -1251,6 +1303,22 @@
     return 0;
 }
 
+char *audio_extn_usb_usbid()
+{
+    struct usb_card_config *card_info;
+
+    if (usbmod == NULL)
+        return NULL;
+
+    if (list_empty(&usbmod->usb_card_conf_list))
+        return NULL;
+
+    card_info = node_to_item(list_head(&usbmod->usb_card_conf_list),\
+                             struct usb_card_config, list);
+
+    return strdup(card_info->usbid);
+}
+
 void audio_extn_usb_init(void *adev)
 {
     if (usbmod == NULL) {
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 533cf6a..3602b97 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -1480,6 +1480,13 @@
     return ret;
 }
 
+void platform_add_external_specific_device(snd_device_t snd_device __unused,
+                                           const char *name __unused,
+                                           unsigned int acdb_id __unused)
+{
+    return;
+}
+
 void platform_add_operator_specific_device(snd_device_t snd_device,
                                            const char *operator,
                                            const char *mixer_path,
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 6791ad6..ae65ef2 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -453,6 +453,13 @@
     return -ENOSYS;
 }
 
+void platform_add_external_specific_device(snd_device_t snd_device __unused,
+                                           const char *name __unused,
+                                           unsigned int acdb_id __unused)
+{
+    return;
+}
+
 void platform_add_operator_specific_device(snd_device_t snd_device __unused,
                                            const char *operator __unused,
                                            const char *mixer_path __unused,
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index ecb3db7..eb4bb4a 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -101,6 +101,12 @@
     int acdb_id;
 };
 
+struct external_specific_device {
+    struct listnode list;
+    char *usbid;
+    int acdb_id;
+};
+
 #define BE_DAI_NAME_MAX_LENGTH 24
 struct be_dai_name_struct {
     unsigned int be_id;
@@ -114,6 +120,7 @@
 
 static struct listnode operator_info_list;
 static struct listnode *operator_specific_device_table[SND_DEVICE_MAX];
+static struct listnode *external_specific_device_table[SND_DEVICE_MAX];
 
 #define AUDIO_PARAMETER_KEY_AUD_CALDATA "cal_data"
 
@@ -736,6 +743,27 @@
 
 static int init_be_dai_name_table(struct audio_device *adev);
 
+static bool is_usb_snd_dev(snd_device_t snd_device)
+{
+    if (snd_device < SND_DEVICE_IN_BEGIN) {
+        if (snd_device == SND_DEVICE_OUT_USB_HEADSET ||\
+            snd_device == SND_DEVICE_OUT_USB_HEADPHONES ||\
+            snd_device == SND_DEVICE_OUT_VOICE_USB_HEADPHONES ||\
+            snd_device == SND_DEVICE_OUT_VOICE_USB_HEADSET ||\
+            snd_device == SND_DEVICE_OUT_VOICE_TTY_FULL_USB ||\
+            snd_device == SND_DEVICE_OUT_VOICE_TTY_VCO_USB)
+            return true;
+    } else {
+        if (snd_device == SND_DEVICE_IN_USB_HEADSET_MIC ||\
+            snd_device == SND_DEVICE_IN_USB_HEADSET_MIC_AEC ||\
+            snd_device == SND_DEVICE_IN_VOICE_USB_HEADSET_MIC ||\
+            snd_device == SND_DEVICE_IN_UNPROCESSED_USB_HEADSET_MIC ||\
+            snd_device == SND_DEVICE_IN_VOICE_RECOG_USB_HEADSET_MIC)
+            return true;
+    }
+    return false;
+}
+
 static void check_operator()
 {
     char value[PROPERTY_VALUE_MAX];
@@ -825,6 +853,30 @@
     return ret;
 }
 
+static int get_external_specific_device_acdb_id(snd_device_t snd_device)
+{
+    struct external_specific_device *ext_dev;
+    int ret = acdb_device_table[snd_device];
+    char *usbid = NULL;
+    struct listnode *node;
+
+    if (is_usb_snd_dev(snd_device))
+        usbid = audio_extn_usb_usbid();
+
+    if (usbid) {
+        list_for_each(node, external_specific_device_table[snd_device]) {
+            ext_dev = node_to_item(node, struct external_specific_device, list);
+            if (ext_dev->usbid && !strcmp(usbid, ext_dev->usbid)) {
+                ret = ext_dev->acdb_id;
+                break;
+            }
+        }
+
+        free(usbid);
+    }
+    return ret;
+}
+
 static const char *get_operator_specific_device_mixer_path(snd_device_t snd_device)
 {
     struct operator_specific_device *device;
@@ -1256,6 +1308,7 @@
         backend_tag_table[dev] = NULL;
         hw_interface_table[dev] = NULL;
         operator_specific_device_table[dev] = NULL;
+        external_specific_device_table[dev] = NULL;
     }
 
     for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
@@ -1923,6 +1976,7 @@
     int32_t dev;
     struct operator_info *info_item;
     struct operator_specific_device *device_item;
+    struct external_specific_device *ext_dev;
     struct app_type_entry *ap;
     struct listnode *node;
 
@@ -1949,6 +2003,17 @@
             }
             free(operator_specific_device_table[dev]);
         }
+
+        if (external_specific_device_table[dev]) {
+            while (!list_empty(external_specific_device_table[dev])) {
+                node = list_head(external_specific_device_table[dev]);
+                list_remove(node);
+                ext_dev = node_to_item(node, struct external_specific_device, list);
+                free(ext_dev->usbid);
+                free(ext_dev);
+            }
+            free(external_specific_device_table[dev]);
+        }
     }
 
     if (my_data->snd_card_name)
@@ -2201,6 +2266,30 @@
 
 }
 
+void platform_add_external_specific_device(snd_device_t snd_device,
+                                           const char *usbid,
+                                           unsigned int acdb_id)
+{
+    struct external_specific_device *device;
+
+    if (external_specific_device_table[snd_device] == NULL) {
+        external_specific_device_table[snd_device] =
+            (struct listnode *)calloc(1, sizeof(struct listnode));
+        list_init(external_specific_device_table[snd_device]);
+    }
+
+    device = (struct external_specific_device *)calloc(1, sizeof(struct external_specific_device));
+
+    device->usbid = strdup(usbid);
+    device->acdb_id = acdb_id;
+
+    list_add_tail(external_specific_device_table[snd_device], &device->list);
+
+    ALOGD("%s: device[%s] usbid[%s] -> acdb_id[%d]", __func__,
+            platform_get_snd_device_name(snd_device), usbid, acdb_id);
+}
+
+
 int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id)
 {
     int ret = 0;
@@ -2234,6 +2323,8 @@
 
     if (operator_specific_device_table[snd_device] != NULL)
         return get_operator_specific_device_acdb_id(snd_device);
+    else if (external_specific_device_table[snd_device] != NULL)
+        return get_external_specific_device_acdb_id(snd_device);
     else
         return acdb_device_table[snd_device];
 }
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 46ceceb..1d46c31 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -111,6 +111,9 @@
                                            const char *operator,
                                            const char *mixer_path,
                                            unsigned int acdb_id);
+void platform_add_external_specific_device(snd_device_t snd_device,
+                                           const char *name,
+                                           unsigned int acdb_id);
 /* return true if adding entry success
    return false if adding entry fails */
 
diff --git a/hal/platform_info.c b/hal/platform_info.c
index 233e120..5159d6d 100644
--- a/hal/platform_info.c
+++ b/hal/platform_info.c
@@ -54,6 +54,7 @@
     SND_DEV,
     MIC_INFO,
     ACDB_METAINFO_KEY,
+    EXTERNAL_DEVICE_SPECIFIC,
 } section_t;
 
 typedef void (* section_process_fn)(const XML_Char **attr);
@@ -73,6 +74,7 @@
 static void process_snd_dev(const XML_Char **attr);
 static void process_mic_info(const XML_Char **attr);
 static void process_acdb_metainfo_key(const XML_Char **attr);
+static void process_external_dev(const XML_Char **attr);
 
 static section_process_fn section_table[] = {
     [ROOT] = process_root,
@@ -89,6 +91,7 @@
     [SND_DEV] = process_snd_dev,
     [MIC_INFO] = process_mic_info,
     [ACDB_METAINFO_KEY] = process_acdb_metainfo_key,
+    [EXTERNAL_DEVICE_SPECIFIC] = process_external_dev,
 };
 
 static section_t section;
@@ -488,6 +491,38 @@
     return;
 }
 
+static void process_external_dev(const XML_Char **attr)
+{
+    snd_device_t snd_device = SND_DEVICE_NONE;
+
+    if (strcmp(attr[0], "name") != 0) {
+        ALOGE("%s: 'name' not found", __func__);
+        goto done;
+    }
+
+    snd_device = platform_get_snd_device_index((char *)attr[1]);
+    if (snd_device < 0) {
+        ALOGE("%s: Device %s in %s not found, no ACDB ID set!",
+              __func__, (char *)attr[3], PLATFORM_INFO_XML_PATH);
+        goto done;
+    }
+
+    if (strcmp(attr[2], "usbid") != 0) {
+        ALOGE("%s: 'usbid' not found", __func__);
+        goto done;
+    }
+
+    if (strcmp(attr[4], "acdb_id") != 0) {
+        ALOGE("%s: 'acdb_id' not found", __func__);
+        goto done;
+    }
+
+    platform_add_external_specific_device(snd_device, (char *)attr[3], atoi((char *)attr[5]));
+
+done:
+    return;
+}
+
 /* platform specific configuration key-value pairs */
 static void process_config_params(const XML_Char **attr)
 {
@@ -900,6 +935,11 @@
             }
             section_process_fn fn = section_table[MIC_INFO];
             fn(attr);
+        } else if (strcmp(tag_name, "external_specific_dev") == 0) {
+            section = EXTERNAL_DEVICE_SPECIFIC;
+        } else if (strcmp(tag_name, "ext_device") == 0) {
+            section_process_fn fn = section_table[section];
+            fn(attr);
         }
         else if (strncmp(tag_name, "aec", strlen("aec")) == 0) {
             if (section != MODULE) {
@@ -958,6 +998,8 @@
         section = ROOT;
     } else if (strcmp(tag_name, "snd_devices") == 0) {
         section = ROOT;
+    } else if (strcmp(tag_name, "external_specific_dev") == 0) {
+        section = ROOT;
     } else if (strcmp(tag_name, "input_snd_device") == 0) {
         section = SND_DEVICES;
     } else if (strcmp(tag_name, "input_snd_device_mic_mapping") == 0) {