OSDN Git Service

Refactor USB HAL (audio_hw.c)
authorPaul McLean <pmclean@google.com>
Wed, 16 Jul 2014 16:48:34 +0000 (09:48 -0700)
committerPaul McLean <pmclean@google.com>
Fri, 25 Jul 2014 19:48:51 +0000 (12:48 -0700)
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: 159868271585670212833166

Change-Id: I82c8d0ef252b2f95ee23f263dc175f4c883bfd64

modules/usbaudio/Android.mk
modules/usbaudio/alsa_device_profile.c [new file with mode: 0644]
modules/usbaudio/alsa_device_profile.h [new file with mode: 0644]
modules/usbaudio/alsa_device_proxy.c [new file with mode: 0644]
modules/usbaudio/alsa_device_proxy.h [new file with mode: 0644]
modules/usbaudio/audio_hw.c
modules/usbaudio/format.c [new file with mode: 0644]
modules/usbaudio/format.h [new file with mode: 0644]
modules/usbaudio/logging.c [new file with mode: 0644]
modules/usbaudio/logging.h [new file with mode: 0644]

index 89c498b..ec8a8c0 100644 (file)
@@ -19,7 +19,11 @@ include $(CLEAR_VARS)
 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 (file)
index 0000000..2791e3e
--- /dev/null
@@ -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 (file)
index 0000000..b75e1dc
--- /dev/null
@@ -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 (file)
index 0000000..b887d1d
--- /dev/null
@@ -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 (file)
index 0000000..f090c56
--- /dev/null
@@ -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 */
index 05f0d2b..4a946e7 100644 (file)
@@ -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>
 
 #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 @@ struct audio_device {
     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 stream_out {
     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 @@ struct stream_out {
     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_profile * profile;
-
-    struct audio_config hal_pcm_config;
+    struct audio_device *dev;           /* hardware information - only using this for the lock */
 
-    /* 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;
+    alsa_device_profile * profile;
+    alsa_device_proxy proxy;            /* state of the stream */
 
-//    struct resampler_itfe *resampler;
-//    struct resampler_buffer_provider buf_provider;
+    // not used?
+    // struct audio_config hal_pcm_config;
 
-    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 @@ struct stream_in {
 };
 
 /*
- * 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 @@ static size_t convert_32_to_16(const int32_t * in_buff, size_t num_in_samples, s
     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)
-{
-    char buffer[256];
-    int buffer_size = sizeof(buffer) / sizeof(buffer[0]);
-    buffer[0] = '\0';
-
-    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++;
-        }
-    }
-
-    return strdup(buffer);
-}
-
-/*
- * 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)
+static char * device_get_parameters(alsa_device_profile * profile, const char * keys)
 {
-    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++;
-        }
-    }
-
-    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++;
-        }
-    }
-
-    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);
-    }
-
-    return works;
-}
-
-/* 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() keys:%s", keys);
 
-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);
-        }
+    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();
 
-/*
- * 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, ",");
-        }
+    /* 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);
     }
-    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;
+    /* 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);
     }
 
-    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;
+    /* 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);
 
-    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);
+    char* result_str = str_parms_to_str(result);
+    str_parms_destroy(result);
 
-    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;
-    }
+    ALOGV("usb:audio_hw::device_get_parameters = %s", result_str);
 
-    pcm_params_free(alsa_hw_params);
-    return ret;
+    return result_str;
 }
 
 /*
@@ -695,10 +217,14 @@ static int read_alsa_device_config(struct audio_device_profile * dev_profile,
  * 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 int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
 
 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 @@ static int out_standby(struct audio_stream *stream)
     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 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
     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 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
     }
 
     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 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
     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 char * out_get_parameters(const struct audio_stream *stream, const char *
 
 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 @@ static int out_set_volume(struct audio_stream_out *stream, float left, float rig
 /* 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 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, si
 
     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 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, si
         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 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, si
     /*
      * 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 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, si
     }
 
     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 @@ static int out_get_render_position(const struct audio_stream_out *stream, uint32
     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 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
     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 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
     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;
+
+    // build this to hand to the alsa_device_proxy
+    struct pcm_config proxy_config;
 
-    if (output_hardware_config_is_cached) {
-        config->sample_rate = cached_output_hardware_config.rate;
+    int ret = 0;
+
+    /* 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 {
+        proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(out->profile);
+        ret = -EINVAL;
+    }
 
-        config->format = audio_format_from_pcm_format(cached_output_hardware_config.format);
+    /* 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;
+        }
+    }
 
-        config->channel_mask =
-                audio_channel_out_mask_from_count(cached_output_hardware_config.channels);
+    /* 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) {
-            // 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. */
+            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);
         }
-    } 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_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,112 +595,94 @@ static void adev_close_output_stream(struct audio_hw_device *dev,
     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 int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
-{
-    return 0;
-}
-
-static char * adev_get_parameters(const struct audio_hw_device *dev, const char *keys)
-{
-    return strdup("");
-}
-
-static int adev_init_check(const struct audio_hw_device *dev)
-{
-    return 0;
-}
-
-static int adev_set_voice_volume(struct audio_hw_device *dev, float volume)
-{
-    return -ENOSYS;
-}
-
-static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
-{
-    return -ENOSYS;
-}
-
-static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode)
-{
-    return 0;
-}
-
-static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
-{
-    return -ENOSYS;
-}
-
-static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
-{
-    return -ENOSYS;
-}
-
 static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
                                          const struct audio_config *config)
 {
-    return 0;
+    /* TODO This needs to be calculated based on format/channels/rate */
+    return 320;
 }
 
