diff options
author | Paul McLean <pmclean@google.com> | 2014-07-16 09:48:34 -0700 |
---|---|---|
committer | Paul McLean <pmclean@google.com> | 2014-07-25 12:48:51 -0700 |
commit | c88e6ae584a5f7ea65ab6df89e381c2c7bf8e84e (patch) | |
tree | c4f1b2e19543ff3ff3df89b113f843ad8292f2c3 | |
parent | 962e9adc2f281b0669fbec2e7f8b011beb726d8c (diff) | |
download | hardware_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
-rw-r--r-- | modules/usbaudio/Android.mk | 6 | ||||
-rw-r--r-- | modules/usbaudio/alsa_device_profile.c | 481 | ||||
-rw-r--r-- | modules/usbaudio/alsa_device_profile.h | 88 | ||||
-rw-r--r-- | modules/usbaudio/alsa_device_proxy.c | 157 | ||||
-rw-r--r-- | modules/usbaudio/alsa_device_proxy.h | 49 | ||||
-rw-r--r-- | modules/usbaudio/audio_hw.c | 1085 | ||||
-rw-r--r-- | modules/usbaudio/format.c | 194 | ||||
-rw-r--r-- | modules/usbaudio/format.h | 27 | ||||
-rw-r--r-- | modules/usbaudio/logging.c | 129 | ||||
-rw-r--r-- | modules/usbaudio/logging.h | 26 |
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 */ |