Refactor USB HAL (audio_hw.c)

Refactoring alsa device attributes to alsa_device_profile.h/.c
Refactoring alsa device state to alsa_device_proxy.h/.c
Refactoring format functions into format.h/.c
Refactoring logging functions into logging.h/.c
Sundry (and extensive) code cleanup
Reworked locking on out_write() and in_read() to allow
  simultaneous input/output

Bug: 15986827, 15856702, 12833166

Change-Id: I82c8d0ef252b2f95ee23f263dc175f4c883bfd64
diff --git a/modules/usbaudio/Android.mk b/modules/usbaudio/Android.mk
index 89c498b..ec8a8c0 100644
--- a/modules/usbaudio/Android.mk
+++ b/modules/usbaudio/Android.mk
@@ -19,7 +19,11 @@
 LOCAL_MODULE := audio.usb.default
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_SRC_FILES := \
-	audio_hw.c
+	audio_hw.c \
+	alsa_device_profile.c \
+	alsa_device_proxy.c \
+	logging.c \
+	format.c
 LOCAL_C_INCLUDES += \
 	external/tinyalsa/include \
 	$(call include-path-for, audio-utils)
diff --git a/modules/usbaudio/alsa_device_profile.c b/modules/usbaudio/alsa_device_profile.c
new file mode 100644
index 0000000..2791e3e
--- /dev/null
+++ b/modules/usbaudio/alsa_device_profile.c
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2014 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 "alsa_device_profile"
+/*#define LOG_NDEBUG 0*/
+/*#define LOG_PCM_PARAMS 0*/
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <log/log.h>
+
+#include "alsa_device_profile.h"
+#include "format.h"
+#include "logging.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+/*#define ENABLE_MULTI_CHANNEL 1*/
+
+/*TODO - Evaluate if this value should/can be retrieved from a device-specific property */
+#define BUFF_DURATION_MS   5
+
+#define DEFAULT_PERIOD_SIZE 1024
+
+static const char * const format_string_map[] = {
+    "AUDIO_FORMAT_PCM_16_BIT",      /* "PCM_FORMAT_S16_LE", */
+    "AUDIO_FORMAT_PCM_32_BIT",      /* "PCM_FORMAT_S32_LE", */
+    "AUDIO_FORMAT_PCM_8_BIT",       /* "PCM_FORMAT_S8", */
+    "AUDIO_FORMAT_PCM_8_24_BIT",    /* "PCM_FORMAT_S24_LE", */
+    "AUDIO_FORMAT_PCM_24_BIT_PACKED"/* "PCM_FORMAT_S24_3LE" */
+};
+
+static const unsigned const format_byte_size_map[] = {
+    2, /* PCM_FORMAT_S16_LE */
+    4, /* PCM_FORMAT_S32_LE */
+    1, /* PCM_FORMAT_S8 */
+    4, /* PCM_FORMAT_S24_LE */
+    3, /* PCM_FORMAT_S24_3LE */
+};
+
+extern int8_t const pcm_format_value_map[50];
+
+/* sort these highest -> lowest (to default to best quality) */
+static const unsigned std_sample_rates[] =
+    {48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000};
+
+void profile_init(alsa_device_profile* profile, int direction)
+{
+    profile->card = profile->device = -1;
+    profile->direction = direction;
+
+    /* Fill the attribute arrays with invalid values */
+    size_t index;
+    for (index = 0; index < ARRAY_SIZE(profile->formats); index++) {
+        profile->formats[index] = PCM_FORMAT_INVALID;
+    }
+
+    for (index = 0; index < ARRAY_SIZE(profile->sample_rates); index++) {
+        profile->sample_rates[index] = 0;
+    }
+
+    for (index = 0; index < ARRAY_SIZE(profile->channel_counts); index++) {
+        profile->channel_counts[index] = 0;
+    }
+
+    profile->min_period_size = profile->max_period_size = 0;
+    profile->min_channel_count = profile->max_channel_count = DEFAULT_CHANNEL_COUNT;
+
+    profile->is_valid = false;
+}
+
+bool profile_is_initialized(alsa_device_profile* profile)
+{
+    return profile->card >= 0 && profile->device >= 0;
+}
+
+bool profile_is_valid(alsa_device_profile* profile) {
+    return profile->is_valid;
+}
+
+/*
+ * Returns the supplied value rounded up to the next even multiple of 16
+ */
+static unsigned int round_to_16_mult(unsigned int size)
+{
+    return (size + 15) & ~15;   // 0xFFFFFFF0;
+}
+
+/*
+ * Returns the system defined minimum period size based on the supplied sample rate.
+ */
+unsigned profile_calc_min_period_size(alsa_device_profile* profile, unsigned sample_rate)
+{
+    ALOGV("profile_calc_min_period_size(%p, rate:%d)", profile, sample_rate);
+    if (profile == NULL) {
+        return DEFAULT_PERIOD_SIZE;
+    } else {
+        unsigned num_sample_frames = (sample_rate * BUFF_DURATION_MS) / 1000;
+        if (num_sample_frames < profile->min_period_size) {
+            num_sample_frames = profile->min_period_size;
+        }
+        return round_to_16_mult(num_sample_frames) * 2;
+    }
+}
+
+unsigned int profile_get_period_size(alsa_device_profile* profile, unsigned sample_rate)
+{
+    // return profile->default_config.period_size;
+    unsigned int period_size = profile_calc_min_period_size(profile, sample_rate);
+    ALOGV("profile_get_period_size(rate:%d) = %d", sample_rate, period_size);
+    return period_size;
+}
+
+/*
+ * Sample Rate
+ */
+unsigned profile_get_default_sample_rate(alsa_device_profile* profile)
+{
+    /*
+     * TODO this won't be right in general. we should store a preferred rate as we are scanning.
+     * But right now it will return the highest rate, which may be correct.
+     */
+    return profile_is_valid(profile) ? profile->sample_rates[0] : DEFAULT_SAMPLE_RATE;
+}
+
+bool profile_is_sample_rate_valid(alsa_device_profile* profile, unsigned rate)
+{
+    if (profile_is_valid(profile)) {
+        size_t index;
+        for (index = 0; profile->sample_rates[index] != 0; index++) {
+            if (profile->sample_rates[index] == rate) {
+                return true;
+            }
+        }
+
+        return false;
+    } else {
+        return rate == DEFAULT_SAMPLE_RATE;
+    }
+}
+
+/*
+ * Format
+ */
+enum pcm_format profile_get_default_format(alsa_device_profile* profile)
+{
+    /*
+     * TODO this won't be right in general. we should store a preferred format as we are scanning.
+     */
+    return profile_is_valid(profile) ? profile->formats[0] : DEFAULT_SAMPLE_FORMAT;
+}
+
+bool profile_is_format_valid(alsa_device_profile* profile, enum pcm_format fmt) {
+    if (profile_is_valid(profile)) {
+        size_t index;
+        for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) {
+            if (profile->formats[index] == fmt) {
+                return true;
+            }
+        }
+
+        return false;
+    } else {
+        return fmt == DEFAULT_SAMPLE_FORMAT;
+    }
+}
+
+/*
+ * Channels
+ */
+unsigned profile_get_default_channel_count(alsa_device_profile* profile)
+{
+    return profile_is_valid(profile) ? profile->channel_counts[0] : DEFAULT_CHANNEL_COUNT;
+}
+
+bool profile_is_channel_count_valid(alsa_device_profile* profile, unsigned count)
+{
+    if (profile_is_initialized(profile)) {
+        return count >= profile->min_channel_count && count <= profile->max_channel_count;
+    } else {
+        return count == DEFAULT_CHANNEL_COUNT;
+    }
+}
+
+static bool profile_test_sample_rate(alsa_device_profile* profile, unsigned rate)
+{
+    struct pcm_config config = profile->default_config;
+    config.rate = rate;
+
+    bool works = false; /* let's be pessimistic */
+    struct pcm * pcm = pcm_open(profile->card, profile->device,
+                                profile->direction, &config);
+
+    if (pcm != NULL) {
+        works = pcm_is_ready(pcm);
+        pcm_close(pcm);
+    }
+
+    return works;
+}
+
+static unsigned profile_enum_sample_rates(alsa_device_profile* profile, unsigned min, unsigned max)
+{
+    unsigned num_entries = 0;
+    unsigned index;
+
+    for (index = 0; index < ARRAY_SIZE(std_sample_rates) &&
+                    num_entries < ARRAY_SIZE(profile->sample_rates) - 1;
+         index++) {
+        if (std_sample_rates[index] >= min && std_sample_rates[index] <= max
+                && profile_test_sample_rate(profile, std_sample_rates[index])) {
+            profile->sample_rates[num_entries++] = std_sample_rates[index];
+        }
+    }
+
+    return num_entries; /* return # of supported rates */
+}
+
+static unsigned profile_enum_sample_formats(alsa_device_profile* profile, struct pcm_mask * mask)
+{
+    const int num_slots = ARRAY_SIZE(mask->bits);
+    const int bits_per_slot = sizeof(mask->bits[0]) * 8;
+
+    const int table_size = ARRAY_SIZE(pcm_format_value_map);
+
+    int slot_index, bit_index, table_index;
+    table_index = 0;
+    int num_written = 0;
+    for (slot_index = 0; slot_index < num_slots && table_index < table_size;
+            slot_index++) {
+        unsigned bit_mask = 1;
+        for (bit_index = 0;
+                bit_index < bits_per_slot && table_index < table_size;
+                bit_index++) {
+            if ((mask->bits[slot_index] & bit_mask) != 0) {
+                enum pcm_format format = pcm_format_value_map[table_index];
+                /* Never return invalid (unrecognized) or 8-bit */
+                if (format != PCM_FORMAT_INVALID && format != PCM_FORMAT_S8) {
+                    profile->formats[num_written++] = format;
+                    if (num_written == ARRAY_SIZE(profile->formats) - 1) {
+                        /* leave at least one PCM_FORMAT_INVALID at the end */
+                        return num_written;
+                    }
+                }
+            }
+            bit_mask <<= 1;
+            table_index++;
+        }
+    }
+
+    return num_written;
+}
+
+static unsigned profile_enum_channel_counts(alsa_device_profile* profile, unsigned min, unsigned max)
+{
+    static const unsigned std_channel_counts[] = {8, 4, 2, 1};
+
+    unsigned num_counts = 0;
+    unsigned index;
+    /* TODO write a profile_test_channel_count() */
+    /* Ensure there is at least one invalid channel count to terminate the channel counts array */
+    for (index = 0; index < ARRAY_SIZE(std_channel_counts) &&
+                    num_counts < ARRAY_SIZE(profile->channel_counts) - 1;
+         index++) {
+        /* TODO Do we want a channel counts test? */
+        if (std_channel_counts[index] >= min && std_channel_counts[index] <= max /* &&
+         profile_test_channel_count(profile, std_channel_counts[index])*/) {
+            profile->channel_counts[num_counts++] = std_channel_counts[index];
+        }
+    }
+
+    return num_counts; /* return # of supported counts */
+}
+
+/*
+ * Reads and decodes configuration info from the specified ALSA card/device.
+ */
+static int read_alsa_device_config(alsa_device_profile * profile, struct pcm_config * config)
+{
+    ALOGV("usb:audio_hw - read_alsa_device_config(c:%d d:%d t:0x%X)",
+          profile->card, profile->device, profile->direction);
+
+    if (profile->card < 0 || profile->device < 0) {
+        return -EINVAL;
+    }
+
+    struct pcm_params * alsa_hw_params =
+        pcm_params_get(profile->card, profile->device, profile->direction);
+    if (alsa_hw_params == NULL) {
+        return -EINVAL;
+    }
+
+    profile->min_period_size = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_SIZE);
+    profile->max_period_size = pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_SIZE);
+
+    profile->min_channel_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
+    profile->max_channel_count = pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS);
+
+    int ret = 0;
+
+    /*
+     * This Logging will be useful when testing new USB devices.
+     */
+#ifdef LOG_PCM_PARAMS
+    log_pcm_params(alsa_hw_params);
+#endif
+
+    config->channels = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
+    config->rate = pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE);
+    config->period_size = profile_calc_min_period_size(profile, config->rate);
+    config->period_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS);
+    config->format = get_pcm_format_for_mask(pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT));
+#ifdef LOG_PCM_PARAMS
+    log_pcm_config(config, "read_alsa_device_config");
+#endif
+    if (config->format == PCM_FORMAT_INVALID) {
+        ret = -EINVAL;
+    }
+
+    pcm_params_free(alsa_hw_params);
+
+    return ret;
+}
+
+bool profile_read_device_info(alsa_device_profile* profile)
+{
+    if (!profile_is_initialized(profile)) {
+        return false;
+    }
+
+    /* let's get some defaults */
+    read_alsa_device_config(profile, &profile->default_config);
+    ALOGV("default_config chans:%d rate:%d format:%d count:%d size:%d",
+          profile->default_config.channels, profile->default_config.rate,
+          profile->default_config.format, profile->default_config.period_count,
+          profile->default_config.period_size);
+
+    struct pcm_params * alsa_hw_params = pcm_params_get(profile->card,
+                                                        profile->device,
+                                                        profile->direction);
+    if (alsa_hw_params == NULL) {
+        return false;
+    }
+
+    /* Formats */
+    struct pcm_mask * format_mask = pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT);
+    profile_enum_sample_formats(profile, format_mask);
+
+    /* Channels */
+    profile_enum_channel_counts(
+            profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS),
+            pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS));
+
+    /* Sample Rates */
+    profile_enum_sample_rates(
+            profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE),
+            pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE));
+
+    profile->is_valid = true;
+
+    return true;
+}
+
+char * profile_get_sample_rate_strs(alsa_device_profile* profile)
+{
+    char buffer[128];
+    buffer[0] = '\0';
+    int buffSize = ARRAY_SIZE(buffer);
+
+    char numBuffer[32];
+
+    int numEntries = 0;
+    unsigned index;
+    for (index = 0; profile->sample_rates[index] != 0; index++) {
+        if (numEntries++ != 0) {
+            strncat(buffer, "|", buffSize);
+        }
+        snprintf(numBuffer, sizeof(numBuffer), "%u", profile->sample_rates[index]);
+        strncat(buffer, numBuffer, buffSize);
+    }
+
+    return strdup(buffer);
+}
+
+char * profile_get_format_strs(alsa_device_profile* profile)
+{
+    /* TODO remove this hack when we have support for input in non PCM16 formats */
+    if (profile->direction == PCM_IN) {
+        return strdup("AUDIO_FORMAT_PCM_16_BIT");
+    }
+
+    char buffer[128];
+    buffer[0] = '\0';
+    int buffSize = ARRAY_SIZE(buffer);
+
+    int numEntries = 0;
+    unsigned index = 0;
+    for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) {
+        if (numEntries++ != 0) {
+            strncat(buffer, "|", buffSize);
+        }
+        strncat(buffer, format_string_map[profile->formats[index]], buffSize);
+    }
+
+    return strdup(buffer);
+}
+
+char * profile_get_channel_count_strs(alsa_device_profile* profile)
+{
+#ifndef ENABLE_MULTI_CHANNEL
+    /* TODO remove this hack when it is superceeded by proper multi-channel support */
+    return strdup(
+            profile->direction == PCM_OUT ?
+                    "AUDIO_CHANNEL_OUT_STEREO" : "AUDIO_CHANNEL_IN_STEREO");
+#else
+    /*
+     * Maps from a channel-count (the table index), to a corresponding AUDIO_CHANNEL_OUT_ string.
+     * (see audio_channel_out_mask_from_count() in audio.h.
+     */
+    static const char * const out_chans_strs[] = {
+        /* 0 */"AUDIO_CHANNEL_NONE",
+        /* 1 */"AUDIO_CHANNEL_OUT_MONO",
+        /* 2 */"AUDIO_CHANNEL_OUT_STEREO",
+        /* 3 */"AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_FRONT_CENTER",
+        /* 4 */"AUDIO_CHANNEL_OUT_QUAD",
+        /* 5 */"AUDIO_CHANNEL_OUT_QUAD|AUDIO_CHANNEL_OUT_FRONT_CENTER",
+        /* 6 */"AUDIO_CHANNEL_OUT_5POINT1",
+        /* 7 */"AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_BACK_CENTER",
+        /* 8 */"AUDIO_CHANNEL_OUT_7POINT1"
+    };
+
+    /*
+     * Maps from a channel-count (the table index), to a corresponding AUDIO_CHANNEL_IN_ string.
+     * (see audio_channel_in_mask_from_count() in audio.h.
+     */
+    static const char * const in_chans_strs[] =
+    {
+        /* 0 */"AUDIO_CHANNEL_NONE",
+        /* 1 */"AUDIO_CHANNEL_IN_MONO",
+        /* 2 */"AUDIO_CHANNEL_IN_STEREO",
+        /* 3 */"",
+        /* 4 */
+        "AUDIO_CHANNEL_IN_LEFT|AUDIO_CHANNEL_IN_RIGHT|AUDIO_CHANNEL_IN_FRONT|AUDIO_CHANNEL_IN_BACK"
+    };
+
+    char buffer[256];
+    buffer[0] = '\0';
+    int buffSize = ARRAY_SIZE(buffer);
+
+    int numEntries = 0;
+    unsigned index = 0;
+    for (index = 0; profile->channel_counts[index] != 0; index++) {
+        if (numEntries++ != 0) {
+            strncat(buffer, "|", buffSize);
+        }
+        if (profile->direction == PCM_OUT) {
+            strncat(buffer, out_chans_strs[profile->channel_counts[index]], buffSize);
+        } else {
+            strncat(buffer, in_chans_strs[profile->channel_counts[index]], buffSize);
+        }
+    }
+
+    return strdup(buffer);
+#endif
+}
diff --git a/modules/usbaudio/alsa_device_profile.h b/modules/usbaudio/alsa_device_profile.h
new file mode 100644
index 0000000..b75e1dc
--- /dev/null
+++ b/modules/usbaudio/alsa_device_profile.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROFILE_H
+#define ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROFILE_H
+
+#include <stdbool.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#define MAX_PROFILE_FORMATS         6  /* We long support the 5 standard formats defined
+                                        * in asound.h, so we just need this to be 1 more
+                                        * than that */
+#define MAX_PROFILE_SAMPLE_RATES    10 /* this number needs to be 1 more than the number of
+                                        * standard formats in std_sample_rates[]
+                                        * (in alsa_device_profile.c) */
+#define MAX_PROFILE_CHANNEL_COUNTS  5  /* this number need to be 1 more than the number of
+                                        * standard channel formats in std_channel_counts[]
+                                        * (in alsa_device_profile.c) */
+
+#define DEFAULT_SAMPLE_RATE         44100
+#define DEFAULT_SAMPLE_FORMAT       PCM_FORMAT_S16_LE
+#define DEFAULT_CHANNEL_COUNT       2
+
+typedef struct  {
+    int card;
+    int device;
+    int direction; /* PCM_OUT or PCM_IN */
+
+    enum pcm_format formats[MAX_PROFILE_FORMATS];
+
+    unsigned sample_rates[MAX_PROFILE_SAMPLE_RATES];
+
+    unsigned channel_counts[MAX_PROFILE_CHANNEL_COUNTS];
+
+    bool is_valid;
+
+    /* read from the hardware device */
+    struct pcm_config default_config;
+
+    unsigned min_period_size;
+    unsigned max_period_size;
+
+    unsigned min_channel_count;
+    unsigned max_channel_count;
+} alsa_device_profile;
+
+void profile_init(alsa_device_profile* profile, int direction);
+bool profile_is_initialized(alsa_device_profile* profile);
+bool profile_is_valid(alsa_device_profile* profile);
+
+bool profile_read_device_info(alsa_device_profile* profile);
+
+/* Audio Config Strings Methods */
+char * profile_get_sample_rate_strs(alsa_device_profile* profile);
+char * profile_get_format_strs(alsa_device_profile* profile);
+char * profile_get_channel_count_strs(alsa_device_profile* profile);
+
+/* Sample Rate Methods */
+unsigned profile_get_default_sample_rate(alsa_device_profile* profile);
+bool profile_is_sample_rate_valid(alsa_device_profile* profile, unsigned rate);
+
+/* Format Methods */
+enum pcm_format profile_get_default_format(alsa_device_profile* profile);
+bool profile_is_format_valid(alsa_device_profile* profile, enum pcm_format fmt);
+
+/* Channel Methods */
+unsigned profile_get_default_channel_count(alsa_device_profile* profile);
+bool profile_is_channel_count_valid(alsa_device_profile* profile, unsigned count);
+
+/* Utility */
+unsigned profile_calc_min_period_size(alsa_device_profile* profile, unsigned sample_rate);
+unsigned int profile_get_period_size(alsa_device_profile* profile, unsigned sample_rate);
+
+#endif /* ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROFILE_H */
diff --git a/modules/usbaudio/alsa_device_proxy.c b/modules/usbaudio/alsa_device_proxy.c
new file mode 100644
index 0000000..b887d1d
--- /dev/null
+++ b/modules/usbaudio/alsa_device_proxy.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2014 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 "alsa_device_proxy"
+/*#define LOG_NDEBUG 0*/
+/*#define LOG_PCM_PARAMS 0*/
+
+#include <log/log.h>
+
+#include "alsa_device_proxy.h"
+
+#include "logging.h"
+
+#define DEFAULT_PERIOD_SIZE     1024
+#define DEFAULT_PERIOD_COUNT    2
+
+//void proxy_init(alsa_device_proxy * proxy)
+//{
+//    proxy->profile = NULL;
+//
+//    proxy->alsa_config.format = DEFAULT_SAMPLE_FORMAT;
+//    proxy->alsa_config.rate = DEFAULT_SAMPLE_RATE;
+//    proxy->alsa_config.channels = DEFAULT_CHANNEL_COUNT;
+//
+//    proxy->alsa_config.period_size = DEFAULT_PERIOD_SIZE;
+//    proxy->alsa_config.period_count = DEFAULT_PERIOD_COUNT;
+//
+//    proxy->pcm = NULL;
+//}
+
+void proxy_prepare(alsa_device_proxy * proxy, alsa_device_profile* profile,
+                   struct pcm_config * config)
+{
+    ALOGV("proxy_prepare()");
+
+    proxy->profile = profile;
+
+#if LOG_PCM_PARAMS
+    log_pcm_config(config, "proxy_setup()");
+#endif
+
+    proxy->alsa_config.format =
+        config->format != PCM_FORMAT_INVALID && profile_is_format_valid(profile, config->format)
+            ? config->format : profile->default_config.format;
+    proxy->alsa_config.rate =
+        config->rate != 0 && profile_is_sample_rate_valid(profile, config->rate)
+            ? config->rate : profile->default_config.rate;
+    proxy->alsa_config.channels =
+        config->channels != 0 && profile_is_channel_count_valid(profile, config->channels)
+            ? config->channels : profile->default_config.channels;
+
+    proxy->alsa_config.period_count = profile->default_config.period_count;
+    proxy->alsa_config.period_size =
+            profile_get_period_size(proxy->profile, proxy->alsa_config.rate);
+
+    proxy->pcm = NULL;
+}
+
+int proxy_open(alsa_device_proxy * proxy)
+{
+    alsa_device_profile* profile = proxy->profile;
+
+    ALOGV("proxy_open(card:%d device:%d %s)", profile->card, profile->device,
+          profile->direction == PCM_OUT ? "PCM_OUT" : "PCM_IN");
+
+    proxy->pcm = pcm_open(profile->card, profile->device, profile->direction, &proxy->alsa_config);
+    if (proxy->pcm == NULL) {
+        return -ENOMEM;
+    }
+
+    if (!pcm_is_ready(proxy->pcm)) {
+        ALOGE("[%s] proxy_open() pcm_open() failed: %s", LOG_TAG, pcm_get_error(proxy->pcm));
+#ifdef LOG_PCM_PARAMS
+        log_pcm_config(&proxy->alsa_config, "config");
+#endif
+        pcm_close(proxy->pcm);
+        proxy->pcm = NULL;
+        return -ENOMEM;
+    }
+
+    return 0;
+}
+
+void proxy_close(alsa_device_proxy * proxy)
+{
+    pcm_close(proxy->pcm);
+    proxy->pcm = NULL;
+}
+
+/*
+ * Sample Rate
+ */
+unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy)
+{
+    return proxy->alsa_config.rate;
+}
+
+/*
+ * Format
+ */
+enum pcm_format proxy_get_format(const alsa_device_proxy * proxy)
+{
+    return proxy->alsa_config.format;
+}
+
+/*
+ * Channel Count
+ */
+unsigned proxy_get_channel_count(const alsa_device_proxy * proxy)
+{
+    return proxy->alsa_config.channels;
+}
+
+/*
+ * Other
+ */
+unsigned int proxy_get_period_size(const alsa_device_proxy * proxy)
+{
+    return proxy->alsa_config.period_size;
+}
+
+unsigned int proxy_get_period_count(const alsa_device_proxy * proxy)
+{
+    return proxy->alsa_config.period_count;
+}
+
+unsigned proxy_get_latency(const alsa_device_proxy * proxy)
+{
+    return (proxy_get_period_size(proxy) * proxy_get_period_count(proxy) * 1000)
+               / proxy_get_sample_rate(proxy);
+}
+
+/*
+ * I/O
+ */
+int proxy_write(const alsa_device_proxy * proxy, const void *data, unsigned int count)
+{
+    return pcm_write(proxy->pcm, data, count);
+}
+
+int proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count)
+{
+    return pcm_read(proxy->pcm, data, count);
+}
diff --git a/modules/usbaudio/alsa_device_proxy.h b/modules/usbaudio/alsa_device_proxy.h
new file mode 100644
index 0000000..f090c56
--- /dev/null
+++ b/modules/usbaudio/alsa_device_proxy.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROXY_H
+#define ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROXY_H
+
+#include <tinyalsa/asoundlib.h>
+
+#include "alsa_device_profile.h"
+
+typedef struct {
+    alsa_device_profile* profile;
+
+    struct pcm_config alsa_config;
+
+    struct pcm * pcm;
+} alsa_device_proxy;
+
+void proxy_prepare(alsa_device_proxy * proxy, alsa_device_profile * profile,
+                   struct pcm_config * config);
+
+unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy);
+enum pcm_format proxy_get_format(const alsa_device_proxy * proxy);
+unsigned proxy_get_channel_count(const alsa_device_proxy * proxy);
+
+unsigned int proxy_get_period_size(const alsa_device_proxy * proxy);
+
+unsigned proxy_get_latency(const alsa_device_proxy * proxy);
+
+int proxy_open(alsa_device_proxy * proxy);
+void proxy_close(alsa_device_proxy * proxy);
+
+int proxy_write(const alsa_device_proxy * proxy, const void *data, unsigned int count);
+int proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count);
+
+#endif /* ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROXY_H */
diff --git a/modules/usbaudio/audio_hw.c b/modules/usbaudio/audio_hw.c
index 05f0d2b..4a946e7 100644
--- a/modules/usbaudio/audio_hw.c
+++ b/modules/usbaudio/audio_hw.c
@@ -16,7 +16,6 @@
 
 #define LOG_TAG "usb_audio_hw"
 /*#define LOG_NDEBUG 0*/
