summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorPaul McLean <pmclean@google.com>2014-07-16 09:48:34 -0700
committerPaul McLean <pmclean@google.com>2014-07-25 12:48:51 -0700
commitc88e6ae584a5f7ea65ab6df89e381c2c7bf8e84e (patch)
treec4f1b2e19543ff3ff3df89b113f843ad8292f2c3 /modules
parent962e9adc2f281b0669fbec2e7f8b011beb726d8c (diff)
downloadhardware_libhardware-c88e6ae584a5f7ea65ab6df89e381c2c7bf8e84e.zip
hardware_libhardware-c88e6ae584a5f7ea65ab6df89e381c2c7bf8e84e.tar.gz
hardware_libhardware-c88e6ae584a5f7ea65ab6df89e381c2c7bf8e84e.tar.bz2
Refactor USB HAL (audio_hw.c)
Refactoring alsa device attributes to alsa_device_profile.h/.c Refactoring alsa device state to alsa_device_proxy.h/.c Refactoring format functions into format.h/.c Refactoring logging functions into logging.h/.c Sundry (and extensive) code cleanup Reworked locking on out_write() and in_read() to allow simultaneous input/output Bug: 15986827, 15856702, 12833166 Change-Id: I82c8d0ef252b2f95ee23f263dc175f4c883bfd64
Diffstat (limited to 'modules')
-rw-r--r--modules/usbaudio/Android.mk6
-rw-r--r--modules/usbaudio/alsa_device_profile.c481
-rw-r--r--modules/usbaudio/alsa_device_profile.h88
-rw-r--r--modules/usbaudio/alsa_device_proxy.c157
-rw-r--r--modules/usbaudio/alsa_device_proxy.h49
-rw-r--r--modules/usbaudio/audio_hw.c1085
-rw-r--r--modules/usbaudio/format.c194
-rw-r--r--modules/usbaudio/format.h27
-rw-r--r--modules/usbaudio/logging.c129
-rw-r--r--modules/usbaudio/logging.h26
10 files changed, 1462 insertions, 780 deletions
diff --git a/modules/usbaudio/Android.mk b/modules/usbaudio/Android.mk
index 89c498b..ec8a8c0 100644
--- a/modules/usbaudio/Android.mk
+++ b/modules/usbaudio/Android.mk
@@ -19,7 +19,11 @@ 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
index 0000000..2791e3e
--- /dev/null
+++ b/modules/usbaudio/alsa_device_profile.c
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "alsa_device_profile"
+/*#define LOG_NDEBUG 0*/
+/*#define LOG_PCM_PARAMS 0*/
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <log/log.h>
+
+#include "alsa_device_profile.h"
+#include "format.h"
+#include "logging.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+/*#define ENABLE_MULTI_CHANNEL 1*/
+
+/*TODO - Evaluate if this value should/can be retrieved from a device-specific property */
+#define BUFF_DURATION_MS 5
+
+#define DEFAULT_PERIOD_SIZE 1024
+
+static const char * const format_string_map[] = {
+ "AUDIO_FORMAT_PCM_16_BIT", /* "PCM_FORMAT_S16_LE", */
+ "AUDIO_FORMAT_PCM_32_BIT", /* "PCM_FORMAT_S32_LE", */
+ "AUDIO_FORMAT_PCM_8_BIT", /* "PCM_FORMAT_S8", */
+ "AUDIO_FORMAT_PCM_8_24_BIT", /* "PCM_FORMAT_S24_LE", */
+ "AUDIO_FORMAT_PCM_24_BIT_PACKED"/* "PCM_FORMAT_S24_3LE" */
+};
+
+static const unsigned const format_byte_size_map[] = {
+ 2, /* PCM_FORMAT_S16_LE */
+ 4, /* PCM_FORMAT_S32_LE */
+ 1, /* PCM_FORMAT_S8 */
+ 4, /* PCM_FORMAT_S24_LE */
+ 3, /* PCM_FORMAT_S24_3LE */
+};
+
+extern int8_t const pcm_format_value_map[50];
+
+/* sort these highest -> lowest (to default to best quality) */
+static const unsigned std_sample_rates[] =
+ {48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000};
+
+void profile_init(alsa_device_profile* profile, int direction)
+{
+ profile->card = profile->device = -1;
+ profile->direction = direction;
+
+ /* Fill the attribute arrays with invalid values */
+ size_t index;
+ for (index = 0; index < ARRAY_SIZE(profile->formats); index++) {
+ profile->formats[index] = PCM_FORMAT_INVALID;
+ }
+
+ for (index = 0; index < ARRAY_SIZE(profile->sample_rates); index++) {
+ profile->sample_rates[index] = 0;
+ }
+
+ for (index = 0; index < ARRAY_SIZE(profile->channel_counts); index++) {
+ profile->channel_counts[index] = 0;
+ }
+
+ profile->min_period_size = profile->max_period_size = 0;
+ profile->min_channel_count = profile->max_channel_count = DEFAULT_CHANNEL_COUNT;
+
+ profile->is_valid = false;
+}
+
+bool profile_is_initialized(alsa_device_profile* profile)
+{
+ return profile->card >= 0 && profile->device >= 0;
+}
+
+bool profile_is_valid(alsa_device_profile* profile) {
+ return profile->is_valid;
+}
+
+/*
+ * Returns the supplied value rounded up to the next even multiple of 16
+ */
+static unsigned int round_to_16_mult(unsigned int size)
+{
+ return (size + 15) & ~15; // 0xFFFFFFF0;
+}
+
+/*
+ * Returns the system defined minimum period size based on the supplied sample rate.
+ */
+unsigned profile_calc_min_period_size(alsa_device_profile* profile, unsigned sample_rate)
+{
+ ALOGV("profile_calc_min_period_size(%p, rate:%d)", profile, sample_rate);
+ if (profile == NULL) {
+ return DEFAULT_PERIOD_SIZE;
+ } else {
+ unsigned num_sample_frames = (sample_rate * BUFF_DURATION_MS) / 1000;
+ if (num_sample_frames < profile->min_period_size) {
+ num_sample_frames = profile->min_period_size;
+ }
+ return round_to_16_mult(num_sample_frames) * 2;
+ }
+}
+
+unsigned int profile_get_period_size(alsa_device_profile* profile, unsigned sample_rate)
+{
+ // return profile->default_config.period_size;
+ unsigned int period_size = profile_calc_min_period_size(profile, sample_rate);
+ ALOGV("profile_get_period_size(rate:%d) = %d", sample_rate, period_size);
+ return period_size;
+}
+
+/*
+ * Sample Rate
+ */
+unsigned profile_get_default_sample_rate(alsa_device_profile* profile)
+{
+ /*
+ * TODO this won't be right in general. we should store a preferred rate as we are scanning.
+ * But right now it will return the highest rate, which may be correct.
+ */
+ return profile_is_valid(profile) ? profile->sample_rates[0] : DEFAULT_SAMPLE_RATE;
+}
+
+bool profile_is_sample_rate_valid(alsa_device_profile* profile, unsigned rate)
+{
+ if (profile_is_valid(profile)) {
+ size_t index;
+ for (index = 0; profile->sample_rates[index] != 0; index++) {
+ if (profile->sample_rates[index] == rate) {
+ return true;
+ }
+ }
+
+ return false;
+ } else {
+ return rate == DEFAULT_SAMPLE_RATE;
+ }
+}
+
+/*
+ * Format
+ */
+enum pcm_format profile_get_default_format(alsa_device_profile* profile)
+{
+ /*
+ * TODO this won't be right in general. we should store a preferred format as we are scanning.
+ */
+ return profile_is_valid(profile) ? profile->formats[0] : DEFAULT_SAMPLE_FORMAT;
+}
+
+bool profile_is_format_valid(alsa_device_profile* profile, enum pcm_format fmt) {
+ if (profile_is_valid(profile)) {
+ size_t index;
+ for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) {
+ if (profile->formats[index] == fmt) {
+ return true;
+ }
+ }
+
+ return false;
+ } else {
+ return fmt == DEFAULT_SAMPLE_FORMAT;
+ }
+}
+
+/*
+ * Channels
+ */
+unsigned profile_get_default_channel_count(alsa_device_profile* profile)
+{
+ return profile_is_valid(profile) ? profile->channel_counts[0] : DEFAULT_CHANNEL_COUNT;
+}
+
+bool profile_is_channel_count_valid(alsa_device_profile* profile, unsigned count)
+{
+ if (profile_is_initialized(profile)) {
+ return count >= profile->min_channel_count && count <= profile->max_channel_count;
+ } else {
+ return count == DEFAULT_CHANNEL_COUNT;
+ }
+}
+
+static bool profile_test_sample_rate(alsa_device_profile* profile, unsigned rate)
+{
+ struct pcm_config config = profile->default_config;
+ config.rate = rate;
+
+ bool works = false; /* let's be pessimistic */
+ struct pcm * pcm = pcm_open(profile->card, profile->device,
+ profile->direction, &config);
+
+ if (pcm != NULL) {
+ works = pcm_is_ready(pcm);
+ pcm_close(pcm);
+ }
+
+ return works;
+}
+
+static unsigned profile_enum_sample_rates(alsa_device_profile* profile, unsigned min, unsigned max)
+{
+ unsigned num_entries = 0;
+ unsigned index;
+
+ for (index = 0; index < ARRAY_SIZE(std_sample_rates) &&
+ num_entries < ARRAY_SIZE(profile->sample_rates) - 1;
+ index++) {
+ if (std_sample_rates[index] >= min && std_sample_rates[index] <= max
+ && profile_test_sample_rate(profile, std_sample_rates[index])) {
+ profile->sample_rates[num_entries++] = std_sample_rates[index];
+ }
+ }
+
+ return num_entries; /* return # of supported rates */
+}
+
+static unsigned profile_enum_sample_formats(alsa_device_profile* profile, struct pcm_mask * mask)
+{
+ const int num_slots = ARRAY_SIZE(mask->bits);
+ const int bits_per_slot = sizeof(mask->bits[0]) * 8;
+
+ const int table_size = ARRAY_SIZE(pcm_format_value_map);
+
+ int slot_index, bit_index, table_index;
+ table_index = 0;
+ int num_written = 0;
+ for (slot_index = 0; slot_index < num_slots && table_index < table_size;
+ slot_index++) {
+ unsigned bit_mask = 1;
+ for (bit_index = 0;
+ bit_index < bits_per_slot && table_index < table_size;
+ bit_index++) {
+ if ((mask->bits[slot_index] & bit_mask) != 0) {
+ enum pcm_format format = pcm_format_value_map[table_index];
+ /* Never return invalid (unrecognized) or 8-bit */
+ if (format != PCM_FORMAT_INVALID && format != PCM_FORMAT_S8) {
+ profile->formats[num_written++] = format;
+ if (num_written == ARRAY_SIZE(profile->formats) - 1) {
+ /* leave at least one PCM_FORMAT_INVALID at the end */
+ return num_written;
+ }
+ }
+ }
+ bit_mask <<= 1;
+ table_index++;
+ }
+ }
+
+ return num_written;
+}
+
+static unsigned profile_enum_channel_counts(alsa_device_profile* profile, unsigned min, unsigned max)
+{
+ static const unsigned std_channel_counts[] = {8, 4, 2, 1};
+
+ unsigned num_counts = 0;
+ unsigned index;
+ /* TODO write a profile_test_channel_count() */
+ /* Ensure there is at least one invalid channel count to terminate the channel counts array */
+ for (index = 0; index < ARRAY_SIZE(std_channel_counts) &&
+ num_counts < ARRAY_SIZE(profile->channel_counts) - 1;
+ index++) {
+ /* TODO Do we want a channel counts test? */
+ if (std_channel_counts[index] >= min && std_channel_counts[index] <= max /* &&
+ profile_test_channel_count(profile, std_channel_counts[index])*/) {
+ profile->channel_counts[num_counts++] = std_channel_counts[index];
+ }
+ }
+
+ return num_counts; /* return # of supported counts */
+}
+
+/*
+ * Reads and decodes configuration info from the specified ALSA card/device.
+ */
+static int read_alsa_device_config(alsa_device_profile * profile, struct pcm_config * config)
+{
+ ALOGV("usb:audio_hw - read_alsa_device_config(c:%d d:%d t:0x%X)",
+ profile->card, profile->device, profile->direction);
+
+ if (profile->card < 0 || profile->device < 0) {
+ return -EINVAL;
+ }
+
+ struct pcm_params * alsa_hw_params =
+ pcm_params_get(profile->card, profile->device, profile->direction);
+ if (alsa_hw_params == NULL) {
+ return -EINVAL;
+ }
+
+ profile->min_period_size = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_SIZE);
+ profile->max_period_size = pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_SIZE);
+
+ profile->min_channel_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
+ profile->max_channel_count = pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS);
+
+ int ret = 0;
+
+ /*
+ * This Logging will be useful when testing new USB devices.
+ */
+#ifdef LOG_PCM_PARAMS
+ log_pcm_params(alsa_hw_params);
+#endif
+
+ config->channels = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS);
+ config->rate = pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE);
+ config->period_size = profile_calc_min_period_size(profile, config->rate);
+ config->period_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS);
+ config->format = get_pcm_format_for_mask(pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT));
+#ifdef LOG_PCM_PARAMS
+ log_pcm_config(config, "read_alsa_device_config");
+#endif
+ if (config->format == PCM_FORMAT_INVALID) {
+ ret = -EINVAL;
+ }
+
+ pcm_params_free(alsa_hw_params);
+
+ return ret;
+}
+
+bool profile_read_device_info(alsa_device_profile* profile)
+{
+ if (!profile_is_initialized(profile)) {
+ return false;
+ }
+
+ /* let's get some defaults */
+ read_alsa_device_config(profile, &profile->default_config);
+ ALOGV("default_config chans:%d rate:%d format:%d count:%d size:%d",
+ profile->default_config.channels, profile->default_config.rate,
+ profile->default_config.format, profile->default_config.period_count,
+ profile->default_config.period_size);
+
+ struct pcm_params * alsa_hw_params = pcm_params_get(profile->card,
+ profile->device,
+ profile->direction);
+ if (alsa_hw_params == NULL) {
+ return false;
+ }
+
+ /* Formats */
+ struct pcm_mask * format_mask = pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT);
+ profile_enum_sample_formats(profile, format_mask);
+
+ /* Channels */
+ profile_enum_channel_counts(
+ profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS));
+
+ /* Sample Rates */
+ profile_enum_sample_rates(
+ profile, pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE));
+
+ profile->is_valid = true;
+
+ return true;
+}
+
+char * profile_get_sample_rate_strs(alsa_device_profile* profile)
+{
+ char buffer[128];
+ buffer[0] = '\0';
+ int buffSize = ARRAY_SIZE(buffer);
+
+ char numBuffer[32];
+
+ int numEntries = 0;
+ unsigned index;
+ for (index = 0; profile->sample_rates[index] != 0; index++) {
+ if (numEntries++ != 0) {
+ strncat(buffer, "|", buffSize);
+ }
+ snprintf(numBuffer, sizeof(numBuffer), "%u", profile->sample_rates[index]);
+ strncat(buffer, numBuffer, buffSize);
+ }
+
+ return strdup(buffer);
+}
+
+char * profile_get_format_strs(alsa_device_profile* profile)
+{
+ /* TODO remove this hack when we have support for input in non PCM16 formats */
+ if (profile->direction == PCM_IN) {
+ return strdup("AUDIO_FORMAT_PCM_16_BIT");
+ }
+
+ char buffer[128];
+ buffer[0] = '\0';
+ int buffSize = ARRAY_SIZE(buffer);
+
+ int numEntries = 0;
+ unsigned index = 0;
+ for (index = 0; profile->formats[index] != PCM_FORMAT_INVALID; index++) {
+ if (numEntries++ != 0) {
+ strncat(buffer, "|", buffSize);
+ }
+ strncat(buffer, format_string_map[profile->formats[index]], buffSize);
+ }
+
+ return strdup(buffer);
+}
+
+char * profile_get_channel_count_strs(alsa_device_profile* profile)
+{
+#ifndef ENABLE_MULTI_CHANNEL
+ /* TODO remove this hack when it is superceeded by proper multi-channel support */
+ return strdup(
+ profile->direction == PCM_OUT ?
+ "AUDIO_CHANNEL_OUT_STEREO" : "AUDIO_CHANNEL_IN_STEREO");
+#else
+ /*
+ * Maps from a channel-count (the table index), to a corresponding AUDIO_CHANNEL_OUT_ string.
+ * (see audio_channel_out_mask_from_count() in audio.h.
+ */
+ static const char * const out_chans_strs[] = {
+ /* 0 */"AUDIO_CHANNEL_NONE",
+ /* 1 */"AUDIO_CHANNEL_OUT_MONO",
+ /* 2 */"AUDIO_CHANNEL_OUT_STEREO",
+ /* 3 */"AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_FRONT_CENTER",
+ /* 4 */"AUDIO_CHANNEL_OUT_QUAD",
+ /* 5 */"AUDIO_CHANNEL_OUT_QUAD|AUDIO_CHANNEL_OUT_FRONT_CENTER",
+ /* 6 */"AUDIO_CHANNEL_OUT_5POINT1",
+ /* 7 */"AUDIO_CHANNEL_OUT_5POINT1|AUDIO_CHANNEL_OUT_BACK_CENTER",
+ /* 8 */"AUDIO_CHANNEL_OUT_7POINT1"
+ };
+
+ /*
+ * Maps from a channel-count (the table index), to a corresponding AUDIO_CHANNEL_IN_ string.
+ * (see audio_channel_in_mask_from_count() in audio.h.
+ */
+ static const char * const in_chans_strs[] =
+ {
+ /* 0 */"AUDIO_CHANNEL_NONE",
+ /* 1 */"AUDIO_CHANNEL_IN_MONO",
+ /* 2 */"AUDIO_CHANNEL_IN_STEREO",
+ /* 3 */"",
+ /* 4 */
+ "AUDIO_CHANNEL_IN_LEFT|AUDIO_CHANNEL_IN_RIGHT|AUDIO_CHANNEL_IN_FRONT|AUDIO_CHANNEL_IN_BACK"
+ };
+
+ char buffer[256];
+ buffer[0] = '\0';
+ int buffSize = ARRAY_SIZE(buffer);
+
+ int numEntries = 0;
+ unsigned index = 0;
+ for (index = 0; profile->channel_counts[index] != 0; index++) {
+ if (numEntries++ != 0) {
+ strncat(buffer, "|", buffSize);
+ }
+ if (profile->direction == PCM_OUT) {
+ strncat(buffer, out_chans_strs[profile->channel_counts[index]], buffSize);
+ } else {
+ strncat(buffer, in_chans_strs[profile->channel_counts[index]], buffSize);
+ }
+ }
+
+ return strdup(buffer);
+#endif
+}
diff --git a/modules/usbaudio/alsa_device_profile.h b/modules/usbaudio/alsa_device_profile.h
new file mode 100644
index 0000000..b75e1dc
--- /dev/null
+++ b/modules/usbaudio/alsa_device_profile.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROFILE_H
+#define ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROFILE_H
+
+#include <stdbool.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#define MAX_PROFILE_FORMATS 6 /* We long support the 5 standard formats defined
+ * in asound.h, so we just need this to be 1 more
+ * than that */
+#define MAX_PROFILE_SAMPLE_RATES 10 /* this number needs to be 1 more than the number of
+ * standard formats in std_sample_rates[]
+ * (in alsa_device_profile.c) */
+#define MAX_PROFILE_CHANNEL_COUNTS 5 /* this number need to be 1 more than the number of
+ * standard channel formats in std_channel_counts[]
+ * (in alsa_device_profile.c) */
+
+#define DEFAULT_SAMPLE_RATE 44100
+#define DEFAULT_SAMPLE_FORMAT PCM_FORMAT_S16_LE
+#define DEFAULT_CHANNEL_COUNT 2
+
+typedef struct {
+ int card;
+ int device;
+ int direction; /* PCM_OUT or PCM_IN */
+
+ enum pcm_format formats[MAX_PROFILE_FORMATS];
+
+ unsigned sample_rates[MAX_PROFILE_SAMPLE_RATES];
+
+ unsigned channel_counts[MAX_PROFILE_CHANNEL_COUNTS];
+
+ bool is_valid;
+
+ /* read from the hardware device */
+ struct pcm_config default_config;
+
+ unsigned min_period_size;
+ unsigned max_period_size;
+
+ unsigned min_channel_count;
+ unsigned max_channel_count;
+} alsa_device_profile;
+
+void profile_init(alsa_device_profile* profile, int direction);
+bool profile_is_initialized(alsa_device_profile* profile);
+bool profile_is_valid(alsa_device_profile* profile);
+
+bool profile_read_device_info(alsa_device_profile* profile);
+
+/* Audio Config Strings Methods */
+char * profile_get_sample_rate_strs(alsa_device_profile* profile);
+char * profile_get_format_strs(alsa_device_profile* profile);
+char * profile_get_channel_count_strs(alsa_device_profile* profile);
+
+/* Sample Rate Methods */
+unsigned profile_get_default_sample_rate(alsa_device_profile* profile);
+bool profile_is_sample_rate_valid(alsa_device_profile* profile, unsigned rate);
+
+/* Format Methods */
+enum pcm_format profile_get_default_format(alsa_device_profile* profile);
+bool profile_is_format_valid(alsa_device_profile* profile, enum pcm_format fmt);
+
+/* Channel Methods */
+unsigned profile_get_default_channel_count(alsa_device_profile* profile);
+bool profile_is_channel_count_valid(alsa_device_profile* profile, unsigned count);
+
+/* Utility */
+unsigned profile_calc_min_period_size(alsa_device_profile* profile, unsigned sample_rate);
+unsigned int profile_get_period_size(alsa_device_profile* profile, unsigned sample_rate);
+
+#endif /* ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROFILE_H */
diff --git a/modules/usbaudio/alsa_device_proxy.c b/modules/usbaudio/alsa_device_proxy.c
new file mode 100644
index 0000000..b887d1d
--- /dev/null
+++ b/modules/usbaudio/alsa_device_proxy.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "alsa_device_proxy"
+/*#define LOG_NDEBUG 0*/
+/*#define LOG_PCM_PARAMS 0*/
+
+#include <log/log.h>
+
+#include "alsa_device_proxy.h"
+
+#include "logging.h"
+
+#define DEFAULT_PERIOD_SIZE 1024
+#define DEFAULT_PERIOD_COUNT 2
+
+//void proxy_init(alsa_device_proxy * proxy)
+//{
+// proxy->profile = NULL;
+//
+// proxy->alsa_config.format = DEFAULT_SAMPLE_FORMAT;
+// proxy->alsa_config.rate = DEFAULT_SAMPLE_RATE;
+// proxy->alsa_config.channels = DEFAULT_CHANNEL_COUNT;
+//
+// proxy->alsa_config.period_size = DEFAULT_PERIOD_SIZE;
+// proxy->alsa_config.period_count = DEFAULT_PERIOD_COUNT;
+//
+// proxy->pcm = NULL;
+//}
+
+void proxy_prepare(alsa_device_proxy * proxy, alsa_device_profile* profile,
+ struct pcm_config * config)
+{
+ ALOGV("proxy_prepare()");
+
+ proxy->profile = profile;
+
+#if LOG_PCM_PARAMS
+ log_pcm_config(config, "proxy_setup()");
+#endif
+
+ proxy->alsa_config.format =
+ config->format != PCM_FORMAT_INVALID && profile_is_format_valid(profile, config->format)
+ ? config->format : profile->default_config.format;
+ proxy->alsa_config.rate =
+ config->rate != 0 && profile_is_sample_rate_valid(profile, config->rate)
+ ? config->rate : profile->default_config.rate;
+ proxy->alsa_config.channels =
+ config->channels != 0 && profile_is_channel_count_valid(profile, config->channels)
+ ? config->channels : profile->default_config.channels;
+
+ proxy->alsa_config.period_count = profile->default_config.period_count;
+ proxy->alsa_config.period_size =
+ profile_get_period_size(proxy->profile, proxy->alsa_config.rate);
+
+ proxy->pcm = NULL;
+}
+
+int proxy_open(alsa_device_proxy * proxy)
+{
+ alsa_device_profile* profile = proxy->profile;
+
+ ALOGV("proxy_open(card:%d device:%d %s)", profile->card, profile->device,
+ profile->direction == PCM_OUT ? "PCM_OUT" : "PCM_IN");
+
+ proxy->pcm = pcm_open(profile->card, profile->device, profile->direction, &proxy->alsa_config);
+ if (proxy->pcm == NULL) {
+ return -ENOMEM;
+ }
+
+ if (!pcm_is_ready(proxy->pcm)) {
+ ALOGE("[%s] proxy_open() pcm_open() failed: %s", LOG_TAG, pcm_get_error(proxy->pcm));
+#ifdef LOG_PCM_PARAMS
+ log_pcm_config(&proxy->alsa_config, "config");
+#endif
+ pcm_close(proxy->pcm);
+ proxy->pcm = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void proxy_close(alsa_device_proxy * proxy)
+{
+ pcm_close(proxy->pcm);
+ proxy->pcm = NULL;
+}
+
+/*
+ * Sample Rate
+ */
+unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy)
+{
+ return proxy->alsa_config.rate;
+}
+
+/*
+ * Format
+ */
+enum pcm_format proxy_get_format(const alsa_device_proxy * proxy)
+{
+ return proxy->alsa_config.format;
+}
+
+/*
+ * Channel Count
+ */
+unsigned proxy_get_channel_count(const alsa_device_proxy * proxy)
+{
+ return proxy->alsa_config.channels;
+}
+
+/*
+ * Other
+ */
+unsigned int proxy_get_period_size(const alsa_device_proxy * proxy)
+{
+ return proxy->alsa_config.period_size;
+}
+
+unsigned int proxy_get_period_count(const alsa_device_proxy * proxy)
+{
+ return proxy->alsa_config.period_count;
+}
+
+unsigned proxy_get_latency(const alsa_device_proxy * proxy)
+{
+ return (proxy_get_period_size(proxy) * proxy_get_period_count(proxy) * 1000)
+ / proxy_get_sample_rate(proxy);
+}
+
+/*
+ * I/O
+ */
+int proxy_write(const alsa_device_proxy * proxy, const void *data, unsigned int count)
+{
+ return pcm_write(proxy->pcm, data, count);
+}
+
+int proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count)
+{
+ return pcm_read(proxy->pcm, data, count);
+}
diff --git a/modules/usbaudio/alsa_device_proxy.h b/modules/usbaudio/alsa_device_proxy.h
new file mode 100644
index 0000000..f090c56
--- /dev/null
+++ b/modules/usbaudio/alsa_device_proxy.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROXY_H
+#define ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROXY_H
+
+#include <tinyalsa/asoundlib.h>
+
+#include "alsa_device_profile.h"
+
+typedef struct {
+ alsa_device_profile* profile;
+
+ struct pcm_config alsa_config;
+
+ struct pcm * pcm;
+} alsa_device_proxy;
+
+void proxy_prepare(alsa_device_proxy * proxy, alsa_device_profile * profile,
+ struct pcm_config * config);
+
+unsigned proxy_get_sample_rate(const alsa_device_proxy * proxy);
+enum pcm_format proxy_get_format(const alsa_device_proxy * proxy);
+unsigned proxy_get_channel_count(const alsa_device_proxy * proxy);
+
+unsigned int proxy_get_period_size(const alsa_device_proxy * proxy);
+
+unsigned proxy_get_latency(const alsa_device_proxy * proxy);
+
+int proxy_open(alsa_device_proxy * proxy);
+void proxy_close(alsa_device_proxy * proxy);
+
+int proxy_write(const alsa_device_proxy * proxy, const void *data, unsigned int count);
+int proxy_read(const alsa_device_proxy * proxy, void *data, unsigned int count);
+
+#endif /* ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_ALSA_DEVICE_PROXY_H */
diff --git a/modules/usbaudio/audio_hw.c b/modules/usbaudio/audio_hw.c
index 05f0d2b..4a946e7 100644
--- a/modules/usbaudio/audio_hw.c
+++ b/modules/usbaudio/audio_hw.c
@@ -16,7 +16,6 @@
#define LOG_TAG "usb_audio_hw"
/*#define LOG_NDEBUG 0*/
-/*#define LOG_PCM_PARAMS 0*/
#include <errno.h>
#include <inttypes.h>
@@ -39,46 +38,11 @@
#include <audio_utils/channels.h>
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#include "alsa_device_profile.h"
+#include "alsa_device_proxy.h"
+#include "logging.h"
-/* This is the default configuration to hand to The Framework on the initial
- * adev_open_output_stream(). Actual device attributes will be used on the subsequent
- * adev_open_output_stream() after the card and device number have been set in out_set_parameters()
- */
-#define OUT_PERIOD_SIZE 1024
-#define OUT_PERIOD_COUNT 4
-#define OUT_SAMPLING_RATE 44100
-
-struct pcm_config default_alsa_out_config = {
- .channels = 2,
- .rate = OUT_SAMPLING_RATE,
- .period_size = OUT_PERIOD_SIZE,
- .period_count = OUT_PERIOD_COUNT,
- .format = PCM_FORMAT_S16_LE,
-};
-
-/*
- * Input defaults. See comment above.
- */
-#define IN_PERIOD_SIZE 1024
-#define IN_PERIOD_COUNT 4
-#define IN_SAMPLING_RATE 44100
-
-struct pcm_config default_alsa_in_config = {
- .channels = 2,
- .rate = IN_SAMPLING_RATE,
- .period_size = IN_PERIOD_SIZE,
- .period_count = IN_PERIOD_COUNT,
- .format = PCM_FORMAT_S16_LE,
- .start_threshold = 1,
- .stop_threshold = (IN_PERIOD_SIZE * IN_PERIOD_COUNT),
-};
-
-struct audio_device_profile {
- int card;
- int device;
- int direction; /* PCM_OUT or PCM_IN */
-};
+#define DEFAULT_INPUT_BUFFER_SIZE_MS 20
struct audio_device {
struct audio_hw_device hw_device;
@@ -86,10 +50,10 @@ 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
index 0000000..6aac1d3
--- /dev/null
+++ b/modules/usbaudio/format.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "usb_profile"
+/*#define LOG_NDEBUG 0*/
+
+#include "format.h"
+
+#include <tinyalsa/asoundlib.h>
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+/*
+ * Maps from bit position in pcm_mask to AUDIO_ format constants.
+ */
+static audio_format_t const format_value_map[] = {
+ AUDIO_FORMAT_PCM_8_BIT, /* 00 - SNDRV_PCM_FORMAT_S8 */
+ AUDIO_FORMAT_PCM_8_BIT, /* 01 - SNDRV_PCM_FORMAT_U8 */
+ AUDIO_FORMAT_PCM_16_BIT, /* 02 - SNDRV_PCM_FORMAT_S16_LE */
+ AUDIO_FORMAT_INVALID, /* 03 - SNDRV_PCM_FORMAT_S16_BE */
+ AUDIO_FORMAT_INVALID, /* 04 - SNDRV_PCM_FORMAT_U16_LE */
+ AUDIO_FORMAT_INVALID, /* 05 - SNDRV_PCM_FORMAT_U16_BE */
+ AUDIO_FORMAT_INVALID, /* 06 - SNDRV_PCM_FORMAT_S24_LE */
+ AUDIO_FORMAT_INVALID, /* 07 - SNDRV_PCM_FORMAT_S24_BE */
+ AUDIO_FORMAT_INVALID, /* 08 - SNDRV_PCM_FORMAT_U24_LE */
+ AUDIO_FORMAT_INVALID, /* 09 - SNDRV_PCM_FORMAT_U24_BE */
+ AUDIO_FORMAT_PCM_32_BIT, /* 10 - SNDRV_PCM_FORMAT_S32_LE */
+ AUDIO_FORMAT_INVALID, /* 11 - SNDRV_PCM_FORMAT_S32_BE */
+ AUDIO_FORMAT_INVALID, /* 12 - SNDRV_PCM_FORMAT_U32_LE */
+ AUDIO_FORMAT_INVALID, /* 13 - SNDRV_PCM_FORMAT_U32_BE */
+ AUDIO_FORMAT_PCM_FLOAT, /* 14 - SNDRV_PCM_FORMAT_FLOAT_LE */
+ AUDIO_FORMAT_INVALID, /* 15 - SNDRV_PCM_FORMAT_FLOAT_BE */
+ AUDIO_FORMAT_INVALID, /* 16 - SNDRV_PCM_FORMAT_FLOAT64_LE */
+ AUDIO_FORMAT_INVALID, /* 17 - SNDRV_PCM_FORMAT_FLOAT64_BE */
+ AUDIO_FORMAT_INVALID, /* 18 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE */
+ AUDIO_FORMAT_INVALID, /* 19 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE */
+ AUDIO_FORMAT_INVALID, /* 20 - SNDRV_PCM_FORMAT_MU_LAW */
+ AUDIO_FORMAT_INVALID, /* 21 - SNDRV_PCM_FORMAT_A_LAW */
+ AUDIO_FORMAT_INVALID, /* 22 - SNDRV_PCM_FORMAT_IMA_ADPCM */
+ AUDIO_FORMAT_INVALID, /* 23 - SNDRV_PCM_FORMAT_MPEG */
+ AUDIO_FORMAT_INVALID, /* 24 - SNDRV_PCM_FORMAT_GSM */
+ AUDIO_FORMAT_INVALID, /* 25 -> 30 (not assigned) */
+ AUDIO_FORMAT_INVALID,
+ AUDIO_FORMAT_INVALID,
+ AUDIO_FORMAT_INVALID,
+ AUDIO_FORMAT_INVALID,
+ AUDIO_FORMAT_INVALID,
+ AUDIO_FORMAT_INVALID, /* 31 - SNDRV_PCM_FORMAT_SPECIAL */
+ AUDIO_FORMAT_PCM_24_BIT_PACKED, /* 32 - SNDRV_PCM_FORMAT_S24_3LE */
+ AUDIO_FORMAT_INVALID, /* 33 - SNDRV_PCM_FORMAT_S24_3BE */
+ AUDIO_FORMAT_INVALID, /* 34 - SNDRV_PCM_FORMAT_U24_3LE */
+ AUDIO_FORMAT_INVALID, /* 35 - SNDRV_PCM_FORMAT_U24_3BE */
+ AUDIO_FORMAT_INVALID, /* 36 - SNDRV_PCM_FORMAT_S20_3LE */
+ AUDIO_FORMAT_INVALID, /* 37 - SNDRV_PCM_FORMAT_S20_3BE */
+ AUDIO_FORMAT_INVALID, /* 38 - SNDRV_PCM_FORMAT_U20_3LE */
+ AUDIO_FORMAT_INVALID, /* 39 - SNDRV_PCM_FORMAT_U20_3BE */
+ AUDIO_FORMAT_INVALID, /* 40 - SNDRV_PCM_FORMAT_S18_3LE */
+ AUDIO_FORMAT_INVALID, /* 41 - SNDRV_PCM_FORMAT_S18_3BE */
+ AUDIO_FORMAT_INVALID, /* 42 - SNDRV_PCM_FORMAT_U18_3LE */
+ AUDIO_FORMAT_INVALID, /* 43 - SNDRV_PCM_FORMAT_U18_3BE */
+ AUDIO_FORMAT_INVALID, /* 44 - SNDRV_PCM_FORMAT_G723_24 */
+ AUDIO_FORMAT_INVALID, /* 45 - SNDRV_PCM_FORMAT_G723_24_1B */
+ AUDIO_FORMAT_INVALID, /* 46 - SNDRV_PCM_FORMAT_G723_40 */
+ AUDIO_FORMAT_INVALID, /* 47 - SNDRV_PCM_FORMAT_G723_40_1B */
+ AUDIO_FORMAT_INVALID, /* 48 - SNDRV_PCM_FORMAT_DSD_U8 */
+ AUDIO_FORMAT_INVALID /* 49 - SNDRV_PCM_FORMAT_DSD_U16_LE */
+};
+
+audio_format_t get_format_for_mask(struct pcm_mask* mask)
+{
+ int num_slots = sizeof(mask->bits) / sizeof(mask->bits[0]);
+ int bits_per_slot = sizeof(mask->bits[0]) * 8;
+
+ int table_size = sizeof(format_value_map) / sizeof(format_value_map[0]);
+
+ int slot_index, bit_index, table_index;
+ table_index = 0;
+ int num_written = 0;
+ for (slot_index = 0; slot_index < num_slots; slot_index++) {
+ unsigned bit_mask = 1;
+ for (bit_index = 0; bit_index < bits_per_slot; bit_index++) {
+ /* don't return b-bit formats even if they are supported */
+ if (table_index >= 2 && (mask->bits[slot_index] & bit_mask) != 0) {
+ /* just return the first one */
+ return table_index < table_size
+ ? format_value_map[table_index]
+ : AUDIO_FORMAT_INVALID;
+ }
+ bit_mask <<= 1;
+ table_index++;
+ }
+ }
+
+ return AUDIO_FORMAT_INVALID;
+}
+
+/*
+ * Maps from bit position in pcm_mask to PCM_ format constants.
+ */
+int8_t const pcm_format_value_map[50] = {
+ PCM_FORMAT_S8, /* 00 - SNDRV_PCM_FORMAT_S8 */
+ PCM_FORMAT_INVALID, /* 01 - SNDRV_PCM_FORMAT_U8 */
+ PCM_FORMAT_S16_LE, /* 02 - SNDRV_PCM_FORMAT_S16_LE */
+ PCM_FORMAT_INVALID, /* 03 - SNDRV_PCM_FORMAT_S16_BE */
+ PCM_FORMAT_INVALID, /* 04 - SNDRV_PCM_FORMAT_U16_LE */
+ PCM_FORMAT_INVALID, /* 05 - SNDRV_PCM_FORMAT_U16_BE */
+ PCM_FORMAT_S24_3LE, /* 06 - SNDRV_PCM_FORMAT_S24_LE */
+ PCM_FORMAT_INVALID, /* 07 - SNDRV_PCM_FORMAT_S24_BE */
+ PCM_FORMAT_INVALID, /* 08 - SNDRV_PCM_FORMAT_U24_LE */
+ PCM_FORMAT_INVALID, /* 09 - SNDRV_PCM_FORMAT_U24_BE */
+ PCM_FORMAT_S32_LE, /* 10 - SNDRV_PCM_FORMAT_S32_LE */
+ PCM_FORMAT_INVALID, /* 11 - SNDRV_PCM_FORMAT_S32_BE */
+ PCM_FORMAT_INVALID, /* 12 - SNDRV_PCM_FORMAT_U32_LE */
+ PCM_FORMAT_INVALID, /* 13 - SNDRV_PCM_FORMAT_U32_BE */
+ PCM_FORMAT_INVALID, /* 14 - SNDRV_PCM_FORMAT_FLOAT_LE */
+ PCM_FORMAT_INVALID, /* 15 - SNDRV_PCM_FORMAT_FLOAT_BE */
+ PCM_FORMAT_INVALID, /* 16 - SNDRV_PCM_FORMAT_FLOAT64_LE */
+ PCM_FORMAT_INVALID, /* 17 - SNDRV_PCM_FORMAT_FLOAT64_BE */
+ PCM_FORMAT_INVALID, /* 18 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE */
+ PCM_FORMAT_INVALID, /* 19 - SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE */
+ PCM_FORMAT_INVALID, /* 20 - SNDRV_PCM_FORMAT_MU_LAW */
+ PCM_FORMAT_INVALID, /* 21 - SNDRV_PCM_FORMAT_A_LAW */
+ PCM_FORMAT_INVALID, /* 22 - SNDRV_PCM_FORMAT_IMA_ADPCM */
+ PCM_FORMAT_INVALID, /* 23 - SNDRV_PCM_FORMAT_MPEG */
+ PCM_FORMAT_INVALID, /* 24 - SNDRV_PCM_FORMAT_GSM */
+ PCM_FORMAT_INVALID, /* 25 -> 30 (not assigned) */
+ PCM_FORMAT_INVALID,
+ PCM_FORMAT_INVALID,
+ PCM_FORMAT_INVALID,
+ PCM_FORMAT_INVALID,
+ PCM_FORMAT_INVALID,
+ PCM_FORMAT_INVALID, /* 31 - SNDRV_PCM_FORMAT_SPECIAL */
+ PCM_FORMAT_S24_3LE, /* 32 - SNDRV_PCM_FORMAT_S24_3LE */ /* ??? */
+ PCM_FORMAT_INVALID, /* 33 - SNDRV_PCM_FORMAT_S24_3BE */
+ PCM_FORMAT_INVALID, /* 34 - SNDRV_PCM_FORMAT_U24_3LE */
+ PCM_FORMAT_INVALID, /* 35 - SNDRV_PCM_FORMAT_U24_3BE */
+ PCM_FORMAT_INVALID, /* 36 - SNDRV_PCM_FORMAT_S20_3LE */
+ PCM_FORMAT_INVALID, /* 37 - SNDRV_PCM_FORMAT_S20_3BE */
+ PCM_FORMAT_INVALID, /* 38 - SNDRV_PCM_FORMAT_U20_3LE */
+ PCM_FORMAT_INVALID, /* 39 - SNDRV_PCM_FORMAT_U20_3BE */
+ PCM_FORMAT_INVALID, /* 40 - SNDRV_PCM_FORMAT_S18_3LE */
+ PCM_FORMAT_INVALID, /* 41 - SNDRV_PCM_FORMAT_S18_3BE */
+ PCM_FORMAT_INVALID, /* 42 - SNDRV_PCM_FORMAT_U18_3LE */
+ PCM_FORMAT_INVALID, /* 43 - SNDRV_PCM_FORMAT_U18_3BE */
+ PCM_FORMAT_INVALID, /* 44 - SNDRV_PCM_FORMAT_G723_24 */
+ PCM_FORMAT_INVALID, /* 45 - SNDRV_PCM_FORMAT_G723_24_1B */
+ PCM_FORMAT_INVALID, /* 46 - SNDRV_PCM_FORMAT_G723_40 */
+ PCM_FORMAT_INVALID, /* 47 - SNDRV_PCM_FORMAT_G723_40_1B */
+ PCM_FORMAT_INVALID, /* 48 - SNDRV_PCM_FORMAT_DSD_U8 */
+ PCM_FORMAT_INVALID /* 49 - SNDRV_PCM_FORMAT_DSD_U16_LE */
+};
+
+/*
+ * Scans the provided format mask and returns the first non-8 bit sample
+ * format supported by the devices.
+ */
+enum pcm_format get_pcm_format_for_mask(struct pcm_mask* mask)
+{
+ int num_slots = ARRAY_SIZE(mask->bits);
+ int bits_per_slot = sizeof(mask->bits[0]) * 8;
+
+ int table_size = ARRAY_SIZE(pcm_format_value_map);
+
+ int slot_index, bit_index, table_index;
+ table_index = 0;
+ int num_written = 0;
+ for (slot_index = 0; slot_index < num_slots && table_index < table_size; slot_index++) {
+ unsigned bit_mask = 1;
+ for (bit_index = 0; bit_index < bits_per_slot && table_index < table_size; bit_index++) {
+ /* skip any 8-bit formats */
+ if (table_index >= 2 && (mask->bits[slot_index] & bit_mask) != 0) {
+ /* just return the first one which will be at least 16-bit */
+ return (int)pcm_format_value_map[table_index];
+ }
+ bit_mask <<= 1;
+ table_index++;
+ }
+ }
+
+ return PCM_FORMAT_INVALID;
+}
diff --git a/modules/usbaudio/format.h b/modules/usbaudio/format.h
new file mode 100644
index 0000000..e23935e
--- /dev/null
+++ b/modules/usbaudio/format.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_FORMAT_H
+#define ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_FORMAT_H
+
+#include <system/audio.h>
+
+#include <tinyalsa/asoundlib.h>
+
+audio_format_t get_format_for_mask(struct pcm_mask* mask);
+enum pcm_format get_pcm_format_for_mask(struct pcm_mask* mask);
+
+#endif /* ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_FORMAT_H */
diff --git a/modules/usbaudio/logging.c b/modules/usbaudio/logging.c
new file mode 100644
index 0000000..0a05511
--- /dev/null
+++ b/modules/usbaudio/logging.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "usb_logging"
+/*#define LOG_NDEBUG 0*/
+
+#include <string.h>
+
+#include <log/log.h>
+
+#include "logging.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+/*
+ * Logging
+ */
+void log_pcm_mask(const char* mask_name, struct pcm_mask* mask)
+{
+ const size_t num_slots = ARRAY_SIZE(mask->bits);
+ const size_t bits_per_slot = (sizeof(mask->bits[0]) * 8);
+ const size_t chars_per_slot = (bits_per_slot + 1); /* comma */
+
+ const size_t BUFF_SIZE =
+ (num_slots * chars_per_slot + 2 + 1); /* brackets and null-terminator */
+ char buff[BUFF_SIZE];
+ buff[0] = '\0';
+
+ size_t slot_index, bit_index;
+ strcat(buff, "[");
+ for (slot_index = 0; slot_index < num_slots; slot_index++) {
+ unsigned bit_mask = 1;
+ for (bit_index = 0; bit_index < bits_per_slot; bit_index++) {
+ strcat(buff, (mask->bits[slot_index] & bit_mask) != 0 ? "1" : "0");
+ bit_mask <<= 1;
+ }
+ if (slot_index < num_slots - 1) {
+ strcat(buff, ",");
+ }
+ }
+ strcat(buff, "]");
+
+ ALOGV("%s: mask:%s", mask_name, buff);
+}
+
+void log_pcm_params(struct pcm_params * alsa_hw_params)
+{
+ ALOGV("usb:audio_hw - PCM_PARAM_SAMPLE_BITS min:%u, max:%u",
+ pcm_params_get_min(alsa_hw_params, PCM_PARAM_SAMPLE_BITS),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_SAMPLE_BITS));
+ ALOGV("usb:audio_hw - PCM_PARAM_FRAME_BITS min:%u, max:%u",
+ pcm_params_get_min(alsa_hw_params, PCM_PARAM_FRAME_BITS),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_FRAME_BITS));
+ log_pcm_mask("PCM_PARAM_FORMAT",
+ pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT));
+ log_pcm_mask("PCM_PARAM_SUBFORMAT",
+ pcm_params_get_mask(alsa_hw_params, PCM_PARAM_SUBFORMAT));
+ ALOGV("usb:audio_hw - PCM_PARAM_CHANNELS min:%u, max:%u",
+ pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS));
+ ALOGV("usb:audio_hw - PCM_PARAM_RATE min:%u, max:%u",
+ pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE));
+ ALOGV("usb:audio_hw - PCM_PARAM_PERIOD_TIME min:%u, max:%u",
+ pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_TIME),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_TIME));
+ ALOGV("usb:audio_hw - PCM_PARAM_PERIOD_SIZE min:%u, max:%u",
+ pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_SIZE),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_SIZE));
+ ALOGV("usb:audio_hw - PCM_PARAM_PERIOD_BYTES min:%u, max:%u",
+ pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIOD_BYTES),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIOD_BYTES));
+ ALOGV("usb:audio_hw - PCM_PARAM_PERIODS min:%u, max:%u",
+ pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_PERIODS));
+ ALOGV("usb:audio_hw - PCM_PARAM_BUFFER_TIME min:%u, max:%u",
+ pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_TIME),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_BUFFER_TIME));
+ ALOGV("usb:audio_hw - PCM_PARAM_BUFFER_SIZE min:%u, max:%u",
+ pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_SIZE),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_BUFFER_SIZE));
+ ALOGV("usb:audio_hw - PCM_PARAM_BUFFER_BYTES min:%u, max:%u",
+ pcm_params_get_min(alsa_hw_params, PCM_PARAM_BUFFER_BYTES),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_BUFFER_BYTES));
+ ALOGV("usb:audio_hw - PCM_PARAM_TICK_TIME min:%u, max:%u",
+ pcm_params_get_min(alsa_hw_params, PCM_PARAM_TICK_TIME),
+ pcm_params_get_max(alsa_hw_params, PCM_PARAM_TICK_TIME));
+}
+
+void log_pcm_config(struct pcm_config * config, const char* label) {
+ ALOGV("log_pcm_config() - %s", label);
+ ALOGV(" channels:%d", config->channels);
+ ALOGV(" rate:%d", config->rate);
+ ALOGV(" period_size:%d", config->period_size);
+ ALOGV(" period_count:%d", config->period_count);
+ ALOGV(" format:%d", config->format);
+#if 0
+ /* Values to use for the ALSA start, stop and silence thresholds. Setting
+ * any one of these values to 0 will cause the default tinyalsa values to be
+ * used instead. Tinyalsa defaults are as follows.
+ *
+ * start_threshold : period_count * period_size
+ * stop_threshold : period_count * period_size
+ * silence_threshold : 0
+ */
+ unsigned int start_threshold;
+ unsigned int stop_threshold;
+ unsigned int silence_threshold;
+
+ /* Minimum number of frames available before pcm_mmap_write() will actually
+ * write into the kernel buffer. Only used if the stream is opened in mmap mode
+ * (pcm_open() called with PCM_MMAP flag set). Use 0 for default.
+ */
+ int avail_min;
+#endif
+}
diff --git a/modules/usbaudio/logging.h b/modules/usbaudio/logging.h
new file mode 100644
index 0000000..b5640ed
--- /dev/null
+++ b/modules/usbaudio/logging.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_LOGGING_H
+#define ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_LOGGING_H
+
+#include <tinyalsa/asoundlib.h>
+
+void log_pcm_mask(const char* mask_name, struct pcm_mask* mask);
+void log_pcm_params(struct pcm_params * alsa_hw_params);
+void log_pcm_config(struct pcm_config * config, const char* label);
+
+#endif /* ANDROID_HARDWARE_LIBHARDWARE_MODULES_USBAUDIO_LOGGING_H */