-/* Helper functions */
+/*
+ * IN functions
+ */
 static uint32_t in_get_sample_rate(const struct audio_stream *stream)
 {
-    return cached_input_hardware_config.rate;
+    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)
 {
-    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);
+    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)
 {
-    // just report stereo for now
+    /* 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)
 {
-    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);
+    /* 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;
+    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;
+        proxy_close(&in->proxy);
         in->standby = true;
     }
 
@@ -1211,19 +702,20 @@ 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);
+    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
+    /* Card/Device */
     param_val = str_parms_get_str(parms, "card", value, sizeof(value));
     if (param_val >= 0) {
         in->profile->card = atoi(value);
@@ -1237,25 +729,25 @@ static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
     }
 
     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);
-    }
+        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) {
-    ALOGV("usb:audio_hw::in in_get_parameters() keys:%s", keys);
-
+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);
+    char * params_str  device_get_parameters(in->profile, keys);
 
     pthread_mutex_unlock(&in->lock);
     pthread_mutex_unlock(&in->dev->lock);
@@ -1279,26 +771,12 @@ static int in_set_gain(struct audio_stream_in *stream, float gain)
 }
 
 /* must be called with hw device and output stream mutexes locked */
-static int start_input_stream(struct stream_in *in) {
-    int return_val = 0;
-
+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);
 
-    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;
+    return proxy_open(&in->proxy);
 }
 
 /* TODO mutex stuff here (see out_write) */
@@ -1308,10 +786,11 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t byte
     void * read_buff = buffer;
     void * out_buff = buffer;
 
-    struct stream_in * in = (struct stream_in *) stream;
+    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) {
@@ -1320,26 +799,30 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t byte
         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).
+    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 = cached_input_hardware_config.channels;
+    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;
     }
 
-    /* Assume (for now) that in->input_framework_format == PCM_FORMAT_S16_LE */
-    if (cached_input_hardware_config.format == PCM_FORMAT_S24_3LE) {
+    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 (cached_input_hardware_config.format == PCM_FORMAT_S32_LE) {
+    } 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).
+    /* 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
@@ -1350,30 +833,31 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t byte
         read_buff = in->conversion_buffer;
     }
 
-    if (pcm_read(in->pcm, read_buff, num_read_buff_bytes) == 0) {
+    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 (cached_input_hardware_config.format != PCM_FORMAT_S16_LE) {
-            // we need to convert
+        if (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) {
+            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 (cached_input_hardware_config.format == PCM_FORMAT_S32_LE) {
+            } 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 {
+            } 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) {
@@ -1390,7 +874,6 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t byte
 
 err:
     pthread_mutex_unlock(&in->lock);
-    pthread_mutex_unlock(&in->dev->lock);
 
     return num_read_buff_bytes;
 }
@@ -1416,7 +899,7 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
     if (in == NULL)
         return -ENOMEM;
 
-    // setup function pointers
+    /* 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;
@@ -1434,54 +917,55 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
     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;
+    in->profile = &in->dev->in_profile;
 
-    if (!input_hardware_config_is_cached) {
-        // just return defaults until we can actually query the device.
-        cached_input_hardware_config = default_alsa_in_config;
-    }
+    struct pcm_config proxy_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;
+        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 {
-        cached_input_hardware_config.rate = config->sample_rate;
+        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 */
-    /* 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;
+        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;
     }
 
-    /* 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;
     }
+    // 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;
 
@@ -1497,7 +981,7 @@ static void adev_close_input_stream(struct audio_hw_device *dev, struct audio_st
 {
     struct stream_in *in = (struct stream_in *)stream;
 
-    // Close the pcm device
+    /* Close the pcm device */
     in_standby(&stream->common);
 
     free(in->conversion_buffer);
@@ -1505,6 +989,49 @@ static void adev_close_input_stream(struct audio_hw_device *dev, struct audio_st
     free(stream);
 }
 
+/*
+ * ADEV Functions
+ */
+static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
+{
+    return 0;
+}
+
+static char * adev_get_parameters(const struct audio_hw_device *dev, const char *keys)
+{
+    return strdup("");
+}
+
+static int adev_init_check(const struct audio_hw_device *dev)
+{
+    return 0;
+}
+
+static int adev_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+    return -ENOSYS;
+}
+
+static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+    return -ENOSYS;
+}
+
+static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode)
+{
+    return 0;
+}
+
+static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+    return -ENOSYS;
+}
+
+static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+    return -ENOSYS;
+}
+
 static int adev_dump(const audio_hw_device_t *device, int fd)
 {
     return 0;
@@ -1515,9 +1042,6 @@ static int adev_close(hw_device_t *device)
     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 @@ static int adev_open(const hw_module_t* module, const char* name, hw_device_t**
     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 (file)
index 0000000..6aac1d3
--- /dev/null
@@ -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 (file)
index 0000000..e23935e
--- /dev/null
@@ -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 (file)
index 0000000..0a05511
--- /dev/null
@@ -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 (file)
index 0000000..b5640ed
--- /dev/null
@@ -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 */