-/*#define LOG_PCM_PARAMS 0*/
 
 #include <errno.h>
 #include <inttypes.h>
@@ -39,46 +38,11 @@
 
 #include <audio_utils/channels.h>
 
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#include "alsa_device_profile.h"
+#include "alsa_device_proxy.h"
+#include "logging.h"
 
-/* This is the default configuration to hand to The Framework on the initial
- * adev_open_output_stream(). Actual device attributes will be used on the subsequent
- * adev_open_output_stream() after the card and device number have been set in out_set_parameters()
- */
-#define OUT_PERIOD_SIZE 1024
-#define OUT_PERIOD_COUNT 4
-#define OUT_SAMPLING_RATE 44100
-
-struct pcm_config default_alsa_out_config = {
-    .channels = 2,
-    .rate = OUT_SAMPLING_RATE,
-    .period_size = OUT_PERIOD_SIZE,
-    .period_count = OUT_PERIOD_COUNT,
-    .format = PCM_FORMAT_S16_LE,
-};
-
-/*
- * Input defaults.  See comment above.
- */
-#define IN_PERIOD_SIZE 1024
-#define IN_PERIOD_COUNT 4
-#define IN_SAMPLING_RATE 44100
-
-struct pcm_config default_alsa_in_config = {
-    .channels = 2,
-    .rate = IN_SAMPLING_RATE,
-    .period_size = IN_PERIOD_SIZE,
-    .period_count = IN_PERIOD_COUNT,
-    .format = PCM_FORMAT_S16_LE,
-    .start_threshold = 1,
-    .stop_threshold = (IN_PERIOD_SIZE * IN_PERIOD_COUNT),
-};
-
-struct audio_device_profile {
-    int card;
-    int device;
-    int direction; /* PCM_OUT or PCM_IN */
-};
+#define DEFAULT_INPUT_BUFFER_SIZE_MS 20
 
 struct audio_device {
     struct audio_hw_device hw_device;
@@ -86,10 +50,10 @@
     pthread_mutex_t lock; /* see note below on mutex acquisition order */
 
     /* output */
-    struct audio_device_profile out_profile;
+    alsa_device_profile out_profile;
 
     /* input */
-    struct audio_device_profile in_profile;
+    alsa_device_profile in_profile;
 
     bool standby;
 };
@@ -98,11 +62,12 @@
     struct audio_stream_out stream;
 
     pthread_mutex_t lock;               /* see note below on mutex acquisition order */
-    struct pcm *pcm;                    /* state of the stream */
     bool standby;
 
-    struct audio_device *dev;           /* hardware information */
-    struct audio_device_profile * profile;
+    struct audio_device *dev;           /* hardware information - only using this for the lock */
+
+    alsa_device_profile * profile;
+    alsa_device_proxy proxy;            /* state of the stream */
 
     void * conversion_buffer;           /* any conversions are put into here
                                          * they could come from here too if
@@ -110,37 +75,21 @@
     size_t conversion_buffer_size;      /* in bytes */
 };
 
-/*
- * Output Configuration Cache
- * FIXME(pmclean) This is not reentrant. Should probably be moved into the stream structure.
- */
-static struct pcm_config cached_output_hardware_config;
-static bool output_hardware_config_is_cached = false;
-
 struct stream_in {
     struct audio_stream_in stream;
 
     pthread_mutex_t lock; /* see note below on mutex acquisition order */
-    struct pcm *pcm;
     bool standby;
 
-    struct audio_device *dev;
+    struct audio_device *dev;           /* hardware information - only using this for the lock */
 
-    struct audio_device_profile * profile;
+    alsa_device_profile * profile;
+    alsa_device_proxy proxy;            /* state of the stream */
 
-    struct audio_config hal_pcm_config;
+    // not used?
+    // struct audio_config hal_pcm_config;
 
-    /* this is the format the framework thinks it's using. We may need to convert from the actual
-     * (24-bit, 32-bit?) format to this theoretical (framework, probably 16-bit)
-     * format in in_read() */
-    enum pcm_format input_framework_format;
-
-//    struct resampler_itfe *resampler;
-//    struct resampler_buffer_provider buf_provider;
-
-    int read_status;
-
-    // We may need to read more data from the device in order to data reduce to 16bit, 4chan */
+    /* We may need to read more data from the device in order to data reduce to 16bit, 4chan */
     void * conversion_buffer;           /* any conversions are put into here
                                          * they could come from here too if
                                          * there was a previous conversion */
@@ -148,17 +97,6 @@
 };
 
 /*
- * Input Configuration Cache
- * FIXME(pmclean) This is not reentrant. Should probably be moved into the stream structure
- * but that will involve changes in The Framework.
- */
-static struct pcm_config cached_input_hardware_config;
-static bool input_hardware_config_is_cached = false;
-
-/*
- * Utility
- */
-/*
  * Data Conversions
  */
 /*
@@ -226,465 +164,49 @@
     return num_in_samples * 2;
 }
 
-/*
- * ALSA Utilities
- */
-/*TODO This table and the function that uses it should be moved to a utilities module (probably) */
-/*
- * Maps bit-positions in a pcm_mask to the corresponding AUDIO_ format string.
- */
-static const char * const format_string_map[] = {
-    "AUDIO_FORMAT_PCM_8_BIT",           /* 00 - SNDRV_PCM_FORMAT_S8 */
-    "AUDIO_FORMAT_PCM_8_BIT",           /* 01 - SNDRV_PCM_FORMAT_U8 */
-    "AUDIO_FORMAT_PCM_16_BIT",          /* 02 - SNDRV_PCM_FORMAT_S16_LE */
-    NULL,                               /* 03 - SNDRV_PCM_FORMAT_S16_BE */
-    NULL,                               /* 04 - SNDRV_PCM_FORMAT_U16_LE */
-    NULL,                               /* 05 - SNDRV_PCM_FORMAT_U16_BE */
-    "AUDIO_FORMAT_PCM_24_BIT_PACKED",   /* 06 - SNDRV_PCM_FORMAT_S24_LE */
-    NULL,                               /* 07 - SNDRV_PCM_FORMAT_S24_BE */
-    NULL,                               /* 08 - SNDRV_PCM_FORMAT_U24_LE */
-    NULL,                               /* 09 - SNDRV_PCM_FORMAT_U24_BE */
-    "AUDIO_FORMAT_PCM_32_BIT",          /* 10 - SNDRV_PCM_FORMAT_S32_LE */
-    NULL,                               /* 11 - SNDRV_PCM_FORMAT_S32_BE */
-    NULL,                               /* 12 - SNDRV_PCM_FORMAT_U32_LE */
-    NULL,                               /* 13 - SNDRV_PCM_FORMAT_U32_BE */
-    "AUDIO_FORMAT_PCM_FLOAT",           /* 14 - SNDRV_PCM_FORMAT_FLOAT_LE */
-    NULL,                               /* 15 - SNDRV_PCM_FORMAT_FLOAT_BE */
-    NULL,                               /* 16 - SNDRV_PCM_FORMAT_FLOAT64_LE */
-    NULL,                               /* 17 - SNDRV_PCM_FORMAT_FLOAT64_BE */
-    NULL,                               /* 18 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE */
-    NULL,                               /* 19 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE */
-    NULL,                               /* 20 - SNDRV_PCM_FORMAT_MU_LAW */
-    NULL,                               /* 21 - SNDRV_PCM_FORMAT_A_LAW */
-    NULL,                               /* 22 - SNDRV_PCM_FORMAT_IMA_ADPCM */
-    NULL,                               /* 23 - SNDRV_PCM_FORMAT_MPEG */
-    NULL,                               /* 24 - SNDRV_PCM_FORMAT_GSM */
-    NULL, NULL, NULL, NULL, NULL, NULL, /* 25 -> 30 (not assigned) */
-    NULL,                               /* 31 - SNDRV_PCM_FORMAT_SPECIAL */
-    "AUDIO_FORMAT_PCM_24_BIT_PACKED",   /* 32 - SNDRV_PCM_FORMAT_S24_3LE */ /* ??? */
-    NULL,                               /* 33 - SNDRV_PCM_FORMAT_S24_3BE */
-    NULL,                               /* 34 - SNDRV_PCM_FORMAT_U24_3LE */
-    NULL,                               /* 35 - SNDRV_PCM_FORMAT_U24_3BE */
-    NULL,                               /* 36 - SNDRV_PCM_FORMAT_S20_3LE */
-    NULL,                               /* 37 - SNDRV_PCM_FORMAT_S20_3BE */
-    NULL,                               /* 38 - SNDRV_PCM_FORMAT_U20_3LE */
-    NULL,                               /* 39 - SNDRV_PCM_FORMAT_U20_3BE */
-    NULL,                               /* 40 - SNDRV_PCM_FORMAT_S18_3LE */
-    NULL,                               /* 41 - SNDRV_PCM_FORMAT_S18_3BE */
-    NULL,                               /* 42 - SNDRV_PCM_FORMAT_U18_3LE */
-    NULL,                               /* 43 - SNDRV_PCM_FORMAT_U18_3BE */
-    NULL,                               /* 44 - SNDRV_PCM_FORMAT_G723_24 */
-    NULL,                               /* 45 - SNDRV_PCM_FORMAT_G723_24_1B */
-    NULL,                               /* 46 - SNDRV_PCM_FORMAT_G723_40 */
-    NULL,                               /* 47 - SNDRV_PCM_FORMAT_G723_40_1B */
-    NULL,                               /* 48 - SNDRV_PCM_FORMAT_DSD_U8 */
-    NULL                                /* 49 - SNDRV_PCM_FORMAT_DSD_U16_LE */
-};
-
-/*
- * Generate string containing a bar ("|") delimited list of AUDIO_ formats specified in
- * the mask parameter.
- *
- */
-static char* get_format_str_for_mask(struct pcm_mask* mask)
+static char * device_get_parameters(alsa_device_profile * profile, const char * keys)
 {
-    char buffer[256];
-    int buffer_size = sizeof(buffer) / sizeof(buffer[0]);
-    buffer[0] = '\0';
+    ALOGV("usb:audio_hw::device_get_parameters() keys:%s", keys);
 
-    int num_slots = sizeof(mask->bits) / sizeof(mask->bits[0]);
-    int bits_per_slot = sizeof(mask->bits[0]) * 8;
-
-    const char* format_str = NULL;
-    int table_size = sizeof(format_string_map)/sizeof(format_string_map[0]);
-
-    int slot_index, bit_index, table_index;
-    table_index = 0;
-    int num_written = 0;
-    for (slot_index = 0; slot_index < num_slots; slot_index++) {
-        unsigned bit_mask = 1;
-        for (bit_index = 0; bit_index < bits_per_slot; bit_index++) {
-            if ((mask->bits[slot_index] & bit_mask) != 0) {
-                format_str = table_index < table_size
-                                ? format_string_map[table_index]
-                                : NULL;
-                if (format_str != NULL) {
-                    if (num_written != 0) {
-                        num_written += snprintf(buffer + num_written,
-                                                buffer_size - num_written, "|");
-                    }
-                    num_written += snprintf(buffer + num_written, buffer_size - num_written,
-                                            "%s", format_str);
-                }
-            }
-            bit_mask <<= 1;
-            table_index++;
-        }
+    if (profile->card < 0 || profile->device < 0) {
+        return strdup("");
     }
 
-    return strdup(buffer);
-}
+    struct str_parms *query = str_parms_create_str(keys);
+    struct str_parms *result = str_parms_create();
 
-/*
- * Maps from bit position in pcm_mask to AUDIO_ format constants.
- */
-static audio_format_t const format_value_map[] = {
-    AUDIO_FORMAT_PCM_8_BIT,           /* 00 - SNDRV_PCM_FORMAT_S8 */
-    AUDIO_FORMAT_PCM_8_BIT,           /* 01 - SNDRV_PCM_FORMAT_U8 */
-    AUDIO_FORMAT_PCM_16_BIT,          /* 02 - SNDRV_PCM_FORMAT_S16_LE */
-    AUDIO_FORMAT_INVALID,             /* 03 - SNDRV_PCM_FORMAT_S16_BE */
-    AUDIO_FORMAT_INVALID,             /* 04 - SNDRV_PCM_FORMAT_U16_LE */
-    AUDIO_FORMAT_INVALID,             /* 05 - SNDRV_PCM_FORMAT_U16_BE */
-    AUDIO_FORMAT_INVALID,             /* 06 - SNDRV_PCM_FORMAT_S24_LE */
-    AUDIO_FORMAT_INVALID,             /* 07 - SNDRV_PCM_FORMAT_S24_BE */
-    AUDIO_FORMAT_INVALID,             /* 08 - SNDRV_PCM_FORMAT_U24_LE */
-    AUDIO_FORMAT_INVALID,             /* 09 - SNDRV_PCM_FORMAT_U24_BE */
-    AUDIO_FORMAT_PCM_32_BIT,          /* 10 - SNDRV_PCM_FORMAT_S32_LE */
-    AUDIO_FORMAT_INVALID,             /* 11 - SNDRV_PCM_FORMAT_S32_BE */
-    AUDIO_FORMAT_INVALID,             /* 12 - SNDRV_PCM_FORMAT_U32_LE */
-    AUDIO_FORMAT_INVALID,             /* 13 - SNDRV_PCM_FORMAT_U32_BE */
-    AUDIO_FORMAT_PCM_FLOAT,           /* 14 - SNDRV_PCM_FORMAT_FLOAT_LE */
-    AUDIO_FORMAT_INVALID,             /* 15 - SNDRV_PCM_FORMAT_FLOAT_BE */
-    AUDIO_FORMAT_INVALID,             /* 16 - SNDRV_PCM_FORMAT_FLOAT64_LE */
-    AUDIO_FORMAT_INVALID,             /* 17 - SNDRV_PCM_FORMAT_FLOAT64_BE */
-    AUDIO_FORMAT_INVALID,             /* 18 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE */
-    AUDIO_FORMAT_INVALID,             /* 19 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE */
-    AUDIO_FORMAT_INVALID,             /* 20 - SNDRV_PCM_FORMAT_MU_LAW */
-    AUDIO_FORMAT_INVALID,             /* 21 - SNDRV_PCM_FORMAT_A_LAW */
-    AUDIO_FORMAT_INVALID,             /* 22 - SNDRV_PCM_FORMAT_IMA_ADPCM */
-    AUDIO_FORMAT_INVALID,             /* 23 - SNDRV_PCM_FORMAT_MPEG */
-    AUDIO_FORMAT_INVALID,             /* 24 - SNDRV_PCM_FORMAT_GSM */
-    AUDIO_FORMAT_INVALID,             /* 25 -> 30 (not assigned) */
-    AUDIO_FORMAT_INVALID,
-    AUDIO_FORMAT_INVALID,
-    AUDIO_FORMAT_INVALID,
-    AUDIO_FORMAT_INVALID,
-    AUDIO_FORMAT_INVALID,
-    AUDIO_FORMAT_INVALID,             /* 31 - SNDRV_PCM_FORMAT_SPECIAL */
-    AUDIO_FORMAT_PCM_24_BIT_PACKED,   /* 32 - SNDRV_PCM_FORMAT_S24_3LE */
-    AUDIO_FORMAT_INVALID,             /* 33 - SNDRV_PCM_FORMAT_S24_3BE */
-    AUDIO_FORMAT_INVALID,             /* 34 - SNDRV_PCM_FORMAT_U24_3LE */
-    AUDIO_FORMAT_INVALID,             /* 35 - SNDRV_PCM_FORMAT_U24_3BE */
-    AUDIO_FORMAT_INVALID,             /* 36 - SNDRV_PCM_FORMAT_S20_3LE */
-    AUDIO_FORMAT_INVALID,             /* 37 - SNDRV_PCM_FORMAT_S20_3BE */
-    AUDIO_FORMAT_INVALID,             /* 38 - SNDRV_PCM_FORMAT_U20_3LE */
-    AUDIO_FORMAT_INVALID,             /* 39 - SNDRV_PCM_FORMAT_U20_3BE */
-    AUDIO_FORMAT_INVALID,             /* 40 - SNDRV_PCM_FORMAT_S18_3LE */
-    AUDIO_FORMAT_INVALID,             /* 41 - SNDRV_PCM_FORMAT_S18_3BE */
-    AUDIO_FORMAT_INVALID,             /* 42 - SNDRV_PCM_FORMAT_U18_3LE */
-    AUDIO_FORMAT_INVALID,             /* 43 - SNDRV_PCM_FORMAT_U18_3BE */
-    AUDIO_FORMAT_INVALID,             /* 44 - SNDRV_PCM_FORMAT_G723_24 */
-    AUDIO_FORMAT_INVALID,             /* 45 - SNDRV_PCM_FORMAT_G723_24_1B */
-    AUDIO_FORMAT_INVALID,             /* 46 - SNDRV_PCM_FORMAT_G723_40 */
-    AUDIO_FORMAT_INVALID,             /* 47 - SNDRV_PCM_FORMAT_G723_40_1B */
-    AUDIO_FORMAT_INVALID,             /* 48 - SNDRV_PCM_FORMAT_DSD_U8 */
-    AUDIO_FORMAT_INVALID              /* 49 - SNDRV_PCM_FORMAT_DSD_U16_LE */
-};
-
-/*
- * Returns true if mask indicates support for PCM_16.
- */
-static bool mask_has_pcm_16(struct pcm_mask* mask) {
-    return (mask->bits[0] & 0x0004) != 0;
-}
-
-static audio_format_t get_format_for_mask(struct pcm_mask* mask)
-{
-    int num_slots = sizeof(mask->bits)/ sizeof(mask->bits[0]);
-    int bits_per_slot = sizeof(mask->bits[0]) * 8;
-
-    int table_size = sizeof(format_value_map) / sizeof(format_value_map[0]);
-
-    int slot_index, bit_index, table_index;
-    table_index = 0;
-    int num_written = 0;
-    for (slot_index = 0; slot_index < num_slots; slot_index++) {
-        unsigned bit_mask = 1;
-        for (bit_index = 0; bit_index < bits_per_slot; bit_index++) {
-            if ((mask->bits[slot_index] & bit_mask) != 0) {
-                /* just return the first one */
-                return table_index < table_size
-                           ? format_value_map[table_index]
-                           : AUDIO_FORMAT_INVALID;
-            }
-            bit_mask <<= 1;
-            table_index++;
-        }
+    /* These keys are from hardware/libhardware/include/audio.h */
+    /* supported sample rates */
+    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES)) {
+        char* rates_list = profile_get_sample_rate_strs(profile);
+        str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES,
+                          rates_list);
+        free(rates_list);
     }
 
-    return AUDIO_FORMAT_INVALID;
-}
-
-/*
- * Maps from bit position in pcm_mask to PCM_ format constants.
- */
-static int8_t const pcm_format_value_map[] = {
-    PCM_FORMAT_S8,          /* 00 - SNDRV_PCM_FORMAT_S8 */
-    PCM_FORMAT_INVALID,     /* 01 - SNDRV_PCM_FORMAT_U8 */
-    PCM_FORMAT_S16_LE,      /* 02 - SNDRV_PCM_FORMAT_S16_LE */
-    PCM_FORMAT_INVALID,     /* 03 - SNDRV_PCM_FORMAT_S16_BE */
-    PCM_FORMAT_INVALID,     /* 04 - SNDRV_PCM_FORMAT_U16_LE */
-    PCM_FORMAT_INVALID,     /* 05 - SNDRV_PCM_FORMAT_U16_BE */
-    PCM_FORMAT_S24_3LE,     /* 06 - SNDRV_PCM_FORMAT_S24_LE */
-    PCM_FORMAT_INVALID,     /* 07 - SNDRV_PCM_FORMAT_S24_BE */
-    PCM_FORMAT_INVALID,     /* 08 - SNDRV_PCM_FORMAT_U24_LE */
-    PCM_FORMAT_INVALID,     /* 09 - SNDRV_PCM_FORMAT_U24_BE */
-    PCM_FORMAT_S32_LE,      /* 10 - SNDRV_PCM_FORMAT_S32_LE */
-    PCM_FORMAT_INVALID,     /* 11 - SNDRV_PCM_FORMAT_S32_BE */
-    PCM_FORMAT_INVALID,     /* 12 - SNDRV_PCM_FORMAT_U32_LE */
-    PCM_FORMAT_INVALID,     /* 13 - SNDRV_PCM_FORMAT_U32_BE */
-    PCM_FORMAT_INVALID,     /* 14 - SNDRV_PCM_FORMAT_FLOAT_LE */
-    PCM_FORMAT_INVALID,     /* 15 - SNDRV_PCM_FORMAT_FLOAT_BE */
-    PCM_FORMAT_INVALID,     /* 16 - SNDRV_PCM_FORMAT_FLOAT64_LE */
-    PCM_FORMAT_INVALID,     /* 17 - SNDRV_PCM_FORMAT_FLOAT64_BE */
-    PCM_FORMAT_INVALID,     /* 18 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE */
-    PCM_FORMAT_INVALID,     /* 19 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE */
-    PCM_FORMAT_INVALID,     /* 20 - SNDRV_PCM_FORMAT_MU_LAW */
-    PCM_FORMAT_INVALID,     /* 21 - SNDRV_PCM_FORMAT_A_LAW */
-    PCM_FORMAT_INVALID,     /* 22 - SNDRV_PCM_FORMAT_IMA_ADPCM */
-    PCM_FORMAT_INVALID,     /* 23 - SNDRV_PCM_FORMAT_MPEG */
-    PCM_FORMAT_INVALID,     /* 24 - SNDRV_PCM_FORMAT_GSM */
-    PCM_FORMAT_INVALID,     /* 25 -> 30 (not assigned) */
-    PCM_FORMAT_INVALID,
-    PCM_FORMAT_INVALID,
-    PCM_FORMAT_INVALID,
-    PCM_FORMAT_INVALID,
-    PCM_FORMAT_INVALID,
-    PCM_FORMAT_INVALID,     /* 31 - SNDRV_PCM_FORMAT_SPECIAL */
-    PCM_FORMAT_S24_3LE,     /* 32 - SNDRV_PCM_FORMAT_S24_3LE */ /* ??? */
-    PCM_FORMAT_INVALID,     /* 33 - SNDRV_PCM_FORMAT_S24_3BE */
-    PCM_FORMAT_INVALID,     /* 34 - SNDRV_PCM_FORMAT_U24_3LE */
-    PCM_FORMAT_INVALID,     /* 35 - SNDRV_PCM_FORMAT_U24_3BE */
-    PCM_FORMAT_INVALID,     /* 36 - SNDRV_PCM_FORMAT_S20_3LE */
-    PCM_FORMAT_INVALID,     /* 37 - SNDRV_PCM_FORMAT_S20_3BE */
-    PCM_FORMAT_INVALID,     /* 38 - SNDRV_PCM_FORMAT_U20_3LE */
-    PCM_FORMAT_INVALID,     /* 39 - SNDRV_PCM_FORMAT_U20_3BE */
-    PCM_FORMAT_INVALID,     /* 40 - SNDRV_PCM_FORMAT_S18_3LE */
-    PCM_FORMAT_INVALID,     /* 41 - SNDRV_PCM_FORMAT_S18_3BE */
-    PCM_FORMAT_INVALID,     /* 42 - SNDRV_PCM_FORMAT_U18_3LE */
-    PCM_FORMAT_INVALID,     /* 43 - SNDRV_PCM_FORMAT_U18_3BE */
-    PCM_FORMAT_INVALID,     /* 44 - SNDRV_PCM_FORMAT_G723_24 */
-    PCM_FORMAT_INVALID,     /* 45 - SNDRV_PCM_FORMAT_G723_24_1B */
-    PCM_FORMAT_INVALID,     /* 46 - SNDRV_PCM_FORMAT_G723_40 */
-    PCM_FORMAT_INVALID,     /* 47 - SNDRV_PCM_FORMAT_G723_40_1B */
-    PCM_FORMAT_INVALID,     /* 48 - SNDRV_PCM_FORMAT_DSD_U8 */
-    PCM_FORMAT_INVALID      /* 49 - SNDRV_PCM_FORMAT_DSD_U16_LE */
-};
-
-/*
- * Scans the provided format mask and returns the first non-8 bit sample
- * format supported by the devices.
- */
-static int get_pcm_format_for_mask(struct pcm_mask* mask) {
-    int num_slots = sizeof(mask->bits)/ sizeof(mask->bits[0]);
-    int bits_per_slot = sizeof(mask->bits[0]) * 8;
-
-    int table_size = ARRAY_SIZE(pcm_format_value_map);
-
-    int slot_index, bit_index, table_index;
-    table_index = 0;
-    int num_written = 0;
-    for (slot_index = 0; slot_index < num_slots && table_index < table_size; slot_index++) {
-        unsigned bit_mask = 1;
-        for (bit_index = 0; bit_index < bits_per_slot  && table_index < table_size; bit_index++) {
-            if ((mask->bits[slot_index] & bit_mask) != 0) {
-                /* TODO - we don't want a low-level function to be making this decision */
-                if (table_index != 0) { /* Don't pick 8-bit */
-                    /* just return the first one */
-                    return (int)pcm_format_value_map[table_index];
-                }
-            }
-            bit_mask <<= 1;
-            table_index++;
-        }
+    /* supported channel counts */
+    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS)) {
+        char* channels_list = profile_get_channel_count_strs(profile);
+        str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_CHANNELS,
+                          channels_list);
+        free(channels_list);
     }
 
-    return PCM_FORMAT_INVALID;
-}
-
-static bool test_out_sample_rate(struct audio_device_profile* dev_profile, unsigned rate) {
-    struct pcm_config local_config = cached_output_hardware_config;
-    local_config.rate = rate;
-
-    bool works = false; /* let's be pessimistic */
-    struct pcm * pcm =
-        pcm_open(dev_profile->card, dev_profile->device, dev_profile->direction, &local_config);
-
-    if (pcm != NULL) {
-        works = pcm_is_ready(pcm);
-        pcm_close(pcm);
+    /* supported sample formats */
+    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) {
+        char * format_params = profile_get_format_strs(profile);
+        str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_FORMATS,
+                          format_params);
+        free(format_params);
     }
+    str_parms_destroy(query);
 
-    return works;
-}
+    char* result_str = str_parms_to_str(result);
+    str_parms_destroy(result);
 
-/* sort these highest -> lowest */
-static const unsigned std_sample_rates[] =
-    {48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000};
+    ALOGV("usb:audio_hw::device_get_parameters = %s", result_str);
 
-static char* enum_std_sample_rates(struct audio_device_profile* dev_profile,
-                                   unsigned min, unsigned max)
-{
-    char buffer[128];
-    buffer[0] = '\0';
-    int buffSize = ARRAY_SIZE(buffer);
-
-    char numBuffer[32];
-
-    int numEntries = 0;
-    unsigned index;
-    for(index = 0; index < ARRAY_SIZE(std_sample_rates); index++) {
-        if (std_sample_rates[index] >= min && std_sample_rates[index] <= max &&
-            test_out_sample_rate(dev_profile, std_sample_rates[index])) {
-            if (numEntries++ != 0) {
-                strncat(buffer, "|", buffSize);
-            }
-            snprintf(numBuffer, sizeof(numBuffer), "%u", std_sample_rates[index]);
-            strncat(buffer, numBuffer, buffSize);
-        }
-    }
-
-    return strdup(buffer);
-}
-
-/*
- * Logging
- */
-static void log_pcm_mask(const char* mask_name, struct pcm_mask* mask) {
-    char buff[512];
-    char bit_buff[32];
-    int buffSize = sizeof(buff)/sizeof(buff[0]);
-
-    buff[0] = '\0';
-
-    int num_slots = sizeof(mask->bits) / sizeof(mask->bits[0]);
-    int bits_per_slot = sizeof(mask->bits[0]) * 8;
-
-    int slot_index, bit_index;
-    strcat(buff, "[");
-    for (slot_index = 0; slot_index < num_slots; slot_index++) {
-        unsigned bit_mask = 1;
-        for (bit_index = 0; bit_index < bits_per_slot; bit_index++) {
-            strcat(buff, (mask->bits[slot_index] & bit_mask) != 0 ? "1" : "0");
-            bit_mask <<= 1;
-        }
-        if (slot_index < num_slots - 1) {
-            strcat(buff, ",");
-        }
-    }
-    strcat(buff, "]");
-
-    ALOGV("usb:audio_hw - %s mask:%s", mask_name, buff);
-}
-
-static void log_pcm_params(struct pcm_params * alsa_hw_params) {
-    ALOGV("usb:audio_hw - PCM_PARAM_SAMPLE_BITS min:%u, max:%u",
-          pcm_params_get_min(alsa_hw_params, PCM_PARAM_SAMPLE_BITS),
-          pcm_params_get_max(alsa_hw_params, PCM_PARAM_SAMPLE_BITS));
-    ALOGV("usb:audio_hw - PCM_PARAM_FRAME_BITS min:%u, max:%u",
-          pcm_params_get_min(alsa_hw_params, PCM_PARAM_FRAME_BITS),
-          pcm_params_get_max(alsa_hw_params, PCM_PARAM_FRAME_BITS));
-    log_pcm_mask("PCM_PARAM_FORMAT", pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT));
-    log_pcm_mask("PCM_PARAM_SUBFORMAT", pcm_params_get_mask(alsa_hw_params, PCM_PARAM_SUBFORMAT));
-    ALOGV("usb:audio_hw - PCM_PARAM_CHANNELS min:%u, max:%u",
-          pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS),
-          pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS));
-    ALOGV("usb:audio_hw - PCM_PARAM_RATE min:%u, max:%u",
-          pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE),
-          pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE));
-    ALOGV("usb:audio_hw - PCM_PARAM_PERIOD_TIME min:%u, max:%u",
-          pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_TIME),
-          pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_TIME));
-    ALOGV("usb:audio_hw - PCM_PARAM_PERIOD_SIZE min:%u, max:%u",
-          pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_SIZE),
-          pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_SIZE));
-    ALOGV("usb:audio_hw - PCM_PARAM_PERIOD_BYTES min:%u, max:%u",
-          pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_BYTES),
-          pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_BYTES));
-    ALOGV("usb:audio_hw - PCM_PARAM_PERIODS min:%u, max:%u",
-          pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS),
-          pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIODS));
-    ALOGV("usb:audio_hw - PCM_PARAM_BUFFER_TIME min:%u, max:%u",
-          pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_TIME),
-          pcm_params_get_max(alsa_hw_params, PCM_PARAM_BUFFER_TIME));
-    ALOGV("usb:audio_hw - PCM_PARAM_BUFFER_SIZE min:%u, max:%u",
-          pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_SIZE),
-          pcm_params_get_max(alsa_hw_params, PCM_PARAM_BUFFER_SIZE));
-    ALOGV("usb:audio_hw - PCM_PARAM_BUFFER_BYTES min:%u, max:%u",
-          pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_BYTES),
-          pcm_params_get_max(alsa_hw_params, PCM_PARAM_BUFFER_BYTES));
-    ALOGV("usb:audio_hw - PCM_PARAM_TICK_TIME min:%u, max:%u",
-          pcm_params_get_min(alsa_hw_params, PCM_PARAM_TICK_TIME),
-          pcm_params_get_max(alsa_hw_params, PCM_PARAM_TICK_TIME));
-}
-
-/*
- * Returns the supplied value rounded up to the next even multiple of 16
- */
-static unsigned int round_to_16_mult(unsigned int size) {
-    return (size + 15) & 0xFFFFFFF0;
-}
-
-/*TODO - Evaluate if this value should/can be retrieved from a device-specific property */
-#define MIN_BUFF_TIME   5   /* milliseconds */
-
-/*
- * Returns the system defined minimum period size based on the supplied sample rate
- */
-static unsigned int calc_min_period_size(unsigned int sample_rate) {
-    unsigned int period_size = (sample_rate * MIN_BUFF_TIME) / 1000;
-    return round_to_16_mult(period_size);
-}
-
-/*
- * Reads and decodes configuration info from the specified ALSA card/device
- */
-static int read_alsa_device_config(struct audio_device_profile * dev_profile,
-                                   struct pcm_config * config)
-{
-    ALOGV("usb:audio_hw - read_alsa_device_config(c:%d d:%d t:0x%X)",
-          dev_profile->card, dev_profile->device, dev_profile->direction);
-
-    if (dev_profile->card < 0 || dev_profile->device < 0) {
-        return -EINVAL;
-    }
-
-    struct pcm_params * alsa_hw_params =
-        pcm_params_get(dev_profile->card, dev_profile->device, dev_profile->direction);
-    if (alsa_hw_params == NULL) {
-        return -EINVAL;
-    }
-
-    int ret = 0;
-
-    /*
-     * This Logging will be useful when testing new USB devices.
-     */
-#ifdef LOG_PCM_PARAMS
-    log_pcm_params(alsa_hw_params);
-#endif
-
-    config->channels = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
-    config->rate = pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE);
-    config->period_size = pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_SIZE);
-    /* round this up to a multiple of 16 */
-    config->period_size = round_to_16_mult(config->period_size);
-    /* make sure it is above a minimum value to minimize jitter */
-    unsigned int min_period_size = calc_min_period_size(config->rate);
-    if (config->period_size < min_period_size) {
-        config->period_size = min_period_size;
-    }
-    config->period_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS);
-
-    int format = get_pcm_format_for_mask(pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT));
-    if (format == PCM_FORMAT_INVALID) {
-        ret = -EINVAL;
-    } else {
-        config->format = format;
-    }
-
-    pcm_params_free(alsa_hw_params);
-    return ret;
+    return result_str;
 }
 
 /*
@@ -695,10 +217,14 @@
  * following order: hw device > out stream
  */
 
-/* Helper functions */
+/*
+ * OUT functions
+ */
 static uint32_t out_get_sample_rate(const struct audio_stream *stream)
 {
-    return cached_output_hardware_config.rate;
+    uint32_t rate = proxy_get_sample_rate(&((struct stream_out*)stream)->proxy);
+    ALOGV("out_get_sample_rate() = %d", rate);
+    return rate;
 }
 
 static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
@@ -708,26 +234,43 @@
 
 static size_t out_get_buffer_size(const struct audio_stream *stream)
 {
-    return cached_output_hardware_config.period_size *
-                audio_stream_out_frame_size((const struct audio_stream_out *)stream);
+    const struct stream_out* out = (const struct stream_out*)stream;
+    size_t buffer_size =
+        proxy_get_period_size(&out->proxy) * audio_stream_out_frame_size(&(out->stream));
+    ALOGV("out_get_buffer_size()  = %zu", buffer_size);
+    return buffer_size;
 }
 
 static uint32_t out_get_channels(const struct audio_stream *stream)
 {
-    // Always Stero for now. We will do *some* conversions in this HAL.
-    /* TODO When AudioPolicyManager & AudioFlinger supports arbitrary channels
-       rewrite this to return the ACTUAL channel format */
+    /*
+     * alsa_device_profile * profile = ((struct stream_out*)stream)->profile;
+     * unsigned channel_count = profile_get_channel_count(profile);
+     * uint32_t channel_mask = audio_channel_out_mask_from_count(channel_count);
+     * ALOGV("out_get_channels() = 0x%X count:%d", channel_mask, channel_count);
+     * return channel_mask;
+     */
+
+    /* Always Stereo for now. We will do *some* conversions in this HAL.
+     * TODO When AudioPolicyManager & AudioFlinger supports arbitrary channels
+     * rewrite this to return the ACTUAL channel format */
     return AUDIO_CHANNEL_OUT_STEREO;
 }
 
 static audio_format_t out_get_format(const struct audio_stream *stream)
 {
-    return audio_format_from_pcm_format(cached_output_hardware_config.format);
+    /* Note: The HAL doesn't do any FORMAT conversion at this time. It
+     * Relies on the framework to provide data in the specified format.
+     * This could change in the future.
+     */
+    alsa_device_proxy * proxy = &((struct stream_out*)stream)->proxy;
+    audio_format_t format = audio_format_from_pcm_format(proxy_get_format(proxy));
+    ALOGV("out_get_format() = %d", format);
+    return format;
 }
 
 static int out_set_format(struct audio_stream *stream, audio_format_t format)
 {
-    cached_output_hardware_config.format = pcm_format_from_audio_format(format);
     return 0;
 }
 
@@ -739,8 +282,7 @@
     pthread_mutex_lock(&out->lock);
 
     if (!out->standby) {
-        pcm_close(out->pcm);
-        out->pcm = NULL;
+        proxy_close(&out->proxy);
         out->standby = true;
     }
 
@@ -760,13 +302,13 @@
     ALOGV("usb:audio_hw::out out_set_parameters() keys:%s", kvpairs);
 
     struct stream_out *out = (struct stream_out *)stream;
-    struct str_parms *parms;
+
     char value[32];
     int param_val;
     int routing = 0;
     int ret_value = 0;
 
-    parms = str_parms_create_str(kvpairs);
+    struct str_parms * parms = str_parms_create_str(kvpairs);
     pthread_mutex_lock(&out->dev->lock);
     pthread_mutex_lock(&out->lock);
 
@@ -784,8 +326,7 @@
     }
 
     if (recache_device_params && out->profile->card >= 0 && out->profile->device >= 0) {
-        ret_value = read_alsa_device_config(out->profile, &cached_output_hardware_config);
-        output_hardware_config_is_cached = (ret_value == 0);
+        ret_value = profile_read_device_info(out->profile) ? 0 : -EINVAL;
     }
 
     pthread_mutex_unlock(&out->lock);
@@ -795,78 +336,9 @@
     return ret_value;
 }
 
-static char * device_get_parameters(struct audio_device_profile * dev_profile, const char *keys)
-{
-    ALOGV("usb:audio_hw::device_get_parameters() keys:%s", keys);
-
-    if (dev_profile->card < 0 || dev_profile->device < 0) {
-        return strdup("");
-    }
-
-    unsigned min, max;
-
-    struct str_parms *query = str_parms_create_str(keys);
-    struct str_parms *result = str_parms_create();
-
-    int num_written = 0;
-    char buffer[256];
-    int buffer_size = sizeof(buffer) / sizeof(buffer[0]);
-    char* result_str = NULL;
-
-    struct pcm_params * alsa_hw_params =
-        pcm_params_get(dev_profile->card, dev_profile->device, dev_profile->direction);
-
-    // These keys are from hardware/libhardware/include/audio.h
-    // supported sample rates
-    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES)) {
-        min = pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE);
-        max = pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE);
-
-        char* rates_list = enum_std_sample_rates(dev_profile, min, max);
-        str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, rates_list);
-        free(rates_list);
-    }  // AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES
-
-    // supported channel counts
-    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS)) {
-        // TODO remove this hack when it is superceeded by proper multi-channel support
-        str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_CHANNELS,
-                          dev_profile->direction == PCM_OUT
-                                          ? "AUDIO_CHANNEL_OUT_STEREO"
-                                          : "AUDIO_CHANNEL_IN_STEREO");
-    }  // AUDIO_PARAMETER_STREAM_SUP_CHANNELS
-
-    // supported sample formats
-    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) {
-        // TODO remove this hack when we have support for input in non PCM16 formats
-        if (dev_profile->direction == PCM_IN) {
-            str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_FORMATS, "AUDIO_FORMAT_PCM_16_BIT");
-        } else {
-            struct pcm_mask * format_mask = pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT);
-            char * format_params = get_format_str_for_mask(format_mask);
-            str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_FORMATS, format_params);
-            free(format_params);
-        }
-    }  // AUDIO_PARAMETER_STREAM_SUP_FORMATS
-
-    pcm_params_free(alsa_hw_params);
-
-    result_str = str_parms_to_str(result);
-
-    // done with these...
-    str_parms_destroy(query);
-    str_parms_destroy(result);
-
-    ALOGV("usb:audio_hw::device_get_parameters = %s", result_str);
-
-    return result_str;
-}
-
 static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
 {
-    ALOGV("usb:audio_hw::out out_get_parameters() keys:%s", keys);
-
-    struct stream_out *out = (struct stream_out *) stream;
+    struct stream_out *out = (struct stream_out *)stream;
     pthread_mutex_lock(&out->dev->lock);
     pthread_mutex_lock(&out->lock);
 
@@ -880,13 +352,8 @@
 
 static uint32_t out_get_latency(const struct audio_stream_out *stream)
 {
-    // struct stream_out *out = (struct stream_out *) stream;
-
-    /*TODO Do we need a term here for the USB latency (as reported in the USB descriptors)? */
-    uint32_t latency = (cached_output_hardware_config.period_size
-                    * cached_output_hardware_config.period_count * 1000)
-                    / out_get_sample_rate(&stream->common);
-    return latency;
+    alsa_device_proxy * proxy = &((struct stream_out*)stream)->proxy;
+    return proxy_get_latency(proxy);
 }
 
 static int out_set_volume(struct audio_stream_out *stream, float left, float right)
@@ -897,25 +364,10 @@
 /* must be called with hw device and output stream mutexes locked */
 static int start_output_stream(struct stream_out *out)
 {
-    int return_val = 0;
-
     ALOGV("usb:audio_hw::out start_output_stream(card:%d device:%d)",
           out->profile->card, out->profile->device);
 
-    out->pcm = pcm_open(out->profile->card, out->profile->device, PCM_OUT,
-                        &cached_output_hardware_config);
-
-    if (out->pcm == NULL) {
-        return -ENOMEM;
-    }
-
-    if (out->pcm && !pcm_is_ready(out->pcm)) {
-        ALOGE("audio_hw audio_hw pcm_open() failed: %s", pcm_get_error(out->pcm));
-        pcm_close(out->pcm);
-        return -ENOMEM;
-    }
-
-    return 0;
+    return proxy_open(&out->proxy);
 }
 
 static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, size_t bytes)
@@ -925,6 +377,8 @@
 
     pthread_mutex_lock(&out->dev->lock);
     pthread_mutex_lock(&out->lock);
+    pthread_mutex_unlock(&out->dev->lock);
+
     if (out->standby) {
         ret = start_output_stream(out);
         if (ret != 0) {
@@ -933,10 +387,15 @@
         out->standby = false;
     }
 
-    // Setup conversion buffer
-    // compute maximum potential buffer size.
-    // * 2 for stereo -> quad conversion
-    // * 3/2 for 16bit -> 24 bit conversion
+    alsa_device_profile* profile = out->profile;
+    alsa_device_proxy* proxy = &out->proxy;
+
+    /*
+     *  Setup conversion buffer
+     * compute maximum potential buffer size.
+     * * 2 for stereo -> quad conversion
+     * * 3/2 for 16bit -> 24 bit conversion
+     */
     size_t required_conversion_buffer_size = (bytes * 3 * 2) / 2;
     if (required_conversion_buffer_size > out->conversion_buffer_size) {
         /* TODO Remove this when AudioPolicyManger/AudioFlinger support arbitrary formats
@@ -951,8 +410,9 @@
     /*
      * Num Channels conversion
      */
-    int num_device_channels = cached_output_hardware_config.channels;
-    int num_req_channels = 2; /* always, for now */
+    int num_device_channels = proxy_get_channel_count(proxy);
+    int num_req_channels = 2; /* always for now */
+
     if (num_device_channels != num_req_channels) {
         audio_format_t audio_format = out_get_format(&(out->stream.common));
         unsigned sample_size_in_bytes = audio_bytes_per_sample(audio_format);
@@ -964,17 +424,15 @@
     }
 
     if (write_buff != NULL && num_write_buff_bytes != 0) {
-        pcm_write(out->pcm, write_buff, num_write_buff_bytes);
+        proxy_write(&out->proxy, write_buff, num_write_buff_bytes);
     }
 
     pthread_mutex_unlock(&out->lock);
-    pthread_mutex_unlock(&out->dev->lock);
 
     return bytes;
 
 err:
     pthread_mutex_unlock(&out->lock);
-    pthread_mutex_unlock(&out->dev->lock);
     if (ret != 0) {
         usleep(bytes * 1000000 / audio_stream_out_frame_size(stream) /
                out_get_sample_rate(&stream->common));
@@ -988,6 +446,13 @@
     return -EINVAL;
 }
 
+static int out_get_presentation_position(const struct audio_stream_out *stream,
+                                         uint64_t *frames, struct timespec *timestamp)
+{
+    /* FIXME - This needs to be implemented */
+    return -EINVAL;
+}
+
 static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
 {
     return 0;
@@ -1021,7 +486,7 @@
     if (!out)
         return -ENOMEM;
 
-    // setup function pointers
+    /* setup function pointers */
     out->stream.common.get_sample_rate = out_get_sample_rate;
     out->stream.common.set_sample_rate = out_set_sample_rate;
     out->stream.common.get_buffer_size = out_get_buffer_size;
@@ -1038,41 +503,85 @@
     out->stream.set_volume = out_set_volume;
     out->stream.write = out_write;
     out->stream.get_render_position = out_get_render_position;
+    out->stream.get_presentation_position = out_get_presentation_position;
     out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
 
     out->dev = adev;
 
-    out->profile = &(adev->out_profile);
-    out->profile->direction = PCM_OUT;
+    out->profile = &adev->out_profile;
 
-    if (output_hardware_config_is_cached) {
-        config->sample_rate = cached_output_hardware_config.rate;
+    // build this to hand to the alsa_device_proxy
+    struct pcm_config proxy_config;
 
-        config->format = audio_format_from_pcm_format(cached_output_hardware_config.format);
+    int ret = 0;
 
-        config->channel_mask =
-                audio_channel_out_mask_from_count(cached_output_hardware_config.channels);
-        if (config->channel_mask != AUDIO_CHANNEL_OUT_STEREO) {
-            // Always report STEREO for now.  AudioPolicyManagerBase/AudioFlinger dont' understand
-            // formats with more channels, so we won't get chosen (say with a 4-channel DAC).
-            /*TODO remove this when the above restriction is removed. */
-            config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
-        }
+    /* Rate */
+    if (config->sample_rate == 0) {
+        proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(out->profile);
+    } else if (profile_is_sample_rate_valid(out->profile, config->sample_rate)) {
+        proxy_config.rate = config->sample_rate;
     } else {
-        cached_output_hardware_config = default_alsa_out_config;
-
-        config->format = out_get_format(&out->stream.common);
-        config->channel_mask = out_get_channels(&out->stream.common);
-        config->sample_rate = out_get_sample_rate(&out->stream.common);
+        proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(out->profile);
+        ret = -EINVAL;
     }
 
+    /* Format */
+    if (config->format == AUDIO_FORMAT_DEFAULT) {
+        proxy_config.format = profile_get_default_format(out->profile);
+        config->format = audio_format_from_pcm_format(proxy_config.format);
+    } else {
+        enum pcm_format fmt = pcm_format_from_audio_format(config->format);
+        if (profile_is_format_valid(out->profile, fmt)) {
+            proxy_config.format = fmt;
+        } else {
+            proxy_config.format = profile_get_default_format(out->profile);
+            config->format = audio_format_from_pcm_format(proxy_config.format);
+            ret = -EINVAL;
+        }
+    }
+
+    /* Channels */
+    if (config->channel_mask == AUDIO_CHANNEL_NONE) {
+        /* This will be needed when the framework supports non-stereo output */
+        /* config->channel_mask =
+         *        audio_channel_out_mask_from_count(profile_get_default_channel_count(out->profile));
+         */
+        proxy_config.channels = profile_get_default_channel_count(out->profile);
+        config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+    } else {
+        /* This will be needed when the framework supports non-stereo output */
+        /*
+         * unsigned channel_count = audio_channel_count_from_out_mask(config->channel_mask);
+         * if (profile_is_channel_count_valid(out->profile, channel_count)) {
+         *     proxy_set_channel_count(out->proxy, channel_count);
+         * } else {
+         *     config->channel_mask =
+         *             audio_channel_out_mask_from_count(proxy_get_channel_count(out->proxy));
+         *     ret = -EINVAL;
+         * }
+         */
+        if (config->channel_mask != AUDIO_CHANNEL_OUT_STEREO) {
+            proxy_config.channels = profile_get_default_channel_count(out->profile);
+            config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+            ret = -EINVAL;
+        }  else {
+            proxy_config.channels = profile_get_default_channel_count(out->profile);
+        }
+    }
+
+    proxy_prepare(&out->proxy, out->profile, &proxy_config);
+
+    /* TODO The retry mechanism isn't implemented in AudioPolicyManager/AudioFlinger. */
+    ret = 0;
+
     out->conversion_buffer = NULL;
     out->conversion_buffer_size = 0;
 
     out->standby = true;
 
     *stream_out = &out->stream;
-    return 0;
+
+    return ret;
 
 err_open:
     free(out);
@@ -1086,16 +595,403 @@
     ALOGV("usb:audio_hw::out adev_close_output_stream()");
     struct stream_out *out = (struct stream_out *)stream;
 
-    // Close the pcm device
+    /* Close the pcm device */
     out_standby(&stream->common);
 
     free(out->conversion_buffer);
+
     out->conversion_buffer = NULL;
     out->conversion_buffer_size = 0;
 
     free(stream);
 }
 
+static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
+                                         const struct audio_config *config)
+{
+    /* TODO This needs to be calculated based on format/channels/rate */
+    return 320;
+}
+
+/*
+ * IN functions
+ */
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+    uint32_t rate = proxy_get_sample_rate(&((const struct stream_in *)stream)->proxy);
+    ALOGV("in_get_sample_rate() = %d", rate);
+    return rate;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+    ALOGV("in_set_sample_rate(%d) - NOPE", rate);
+    return -ENOSYS;
+}
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+    const struct stream_in * in = ((const struct stream_in*)stream);
+    size_t buffer_size =
+        proxy_get_period_size(&in->proxy) * audio_stream_in_frame_size(&(in->stream));
+    ALOGV("in_get_buffer_size() = %zd", buffer_size);
+
+    return buffer_size;
+}
+
+static uint32_t in_get_channels(const struct audio_stream *stream)
+{
+    /* TODO Here is the code we need when we support arbitrary channel counts
+     * alsa_device_proxy * proxy = ((struct stream_in*)stream)->proxy;
+     * unsigned channel_count = proxy_get_channel_count(proxy);
+     * uint32_t channel_mask = audio_channel_in_mask_from_count(channel_count);
+     * ALOGV("in_get_channels() = 0x%X count:%d", channel_mask, channel_count);
+     * return channel_mask;
+     */
+    /* TODO When AudioPolicyManager & AudioFlinger supports arbitrary channels
+     rewrite this to return the ACTUAL channel format */
+    return AUDIO_CHANNEL_IN_STEREO;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+    /* TODO Here is the code we need when we support arbitrary input formats
+     * alsa_device_proxy * proxy = ((struct stream_in*)stream)->proxy;
+     * audio_format_t format = audio_format_from_pcm_format(proxy_get_format(proxy));
+     * ALOGV("in_get_format() = %d", format);
+     * return format;
+     */
+    /* Input only supports PCM16 */
+    /* TODO When AudioPolicyManager & AudioFlinger supports arbitrary input formats
+       rewrite this to return the ACTUAL channel format (above) */
+    return AUDIO_FORMAT_PCM_16_BIT;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+    ALOGV("in_set_format(%d) - NOPE", format);
+
+    return -ENOSYS;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+    struct stream_in *in = (struct stream_in *)stream;
+
+    pthread_mutex_lock(&in->dev->lock);
+    pthread_mutex_lock(&in->lock);
+
+    if (!in->standby) {
+        proxy_close(&in->proxy);
+        in->standby = true;
+    }
+
+    pthread_mutex_unlock(&in->lock);
+    pthread_mutex_unlock(&in->dev->lock);
+
+    return 0;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+    return 0;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+    ALOGV("usb: audio_hw::in in_set_parameters() keys:%s", kvpairs);
+
+    struct stream_in *in = (struct stream_in *)stream;
+
+    char value[32];
+    int param_val;
+    int routing = 0;
+    int ret_value = 0;
+
+    struct str_parms * parms = str_parms_create_str(kvpairs);
+
+    pthread_mutex_lock(&in->dev->lock);
+    pthread_mutex_lock(&in->lock);
+
+    bool recache_device_params = false;
+
+    /* Card/Device */
+    param_val = str_parms_get_str(parms, "card", value, sizeof(value));
+    if (param_val >= 0) {
+        in->profile->card = atoi(value);
+        recache_device_params = true;
+    }
+
+    param_val = str_parms_get_str(parms, "device", value, sizeof(value));
+    if (param_val >= 0) {
+        in->profile->device = atoi(value);
+        recache_device_params = true;
+    }
+
+    if (recache_device_params && in->profile->card >= 0 && in->profile->device >= 0) {
+        ret_value = profile_read_device_info(in->profile) ? 0 : -EINVAL;
+     }
+
+    pthread_mutex_unlock(&in->lock);
+    pthread_mutex_unlock(&in->dev->lock);
+
+    str_parms_destroy(parms);
+
+    return ret_value;
+}
+
+static char * in_get_parameters(const struct audio_stream *stream, const char *keys)
+{
+    struct stream_in *in = (struct stream_in *)stream;
+
+    pthread_mutex_lock(&in->dev->lock);
+    pthread_mutex_lock(&in->lock);
+
+    char * params_str =  device_get_parameters(in->profile, keys);
+
+    pthread_mutex_unlock(&in->lock);
+    pthread_mutex_unlock(&in->dev->lock);
+
+    return params_str;
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+    return 0;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+    return 0;
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+    return 0;
+}
+
+/* must be called with hw device and output stream mutexes locked */
+static int start_input_stream(struct stream_in *in)
+{
+    ALOGV("usb:audio_hw::start_input_stream(card:%d device:%d)",
+          in->profile->card, in->profile->device);
+
+    return proxy_open(&in->proxy);
+}
+
+/* TODO mutex stuff here (see out_write) */
+static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t bytes)
+{
+    size_t num_read_buff_bytes = 0;
+    void * read_buff = buffer;
+    void * out_buff = buffer;
+
+    struct stream_in * in = (struct stream_in *)stream;
+
+    pthread_mutex_lock(&in->dev->lock);
+    pthread_mutex_lock(&in->lock);
+    pthread_mutex_unlock(&in->dev->lock);
+
+    if (in->standby) {
+        if (start_input_stream(in) != 0) {
+            goto err;
+        }
+        in->standby = false;
+    }
+
+    alsa_device_profile * profile = in->profile;
+
+    /*
+     * OK, we need to figure out how much data to read to be able to output the requested
+     * number of bytes in the HAL format (16-bit, stereo).
+     */
+    num_read_buff_bytes = bytes;
+    int num_device_channels = proxy_get_channel_count(&in->proxy);
+    int num_req_channels = 2; /* always, for now */
+
+    if (num_device_channels != num_req_channels) {
+        num_read_buff_bytes = (num_device_channels * num_read_buff_bytes) / num_req_channels;
+    }
+
+    enum pcm_format format = proxy_get_format(&in->proxy);
+    if (format == PCM_FORMAT_S24_3LE) {
+        /* 24-bit USB device */
+        num_read_buff_bytes = (3 * num_read_buff_bytes) / 2;
+    } else if (format == PCM_FORMAT_S32_LE) {
+        /* 32-bit USB device */
+        num_read_buff_bytes = num_read_buff_bytes * 2;
+    }
+
+    /* Setup/Realloc the conversion buffer (if necessary). */
+    if (num_read_buff_bytes != bytes) {
+        if (num_read_buff_bytes > in->conversion_buffer_size) {
+            /*TODO Remove this when AudioPolicyManger/AudioFlinger support arbitrary formats
+              (and do these conversions themselves) */
+            in->conversion_buffer_size = num_read_buff_bytes;
+            in->conversion_buffer = realloc(in->conversion_buffer, in->conversion_buffer_size);
+        }
+        read_buff = in->conversion_buffer;
+    }
+
+    if (proxy_read(&in->proxy, read_buff, num_read_buff_bytes) == 0) {
+        /*
+         * Do any conversions necessary to send the data in the format specified to/by the HAL
+         * (but different from the ALSA format), such as 24bit ->16bit, or 4chan -> 2chan.
+         */
+        if (format != PCM_FORMAT_S16_LE) {
+            /* we need to convert */
+            if (num_device_channels != num_req_channels) {
+                out_buff = read_buff;
+            }
+
+            if (format == PCM_FORMAT_S24_3LE) {
+                num_read_buff_bytes =
+                    convert_24_3_to_16(read_buff, num_read_buff_bytes / 3, out_buff);
+            } else if (format == PCM_FORMAT_S32_LE) {
+                num_read_buff_bytes =
+                    convert_32_to_16(read_buff, num_read_buff_bytes / 4, out_buff);
+            } else {
+                goto err;
+            }
+        }
+
+        if (num_device_channels != num_req_channels) {
+            // ALOGV("chans dev:%d req:%d", num_device_channels, num_req_channels);
+
+            out_buff = buffer;
+            /* Num Channels conversion */
+            if (num_device_channels != num_req_channels) {
+                audio_format_t audio_format = in_get_format(&(in->stream.common));
+                unsigned sample_size_in_bytes = audio_bytes_per_sample(audio_format);
+
+                num_read_buff_bytes =
+                    adjust_channels(read_buff, num_device_channels,
+                                    out_buff, num_req_channels,
+                                    sample_size_in_bytes, num_read_buff_bytes);
+            }
+        }
+    }
+
+err:
+    pthread_mutex_unlock(&in->lock);
+
+    return num_read_buff_bytes;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+    return 0;
+}
+
+static int adev_open_input_stream(struct audio_hw_device *dev,
+                                  audio_io_handle_t handle,
+                                  audio_devices_t devices,
+                                  struct audio_config *config,
+                                  struct audio_stream_in **stream_in,
+                                  audio_input_flags_t flags __unused)
+{
+    ALOGV("usb: in adev_open_input_stream() rate:%" PRIu32 ", chanMask:0x%" PRIX32 ", fmt:%" PRIu8,
+          config->sample_rate, config->channel_mask, config->format);
+
+    struct stream_in *in = (struct stream_in *)calloc(1, sizeof(struct stream_in));
+    int ret = 0;
+
+    if (in == NULL)
+        return -ENOMEM;
+
+    /* setup function pointers */
+    in->stream.common.get_sample_rate = in_get_sample_rate;
+    in->stream.common.set_sample_rate = in_set_sample_rate;
+    in->stream.common.get_buffer_size = in_get_buffer_size;
+    in->stream.common.get_channels = in_get_channels;
+    in->stream.common.get_format = in_get_format;
+    in->stream.common.set_format = in_set_format;
+    in->stream.common.standby = in_standby;
+    in->stream.common.dump = in_dump;
+    in->stream.common.set_parameters = in_set_parameters;
+    in->stream.common.get_parameters = in_get_parameters;
+    in->stream.common.add_audio_effect = in_add_audio_effect;
+    in->stream.common.remove_audio_effect = in_remove_audio_effect;
+
+    in->stream.set_gain = in_set_gain;
+    in->stream.read = in_read;
+    in->stream.get_input_frames_lost = in_get_input_frames_lost;
+
+    in->dev = (struct audio_device *)dev;
+
+    in->profile = &in->dev->in_profile;
+
+    struct pcm_config proxy_config;
+
+    /* Rate */
+    if (config->sample_rate == 0) {
+        proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(in->profile);
+    } else if (profile_is_sample_rate_valid(in->profile, config->sample_rate)) {
+        proxy_config.rate = config->sample_rate;
+    } else {
+        proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(in->profile);
+        ret = -EINVAL;
+    }
+
+    /* Format */
+    /* until the framework supports format conversion, just take what it asks for
+     * i.e. AUDIO_FORMAT_PCM_16_BIT */
+    if (config->format == AUDIO_FORMAT_DEFAULT) {
+        /* just return AUDIO_FORMAT_PCM_16_BIT until the framework supports other input
+         * formats */
+        config->format = AUDIO_FORMAT_PCM_16_BIT;
+        proxy_config.format = PCM_FORMAT_S16_LE;
+    } else if (config->format == AUDIO_FORMAT_PCM_16_BIT) {
+        /* Always accept AUDIO_FORMAT_PCM_16_BIT until the framework supports other input
+         * formats */
+        proxy_config.format = PCM_FORMAT_S16_LE;
+    } else {
+        /* When the framework support other formats, validate here */
+        config->format = AUDIO_FORMAT_PCM_16_BIT;
+        proxy_config.format = PCM_FORMAT_S16_LE;
+        ret = -EINVAL;
+    }
+
+    if (config->channel_mask == AUDIO_CHANNEL_NONE) {
+        /* just return AUDIO_CHANNEL_IN_STEREO until the framework supports other input
+         * formats */
+        config->channel_mask = AUDIO_CHANNEL_IN_STEREO;
+
+    } else if (config->channel_mask != AUDIO_CHANNEL_IN_STEREO) {
+        /* allow only stereo capture for now */
+        config->channel_mask = AUDIO_CHANNEL_IN_STEREO;
+        ret = -EINVAL;
+    }
+    // proxy_config.channels = 0;  /* don't change */
+    proxy_config.channels = profile_get_default_channel_count(in->profile);
+
+    proxy_prepare(&in->proxy, in->profile, &proxy_config);
+
+    in->standby = true;
+
+    in->conversion_buffer = NULL;
+    in->conversion_buffer_size = 0;
+
+    *stream_in = &in->stream;
+
+    return ret;
+}
+
+static void adev_close_input_stream(struct audio_hw_device *dev, struct audio_stream_in *stream)
+{
+    struct stream_in *in = (struct stream_in *)stream;
+
+    /* Close the pcm device */
+    in_standby(&stream->common);
+
+    free(in->conversion_buffer);
+
+    free(stream);
+}
+
+/*
+ * ADEV Functions
+ */
 static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
 {
     return 0;
@@ -1136,375 +1032,6 @@
     return -ENOSYS;
 }
 
-static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
-                                         const struct audio_config *config)
-{
-    return 0;
-}
-
-/* Helper functions */
-static uint32_t in_get_sample_rate(const struct audio_stream *stream)
-{
-    return cached_input_hardware_config.rate;
-}
-
-static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
-{
-    return -ENOSYS;
-}
-
-static size_t in_get_buffer_size(const struct audio_stream *stream)
-{
-    size_t buffer_size = cached_input_hardware_config.period_size *
-                            audio_stream_in_frame_size((const struct audio_stream_in *)stream);
-    ALOGV("usb: in_get_buffer_size() = %zu", buffer_size);
-    return buffer_size;
-}
-
-static uint32_t in_get_channels(const struct audio_stream *stream)
-{
-    // just report stereo for now
-    return AUDIO_CHANNEL_IN_STEREO;
-}
-
-static audio_format_t in_get_format(const struct audio_stream *stream)
-{
-    const struct stream_in * in_stream = (const struct stream_in *)stream;
-
-    ALOGV("in_get_format() = %d -> %d", in_stream->input_framework_format,
-          audio_format_from_pcm_format(in_stream->input_framework_format));
-    /* return audio_format_from_pcm_format(cached_input_hardware_config.format); */
-    return audio_format_from_pcm_format(in_stream->input_framework_format);
-}
-
-static int in_set_format(struct audio_stream *stream, audio_format_t format)
-{
-    return -ENOSYS;
-}
-
-static int in_standby(struct audio_stream *stream)
-{
-    struct stream_in *in = (struct stream_in *) stream;
-
-    pthread_mutex_lock(&in->dev->lock);
-    pthread_mutex_lock(&in->lock);
-
-    if (!in->standby) {
-        pcm_close(in->pcm);
-        in->pcm = NULL;
-        in->standby = true;
-    }
-
-    pthread_mutex_unlock(&in->lock);
-    pthread_mutex_unlock(&in->dev->lock);
-
-    return 0;
-}
-
-static int in_dump(const struct audio_stream *stream, int fd)
-{
-    return 0;
-}
-
-static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
-{
-    ALOGV("usb: audio_hw::in in_set_parameters() keys:%s", kvpairs);
-
-    struct stream_in *in = (struct stream_in *)stream;
-    struct str_parms *parms;
-    char value[32];
-    int param_val;
-    int routing = 0;
-    int ret_value = 0;
-
-    parms = str_parms_create_str(kvpairs);
-    pthread_mutex_lock(&in->dev->lock);
-    pthread_mutex_lock(&in->lock);
-
-    bool recache_device_params = false;
-
-    // Card/Device
-    param_val = str_parms_get_str(parms, "card", value, sizeof(value));
-    if (param_val >= 0) {
-        in->profile->card = atoi(value);
-        recache_device_params = true;
-    }
-
-    param_val = str_parms_get_str(parms, "device", value, sizeof(value));
-    if (param_val >= 0) {
-        in->profile->device = atoi(value);
-        recache_device_params = true;
-    }
-
-    if (recache_device_params && in->profile->card >= 0 && in->profile->device >= 0) {
-        ret_value = read_alsa_device_config(in->profile, &cached_input_hardware_config);
-        input_hardware_config_is_cached = (ret_value == 0);
-    }
-
-    pthread_mutex_unlock(&in->lock);
-    pthread_mutex_unlock(&in->dev->lock);
-    str_parms_destroy(parms);
-
-    return ret_value;
-}
-
-static char * in_get_parameters(const struct audio_stream *stream, const char *keys) {
-    ALOGV("usb:audio_hw::in in_get_parameters() keys:%s", keys);
-
-    struct stream_in *in = (struct stream_in *)stream;
-    pthread_mutex_lock(&in->dev->lock);
-    pthread_mutex_lock(&in->lock);
-
-    char * params_str  = device_get_parameters(in->profile, keys);
-
-    pthread_mutex_unlock(&in->lock);
-    pthread_mutex_unlock(&in->dev->lock);
-
-    return params_str;
-}
-
-static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
-{
-    return 0;
-}
-
-static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
-{
-    return 0;
-}
-
-static int in_set_gain(struct audio_stream_in *stream, float gain)
-{
-    return 0;
-}
-
-/* must be called with hw device and output stream mutexes locked */
-static int start_input_stream(struct stream_in *in) {
-    int return_val = 0;
-
-    ALOGV("usb:audio_hw::start_input_stream(card:%d device:%d)",
-          in->profile->card, in->profile->device);
-
-    in->pcm = pcm_open(in->profile->card, in->profile->device, PCM_IN,
-                       &cached_input_hardware_config);
-    if (in->pcm == NULL) {
-        ALOGE("usb:audio_hw pcm_open() in->pcm == NULL");
-        return -ENOMEM;
-    }
-
-    if (in->pcm && !pcm_is_ready(in->pcm)) {
-        ALOGE("usb:audio_hw audio_hw pcm_open() failed: %s", pcm_get_error(in->pcm));
-        pcm_close(in->pcm);
-        return -ENOMEM;
-    }
-
-    return 0;
-}
-
-/* TODO mutex stuff here (see out_write) */
-static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t bytes)
-{
-    size_t num_read_buff_bytes = 0;
-    void * read_buff = buffer;
-    void * out_buff = buffer;
-
-    struct stream_in * in = (struct stream_in *) stream;
-
-    pthread_mutex_lock(&in->dev->lock);
-    pthread_mutex_lock(&in->lock);
-
-    if (in->standby) {
-        if (start_input_stream(in) != 0) {
-            goto err;
-        }
-        in->standby = false;
-    }
-
-    // OK, we need to figure out how much data to read to be able to output the requested
-    // number of bytes in the HAL format (16-bit, stereo).
-    num_read_buff_bytes = bytes;
-    int num_device_channels = cached_input_hardware_config.channels;
-    int num_req_channels = 2; /* always, for now */
-
-    if (num_device_channels != num_req_channels) {
-        num_read_buff_bytes = (num_device_channels * num_read_buff_bytes) / num_req_channels;
-    }
-
-    /* Assume (for now) that in->input_framework_format == PCM_FORMAT_S16_LE */
-    if (cached_input_hardware_config.format == PCM_FORMAT_S24_3LE) {
-        /* 24-bit USB device */
-        num_read_buff_bytes = (3 * num_read_buff_bytes) / 2;
-    } else if (cached_input_hardware_config.format == PCM_FORMAT_S32_LE) {
-        /* 32-bit USB device */
-        num_read_buff_bytes = num_read_buff_bytes * 2;
-    }
-
-    // Setup/Realloc the conversion buffer (if necessary).
-    if (num_read_buff_bytes != bytes) {
-        if (num_read_buff_bytes > in->conversion_buffer_size) {
-            /*TODO Remove this when AudioPolicyManger/AudioFlinger support arbitrary formats
-              (and do these conversions themselves) */
-            in->conversion_buffer_size = num_read_buff_bytes;
-            in->conversion_buffer = realloc(in->conversion_buffer, in->conversion_buffer_size);
-        }
-        read_buff = in->conversion_buffer;
-    }
-
-    if (pcm_read(in->pcm, read_buff, num_read_buff_bytes) == 0) {
-        /*
-         * Do any conversions necessary to send the data in the format specified to/by the HAL
-         * (but different from the ALSA format), such as 24bit ->16bit, or 4chan -> 2chan.
-         */
-        if (cached_input_hardware_config.format != PCM_FORMAT_S16_LE) {
-            // we need to convert
-            if (num_device_channels != num_req_channels) {
-                out_buff = read_buff;
-            }
-
-            if (cached_input_hardware_config.format == PCM_FORMAT_S24_3LE) {
-                num_read_buff_bytes =
-                    convert_24_3_to_16(read_buff, num_read_buff_bytes / 3, out_buff);
-            } else if (cached_input_hardware_config.format == PCM_FORMAT_S32_LE) {
-                num_read_buff_bytes =
-                    convert_32_to_16(read_buff, num_read_buff_bytes / 4, out_buff);
-            }
-            else {
-                goto err;
-            }
-        }
-
-        if (num_device_channels != num_req_channels) {
-            out_buff = buffer;
-            /* Num Channels conversion */
-            if (num_device_channels != num_req_channels) {
-                audio_format_t audio_format = in_get_format(&(in->stream.common));
-                unsigned sample_size_in_bytes = audio_bytes_per_sample(audio_format);
-
-                num_read_buff_bytes =
-                    adjust_channels(read_buff, num_device_channels,
-                                    out_buff, num_req_channels,
-                                    sample_size_in_bytes, num_read_buff_bytes);
-            }
-        }
-    }
-
-err:
-    pthread_mutex_unlock(&in->lock);
-    pthread_mutex_unlock(&in->dev->lock);
-
-    return num_read_buff_bytes;
-}
-
-static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
-{
-    return 0;
-}
-
-static int adev_open_input_stream(struct audio_hw_device *dev,
-                                  audio_io_handle_t handle,
-                                  audio_devices_t devices,
-                                  struct audio_config *config,
-                                  struct audio_stream_in **stream_in,
-                                  audio_input_flags_t flags __unused)
-{
-    ALOGV("usb: in adev_open_input_stream() rate:%" PRIu32 ", chanMask:0x%" PRIX32 ", fmt:%" PRIu8,
-          config->sample_rate, config->channel_mask, config->format);
-
-    struct stream_in *in = (struct stream_in *)calloc(1, sizeof(struct stream_in));
-    int ret = 0;
-
-    if (in == NULL)
-        return -ENOMEM;
-
-    // setup function pointers
-    in->stream.common.get_sample_rate = in_get_sample_rate;
-    in->stream.common.set_sample_rate = in_set_sample_rate;
-    in->stream.common.get_buffer_size = in_get_buffer_size;
-    in->stream.common.get_channels = in_get_channels;
-    in->stream.common.get_format = in_get_format;
-    in->stream.common.set_format = in_set_format;
-    in->stream.common.standby = in_standby;
-    in->stream.common.dump = in_dump;
-    in->stream.common.set_parameters = in_set_parameters;
-    in->stream.common.get_parameters = in_get_parameters;
-    in->stream.common.add_audio_effect = in_add_audio_effect;
-    in->stream.common.remove_audio_effect = in_remove_audio_effect;
-
-    in->stream.set_gain = in_set_gain;
-    in->stream.read = in_read;
-    in->stream.get_input_frames_lost = in_get_input_frames_lost;
-
-    in->input_framework_format = PCM_FORMAT_S16_LE;
-
-    in->dev = (struct audio_device *)dev;
-
-    in->profile = &(in->dev->in_profile);
-    in->profile->direction = PCM_IN;
-
-    if (!input_hardware_config_is_cached) {
-        // just return defaults until we can actually query the device.
-        cached_input_hardware_config = default_alsa_in_config;
-    }
-
-    /* Rate */
-    /* TODO Check that the requested rate is valid for the connected device */
-    if (config->sample_rate == 0) {
-        config->sample_rate = cached_input_hardware_config.rate;
-    } else {
-        cached_input_hardware_config.rate = config->sample_rate;
-    }
-
-    /* Format */
-    /* until the framework supports format conversion, just take what it asks for
-     * i.e. AUDIO_FORMAT_PCM_16_BIT */
-    /* config->format = audio_format_from_pcm_format(cached_input_hardware_config.format); */
-    if (config->format == AUDIO_FORMAT_DEFAULT) {
-        /* just return AUDIO_FORMAT_PCM_16_BIT until the framework supports other input
-         * formats */
-        config->format = AUDIO_FORMAT_PCM_16_BIT;
-    } else if (config->format == AUDIO_FORMAT_PCM_16_BIT) {
-        /* Always accept AUDIO_FORMAT_PCM_16_BIT until the framework supports other input
-         * formats */
-    } else {
-        /* When the framework support other formats, validate here */
-        config->format = AUDIO_FORMAT_PCM_16_BIT;
-        ret = -EINVAL;
-    }
-
-    /* don't change the cached_input_hardware_config, we will open it as what it is and
-     * convert as necessary */
-    if (config->channel_mask == AUDIO_CHANNEL_NONE) {
-        /* just return AUDIO_CHANNEL_IN_STEREO until the framework supports other input
-         * formats */
-        config->channel_mask = AUDIO_CHANNEL_IN_STEREO;
-    } else if (config->channel_mask != AUDIO_CHANNEL_IN_STEREO) {
-        /* allow only stereo capture for now */
-        config->channel_mask = AUDIO_CHANNEL_IN_STEREO;
-        ret = -EINVAL;
-    }
-
-    in->standby = true;
-
-    in->conversion_buffer = NULL;
-    in->conversion_buffer_size = 0;
-
-    *stream_in = &in->stream;
-
-    return ret;
-}
-
-static void adev_close_input_stream(struct audio_hw_device *dev, struct audio_stream_in *stream)
-{
-    struct stream_in *in = (struct stream_in *)stream;
-
-    // Close the pcm device
-    in_standby(&stream->common);
-
-    free(in->conversion_buffer);
-
-    free(stream);
-}
-
 static int adev_dump(const audio_hw_device_t *device, int fd)
 {
     return 0;
@@ -1515,9 +1042,6 @@
     struct audio_device *adev = (struct audio_device *)device;
     free(device);
 
-    output_hardware_config_is_cached = false;
-    input_hardware_config_is_cached = false;
-
     return 0;
 }
 
@@ -1530,9 +1054,12 @@
     if (!adev)
         return -ENOMEM;
 
+    profile_init(&adev->out_profile, PCM_OUT);
+    profile_init(&adev->in_profile, PCM_IN);
+
     adev->hw_device.common.tag = HARDWARE_DEVICE_TAG;
     adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
-    adev->hw_device.common.module = (struct hw_module_t *) module;
+    adev->hw_device.common.module = (struct hw_module_t *)module;
     adev->hw_device.common.close = adev_close;
 
     adev->hw_device.init_check = adev_init_check;
diff --git a/modules/usbaudio/format.c b/modules/usbaudio/format.c
new file mode 100644
index 0000000..6aac1d3
--- /dev/null
+++ b/modules/usbaudio/format.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2014 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 "usb_profile"
+/*#define LOG_NDEBUG 0*/
+
+#include "format.h"
+
+#include <tinyalsa/asoundlib.h>
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+/*
+ * Maps from bit position in pcm_mask to AUDIO_ format constants.
+ */
+static audio_format_t const format_value_map[] = {
+    AUDIO_FORMAT_PCM_8_BIT,           /* 00 - SNDRV_PCM_FORMAT_S8 */
+    AUDIO_FORMAT_PCM_8_BIT,           /* 01 - SNDRV_PCM_FORMAT_U8 */
+    AUDIO_FORMAT_PCM_16_BIT,          /* 02 - SNDRV_PCM_FORMAT_S16_LE */
+    AUDIO_FORMAT_INVALID,             /* 03 - SNDRV_PCM_FORMAT_S16_BE */
+    AUDIO_FORMAT_INVALID,             /* 04 - SNDRV_PCM_FORMAT_U16_LE */
+    AUDIO_FORMAT_INVALID,             /* 05 - SNDRV_PCM_FORMAT_U16_BE */
+    AUDIO_FORMAT_INVALID,             /* 06 - SNDRV_PCM_FORMAT_S24_LE */
+    AUDIO_FORMAT_INVALID,             /* 07 - SNDRV_PCM_FORMAT_S24_BE */
+    AUDIO_FORMAT_INVALID,             /* 08 - SNDRV_PCM_FORMAT_U24_LE */
+    AUDIO_FORMAT_INVALID,             /* 09 - SNDRV_PCM_FORMAT_U24_BE */
+    AUDIO_FORMAT_PCM_32_BIT,          /* 10 - SNDRV_PCM_FORMAT_S32_LE */
+    AUDIO_FORMAT_INVALID,             /* 11 - SNDRV_PCM_FORMAT_S32_BE */
+    AUDIO_FORMAT_INVALID,             /* 12 - SNDRV_PCM_FORMAT_U32_LE */
+    AUDIO_FORMAT_INVALID,             /* 13 - SNDRV_PCM_FORMAT_U32_BE */
+    AUDIO_FORMAT_PCM_FLOAT,           /* 14 - SNDRV_PCM_FORMAT_FLOAT_LE */
+    AUDIO_FORMAT_INVALID,             /* 15 - SNDRV_PCM_FORMAT_FLOAT_BE */
+    AUDIO_FORMAT_INVALID,             /* 16 - SNDRV_PCM_FORMAT_FLOAT64_LE */
+    AUDIO_FORMAT_INVALID,             /* 17 - SNDRV_PCM_FORMAT_FLOAT64_BE */
+    AUDIO_FORMAT_INVALID,             /* 18 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE */
+    AUDIO_FORMAT_INVALID,             /* 19 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE */
+    AUDIO_FORMAT_INVALID,             /* 20 - SNDRV_PCM_FORMAT_MU_LAW */
+    AUDIO_FORMAT_INVALID,             /* 21 - SNDRV_PCM_FORMAT_A_LAW */
+    AUDIO_FORMAT_INVALID,             /* 22 - SNDRV_PCM_FORMAT_IMA_ADPCM */
+    AUDIO_FORMAT_INVALID,             /* 23 - SNDRV_PCM_FORMAT_MPEG */
+    AUDIO_FORMAT_INVALID,             /* 24 - SNDRV_PCM_FORMAT_GSM */
+    AUDIO_FORMAT_INVALID,             /* 25 -> 30 (not assigned) */
+    AUDIO_FORMAT_INVALID,
+    AUDIO_FORMAT_INVALID,
+    AUDIO_FORMAT_INVALID,
+    AUDIO_FORMAT_INVALID,
+    AUDIO_FORMAT_INVALID,
+    AUDIO_FORMAT_INVALID,             /* 31 - SNDRV_PCM_FORMAT_SPECIAL */
+    AUDIO_FORMAT_PCM_24_BIT_PACKED,   /* 32 - SNDRV_PCM_FORMAT_S24_3LE */
+    AUDIO_FORMAT_INVALID,             /* 33 - SNDRV_PCM_FORMAT_S24_3BE */
+    AUDIO_FORMAT_INVALID,             /* 34 - SNDRV_PCM_FORMAT_U24_3LE */
+    AUDIO_FORMAT_INVALID,             /* 35 - SNDRV_PCM_FORMAT_U24_3BE */
+    AUDIO_FORMAT_INVALID,             /* 36 - SNDRV_PCM_FORMAT_S20_3LE */
+    AUDIO_FORMAT_INVALID,             /* 37 - SNDRV_PCM_FORMAT_S20_3BE */
+    AUDIO_FORMAT_INVALID,             /* 38 - SNDRV_PCM_FORMAT_U20_3LE */
+    AUDIO_FORMAT_INVALID,             /* 39 - SNDRV_PCM_FORMAT_U20_3BE */
+    AUDIO_FORMAT_INVALID,             /* 40 - SNDRV_PCM_FORMAT_S18_3LE */
+    AUDIO_FORMAT_INVALID,             /* 41 - SNDRV_PCM_FORMAT_S18_3BE */
+    AUDIO_FORMAT_INVALID,             /* 42 - SNDRV_PCM_FORMAT_U18_3LE */
+    AUDIO_FORMAT_INVALID,             /* 43 - SNDRV_PCM_FORMAT_U18_3BE */
+    AUDIO_FORMAT_INVALID,             /* 44 - SNDRV_PCM_FORMAT_G723_24 */
+    AUDIO_FORMAT_INVALID,             /* 45 - SNDRV_PCM_FORMAT_G723_24_1B */
+    AUDIO_FORMAT_INVALID,             /* 46 - SNDRV_PCM_FORMAT_G723_40 */
+    AUDIO_FORMAT_INVALID,             /* 47 - SNDRV_PCM_FORMAT_G723_40_1B */
+    AUDIO_FORMAT_INVALID,             /* 48 - SNDRV_PCM_FORMAT_DSD_U8 */
+    AUDIO_FORMAT_INVALID              /* 49 - SNDRV_PCM_FORMAT_DSD_U16_LE */
+};
+
+audio_format_t get_format_for_mask(struct pcm_mask* mask)
+{
+    int num_slots = sizeof(mask->bits) / sizeof(mask->bits[0]);
+    int bits_per_slot = sizeof(mask->bits[0]) * 8;
+
+    int table_size = sizeof(format_value_map) / sizeof(format_value_map[0]);
+
+    int slot_index, bit_index, table_index;
+    table_index = 0;
+    int num_written = 0;
+    for (slot_index = 0; slot_index < num_slots; slot_index++) {
+        unsigned bit_mask = 1;
+        for (bit_index = 0; bit_index < bits_per_slot; bit_index++) {
+            /* don't return b-bit formats even if they are supported */
+            if (table_index >= 2 && (mask->bits[slot_index] & bit_mask) != 0) {
+                /* just return the first one */
+                return table_index < table_size
+                           ? format_value_map[table_index]
+                           : AUDIO_FORMAT_INVALID;
+            }
+            bit_mask <<= 1;
+            table_index++;
+        }
+    }
+
+    return AUDIO_FORMAT_INVALID;
+}
+
+/*
+ * Maps from bit position in pcm_mask to PCM_ format constants.
+ */
+int8_t const pcm_format_value_map[50] = {
+    PCM_FORMAT_S8,          /* 00 - SNDRV_PCM_FORMAT_S8 */
+    PCM_FORMAT_INVALID,     /* 01 - SNDRV_PCM_FORMAT_U8 */
+    PCM_FORMAT_S16_LE,      /* 02 - SNDRV_PCM_FORMAT_S16_LE */
+    PCM_FORMAT_INVALID,     /* 03 - SNDRV_PCM_FORMAT_S16_BE */
+    PCM_FORMAT_INVALID,     /* 04 - SNDRV_PCM_FORMAT_U16_LE */
+    PCM_FORMAT_INVALID,     /* 05 - SNDRV_PCM_FORMAT_U16_BE */
+    PCM_FORMAT_S24_3LE,     /* 06 - SNDRV_PCM_FORMAT_S24_LE */
+    PCM_FORMAT_INVALID,     /* 07 - SNDRV_PCM_FORMAT_S24_BE */
+    PCM_FORMAT_INVALID,     /* 08 - SNDRV_PCM_FORMAT_U24_LE */
+    PCM_FORMAT_INVALID,     /* 09 - SNDRV_PCM_FORMAT_U24_BE */
+    PCM_FORMAT_S32_LE,      /* 10 - SNDRV_PCM_FORMAT_S32_LE */
+    PCM_FORMAT_INVALID,     /* 11 - SNDRV_PCM_FORMAT_S32_BE */
+    PCM_FORMAT_INVALID,     /* 12 - SNDRV_PCM_FORMAT_U32_LE */
+    PCM_FORMAT_INVALID,     /* 13 - SNDRV_PCM_FORMAT_U32_BE */
+    PCM_FORMAT_INVALID,     /* 14 - SNDRV_PCM_FORMAT_FLOAT_LE */
+    PCM_FORMAT_INVALID,     /* 15 - SNDRV_PCM_FORMAT_FLOAT_BE */
+    PCM_FORMAT_INVALID,     /* 16 - SNDRV_PCM_FORMAT_FLOAT64_LE */
+    PCM_FORMAT_INVALID,     /* 17 - SNDRV_PCM_FORMAT_FLOAT64_BE */
+    PCM_FORMAT_INVALID,     /* 18 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE */
+    PCM_FORMAT_INVALID,     /* 19 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE */
+    PCM_FORMAT_INVALID,     /* 20 - SNDRV_PCM_FORMAT_MU_LAW */
+    PCM_FORMAT_INVALID,     /* 21 - SNDRV_PCM_FORMAT_A_LAW */
+    PCM_FORMAT_INVALID,     /* 22 - SNDRV_PCM_FORMAT_IMA_ADPCM */
+    PCM_FORMAT_INVALID,     /* 23 - SNDRV_PCM_FORMAT_MPEG */
+    PCM_FORMAT_INVALID,     /* 24 - SNDRV_PCM_FORMAT_GSM */
+    PCM_FORMAT_INVALID,     /* 25 -> 30 (not assigned) */
+    PCM_FORMAT_INVALID,
+    PCM_FORMAT_INVALID,
+    PCM_FORMAT_INVALID,
+    PCM_FORMAT_INVALID,
+    PCM_FORMAT_INVALID,
+    PCM_FORMAT_INVALID,     /* 31 - SNDRV_PCM_FORMAT_SPECIAL */
+    PCM_FORMAT_S24_3LE,     /* 32 - SNDRV_PCM_FORMAT_S24_3LE */ /* ??? */
+    PCM_FORMAT_INVALID,     /* 33 - SNDRV_PCM_FORMAT_S24_3BE */
+    PCM_FORMAT_INVALID,     /* 34 - SNDRV_PCM_FORMAT_U24_3LE */
+    PCM_FORMAT_INVALID,     /* 35 - SNDRV_PCM_FORMAT_U24_3BE */
+    PCM_FORMAT_INVALID,     /* 36 - SNDRV_PCM_FORMAT_S20_3LE */
+    PCM_FORMAT_INVALID,     /* 37 - SNDRV_PCM_FORMAT_S20_3BE */
+    PCM_FORMAT_INVALID,     /* 38 - SNDRV_PCM_FORMAT_U20_3LE */
+    PCM_FORMAT_INVALID,     /* 39 - SNDRV_PCM_FORMAT_U20_3BE */
+    PCM_FORMAT_INVALID,     /* 40 - SNDRV_PCM_FORMAT_S18_3LE */
+    PCM_FORMAT_INVALID,     /* 41 - SNDRV_PCM_FORMAT_S18_3BE */
+    PCM_FORMAT_INVALID,     /* 42 - SNDRV_PCM_FORMAT_U18_3LE */
+    PCM_FORMAT_INVALID,     /* 43 - SNDRV_PCM_FORMAT_U18_3BE */
+    PCM_FORMAT_INVALID,     /* 44 - SNDRV_PCM_FORMAT_G723_24 */
+    PCM_FORMAT_INVALID,     /* 45 - SNDRV_PCM_FORMAT_G723_24_1B */
+    PCM_FORMAT_INVALID,     /* 46 - SNDRV_PCM_FORMAT_G723_40 */
+    PCM_FORMAT_INVALID,     /* 47 - SNDRV_PCM_FORMAT_G723_40_1B */
+    PCM_FORMAT_INVALID,     /* 48 - SNDRV_PCM_FORMAT_DSD_U8 */
+    PCM_FORMAT_INVALID      /* 49 - SNDRV_PCM_FORMAT_DSD_U16_LE */
+};
+
+/*
+ * Scans the provided format mask and returns the first non-8 bit sample
+ * format supported by the devices.
+ */
+enum pcm_format get_pcm_format_for_mask(struct pcm_mask* mask)
+{
+    int num_slots = ARRAY_SIZE(mask->bits);
+    int bits_per_slot = sizeof(mask->bits[0]) * 8;
+
+    int table_size = ARRAY_SIZE(pcm_format_value_map);
+
+    int slot_index, bit_index, table_index;
+    table_index = 0;
+    int num_written = 0;
+    for (slot_index = 0; slot_index < num_slots && table_index < table_size; slot_index++) {
+        unsigned bit_mask = 1;
+        for (bit_index = 0; bit_index < bits_per_slot && table_index < table_size; bit_index++) {
+            /* skip any 8-bit formats */
+            if (table_index >= 2 && (mask->bits[slot_index] & bit_mask) != 0) {
+                /* just return the first one which will be at least 16-bit */
+                return (int)pcm_format_value_map[table_index];
+            }
+            bit_mask <<= 1;
+            table_index++;
+        }
+    }
+
+    return PCM_FORMAT_INVALID;
+}
diff --git a/modules/usbaudio/format.h b/modules/usbaudio/format.h
new file mode 100644
index 0000000..e23935e
--- /dev/null
+++ b/modules/usbaudio/format.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_FORMAT_H
+#define ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_FORMAT_H
+
+#include <system/audio.h>
+
+#include <tinyalsa/asoundlib.h>
+
+audio_format_t get_format_for_mask(struct pcm_mask* mask);
+enum pcm_format get_pcm_format_for_mask(struct pcm_mask* mask);
+
+#endif /* ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_FORMAT_H */
diff --git a/modules/usbaudio/logging.c b/modules/usbaudio/logging.c
new file mode 100644
index 0000000..0a05511
--- /dev/null
+++ b/modules/usbaudio/logging.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 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 "usb_logging"
+/*#define LOG_NDEBUG 0*/
+
+#include <string.h>
+
+#include <log/log.h>
+
+#include "logging.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+/*
+ * Logging
+ */
+void log_pcm_mask(const char* mask_name, struct pcm_mask* mask)
+{
+    const size_t num_slots = ARRAY_SIZE(mask->bits);
+    const size_t bits_per_slot = (sizeof(mask->bits[0]) * 8);
+    const size_t chars_per_slot = (bits_per_slot + 1); /* comma */
+
+    const size_t BUFF_SIZE =
+            (num_slots * chars_per_slot + 2 + 1);  /* brackets and null-terminator */
+    char buff[BUFF_SIZE];
+    buff[0] = '\0';
+
+    size_t slot_index, bit_index;
+    strcat(buff, "[");
+    for (slot_index = 0; slot_index < num_slots; slot_index++) {
+        unsigned bit_mask = 1;
+        for (bit_index = 0; bit_index < bits_per_slot; bit_index++) {
+            strcat(buff, (mask->bits[slot_index] & bit_mask) != 0 ? "1" : "0");
+            bit_mask <<= 1;
+        }
+        if (slot_index < num_slots - 1) {
+            strcat(buff, ",");
+        }
+    }
+    strcat(buff, "]");
+
+    ALOGV("%s: mask:%s", mask_name, buff);
+}
+
+void log_pcm_params(struct pcm_params * alsa_hw_params)
+{
+    ALOGV("usb:audio_hw - PCM_PARAM_SAMPLE_BITS min:%u, max:%u",
+          pcm_params_get_min(alsa_hw_params, PCM_PARAM_SAMPLE_BITS),
+          pcm_params_get_max(alsa_hw_params, PCM_PARAM_SAMPLE_BITS));
+    ALOGV("usb:audio_hw - PCM_PARAM_FRAME_BITS min:%u, max:%u",
+          pcm_params_get_min(alsa_hw_params, PCM_PARAM_FRAME_BITS),
+          pcm_params_get_max(alsa_hw_params, PCM_PARAM_FRAME_BITS));
+    log_pcm_mask("PCM_PARAM_FORMAT",
+                 pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT));
+    log_pcm_mask("PCM_PARAM_SUBFORMAT",
+                 pcm_params_get_mask(alsa_hw_params, PCM_PARAM_SUBFORMAT));
+    ALOGV("usb:audio_hw - PCM_PARAM_CHANNELS min:%u, max:%u",
+          pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS),
+          pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS));
+    ALOGV("usb:audio_hw - PCM_PARAM_RATE min:%u, max:%u",
+          pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE),
+          pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE));
+    ALOGV("usb:audio_hw - PCM_PARAM_PERIOD_TIME min:%u, max:%u",
+          pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_TIME),
+          pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_TIME));
+    ALOGV("usb:audio_hw - PCM_PARAM_PERIOD_SIZE min:%u, max:%u",
+          pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_SIZE),
+          pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_SIZE));
+    ALOGV("usb:audio_hw - PCM_PARAM_PERIOD_BYTES min:%u, max:%u",
+          pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_BYTES),
+          pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_BYTES));
+    ALOGV("usb:audio_hw - PCM_PARAM_PERIODS min:%u, max:%u",
+          pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS),
+          pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIODS));
+    ALOGV("usb:audio_hw - PCM_PARAM_BUFFER_TIME min:%u, max:%u",
+          pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_TIME),
+          pcm_params_get_max(alsa_hw_params, PCM_PARAM_BUFFER_TIME));
+    ALOGV("usb:audio_hw - PCM_PARAM_BUFFER_SIZE min:%u, max:%u",
+          pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_SIZE),
+          pcm_params_get_max(alsa_hw_params, PCM_PARAM_BUFFER_SIZE));
+    ALOGV("usb:audio_hw - PCM_PARAM_BUFFER_BYTES min:%u, max:%u",
+          pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_BYTES),
+          pcm_params_get_max(alsa_hw_params, PCM_PARAM_BUFFER_BYTES));
+    ALOGV("usb:audio_hw - PCM_PARAM_TICK_TIME min:%u, max:%u",
+          pcm_params_get_min(alsa_hw_params, PCM_PARAM_TICK_TIME),
+          pcm_params_get_max(alsa_hw_params, PCM_PARAM_TICK_TIME));
+}
+
+void log_pcm_config(struct pcm_config * config, const char* label) {
+    ALOGV("log_pcm_config() - %s", label);
+    ALOGV("  channels:%d", config->channels);
+    ALOGV("  rate:%d", config->rate);
+    ALOGV("  period_size:%d", config->period_size);
+    ALOGV("  period_count:%d", config->period_count);
+    ALOGV("  format:%d", config->format);
+#if 0
+    /* Values to use for the ALSA start, stop and silence thresholds.  Setting
+     * any one of these values to 0 will cause the default tinyalsa values to be
+     * used instead.  Tinyalsa defaults are as follows.
+     *
+     * start_threshold   : period_count * period_size
+     * stop_threshold    : period_count * period_size
+     * silence_threshold : 0
+     */
+    unsigned int start_threshold;
+    unsigned int stop_threshold;
+    unsigned int silence_threshold;
+
+    /* Minimum number of frames available before pcm_mmap_write() will actually
+     * write into the kernel buffer. Only used if the stream is opened in mmap mode
+     * (pcm_open() called with PCM_MMAP flag set).   Use 0 for default.
+     */
+    int avail_min;
+#endif
+}
diff --git a/modules/usbaudio/logging.h b/modules/usbaudio/logging.h
new file mode 100644
index 0000000..b5640ed
--- /dev/null
+++ b/modules/usbaudio/logging.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_LOGGING_H
+#define ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_LOGGING_H
+
+#include <tinyalsa/asoundlib.h>
+
+void log_pcm_mask(const char* mask_name, struct pcm_mask* mask);
+void log_pcm_params(struct pcm_params * alsa_hw_params);
+void log_pcm_config(struct pcm_config * config, const char* label);
+
+#endif /* ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_LOGGING_H */