summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2021-08-14 06:30:58 +0000
committerXin Li <delphij@google.com>2021-08-14 06:30:58 +0000
commit1a7761431d9c83fe5439bdd4bb219bbc66f33c9f (patch)
treea4fae5bd553b2fa61530cf87a79d59e969189222
parent37b2639e96e6716f4cc950519f5eebc88e9886b7 (diff)
parent920309b25fdfe05879ea3f1296f8a4c4eeca8370 (diff)
downloadlibhardware-temp_sam_202323961.tar.gz
Merge sc-dev-plus-aosp-without-vendor@7634622temp_sam_202323961
Merged-In: I8d13832dd297c5ca7ddeb87fb5302e5bad82c309 Change-Id: I9d5a74b5189e6b445ef922da0f81884d60ac82f4
-rw-r--r--modules/usbaudio/audio_hal.c1007
1 files changed, 797 insertions, 210 deletions
diff --git a/modules/usbaudio/audio_hal.c b/modules/usbaudio/audio_hal.c
index a19a0ae3..39c0fb5c 100644
--- a/modules/usbaudio/audio_hal.c
+++ b/modules/usbaudio/audio_hal.c
@@ -22,6 +22,7 @@
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/time.h>
#include <unistd.h>
@@ -68,6 +69,8 @@ struct audio_device {
bool mic_muted;
int32_t inputs_open; /* number of input streams currently open. */
+
+ audio_patch_handle_t next_patch_handle; // Increase 1 when create audio patch
};
struct stream_lock {
@@ -75,6 +78,12 @@ struct stream_lock {
pthread_mutex_t pre_lock; /* acquire before lock to avoid DOS by playback thread */
};
+struct alsa_device_info {
+ alsa_device_profile profile; /* The profile of the ALSA device */
+ alsa_device_proxy proxy; /* The state */
+ struct listnode list_node;
+};
+
struct stream_out {
struct audio_stream_out stream;
@@ -84,10 +93,7 @@ struct stream_out {
struct audio_device *adev; /* hardware information - only using this for the lock */
- alsa_device_profile profile; /* The profile of the ALSA device connected to the stream.
- */
-
- alsa_device_proxy proxy; /* state of the stream */
+ struct listnode alsa_devices; /* The ALSA devices connected to the stream. */
unsigned hal_channel_count; /* channel count exposed to AudioFlinger.
* This may differ from the device channel count when
@@ -106,6 +112,12 @@ struct stream_out {
* they could come from here too if
* there was a previous conversion */
size_t conversion_buffer_size; /* in bytes */
+
+ struct pcm_config config;
+
+ audio_io_handle_t handle; // Unique constant for a stream
+
+ audio_patch_handle_t patch_handle; // Patch handle for this stream
};
struct stream_in {
@@ -117,10 +129,7 @@ struct stream_in {
struct audio_device *adev; /* hardware information - only using this for the lock */
- alsa_device_profile profile; /* The profile of the ALSA device connected to the stream.
- */
-
- alsa_device_proxy proxy; /* state of the stream */
+ struct listnode alsa_devices; /* The ALSA devices connected to the stream. */
unsigned hal_channel_count; /* channel count exposed to AudioFlinger.
* This may differ from the device channel count when
@@ -140,7 +149,79 @@ struct stream_in {
* they could come from here too if
* there was a previous conversion */
size_t conversion_buffer_size; /* in bytes */
+
+ struct pcm_config config;
+
+ audio_io_handle_t handle; // Unique identifier for a stream
+
+ audio_patch_handle_t patch_handle; // Patch handle for this stream
+};
+
+// Map channel count to output channel mask
+static const audio_channel_mask_t OUT_CHANNEL_MASKS_MAP[FCC_24 + 1] = {
+ [0] = AUDIO_CHANNEL_NONE, // == 0 (so this line is optional and could be omitted)
+ // != AUDIO_CHANNEL_INVALID == 0xC0000000u
+
+ [1] = AUDIO_CHANNEL_OUT_MONO,
+ [2] = AUDIO_CHANNEL_OUT_STEREO,
+ [3] = AUDIO_CHANNEL_OUT_2POINT1,
+ [4] = AUDIO_CHANNEL_OUT_QUAD,
+ [5] = AUDIO_CHANNEL_OUT_PENTA,
+ [6] = AUDIO_CHANNEL_OUT_5POINT1,
+ [7] = AUDIO_CHANNEL_OUT_6POINT1,
+ [8] = AUDIO_CHANNEL_OUT_7POINT1,
+
+ [9 ... 11] = AUDIO_CHANNEL_NONE, // == 0 (so this line is optional and could be omitted).
+
+ [12] = AUDIO_CHANNEL_OUT_7POINT1POINT4,
+
+ [13 ... 23] = AUDIO_CHANNEL_NONE, // == 0 (so this line is optional and could be omitted).
+
+ [24] = AUDIO_CHANNEL_OUT_22POINT2,
+};
+static const int OUT_CHANNEL_MASKS_SIZE = AUDIO_ARRAY_SIZE(OUT_CHANNEL_MASKS_MAP);
+
+// Map channel count to input channel mask
+static const audio_channel_mask_t IN_CHANNEL_MASKS_MAP[] = {
+ AUDIO_CHANNEL_NONE, /* 0 */
+ AUDIO_CHANNEL_IN_MONO, /* 1 */
+ AUDIO_CHANNEL_IN_STEREO, /* 2 */
+ /* channel counts greater than this are not considered */
};
+static const int IN_CHANNEL_MASKS_SIZE = AUDIO_ARRAY_SIZE(IN_CHANNEL_MASKS_MAP);
+
+// Map channel count to index mask
+static const audio_channel_mask_t CHANNEL_INDEX_MASKS_MAP[FCC_24 + 1] = {
+ [0] = AUDIO_CHANNEL_NONE, // == 0 (so this line is optional and could be omitted).
+
+ [1] = AUDIO_CHANNEL_INDEX_MASK_1,
+ [2] = AUDIO_CHANNEL_INDEX_MASK_2,
+ [3] = AUDIO_CHANNEL_INDEX_MASK_3,
+ [4] = AUDIO_CHANNEL_INDEX_MASK_4,
+ [5] = AUDIO_CHANNEL_INDEX_MASK_5,
+ [6] = AUDIO_CHANNEL_INDEX_MASK_6,
+ [7] = AUDIO_CHANNEL_INDEX_MASK_7,
+ [8] = AUDIO_CHANNEL_INDEX_MASK_8,
+
+ [9] = AUDIO_CHANNEL_INDEX_MASK_9,
+ [10] = AUDIO_CHANNEL_INDEX_MASK_10,
+ [11] = AUDIO_CHANNEL_INDEX_MASK_11,
+ [12] = AUDIO_CHANNEL_INDEX_MASK_12,
+ [13] = AUDIO_CHANNEL_INDEX_MASK_13,
+ [14] = AUDIO_CHANNEL_INDEX_MASK_14,
+ [15] = AUDIO_CHANNEL_INDEX_MASK_15,
+ [16] = AUDIO_CHANNEL_INDEX_MASK_16,
+
+ [17] = AUDIO_CHANNEL_INDEX_MASK_17,
+ [18] = AUDIO_CHANNEL_INDEX_MASK_18,
+ [19] = AUDIO_CHANNEL_INDEX_MASK_19,
+ [20] = AUDIO_CHANNEL_INDEX_MASK_20,
+ [21] = AUDIO_CHANNEL_INDEX_MASK_21,
+ [22] = AUDIO_CHANNEL_INDEX_MASK_22,
+ [23] = AUDIO_CHANNEL_INDEX_MASK_23,
+ [24] = AUDIO_CHANNEL_INDEX_MASK_24,
+};
+static const int CHANNEL_INDEX_MASKS_SIZE = AUDIO_ARRAY_SIZE(CHANNEL_INDEX_MASKS_MAP);
/*
* Locking Helpers
@@ -158,6 +239,9 @@ static void stream_lock_init(struct stream_lock *lock) {
}
static void stream_lock(struct stream_lock *lock) {
+ if (lock == NULL) {
+ return;
+ }
pthread_mutex_lock(&lock->pre_lock);
pthread_mutex_lock(&lock->lock);
pthread_mutex_unlock(&lock->pre_lock);
@@ -191,13 +275,52 @@ static void adev_add_stream_to_list(
device_unlock(adev);
}
-static void adev_remove_stream_from_list(
- struct audio_device* adev, struct listnode* stream_node) {
- device_lock(adev);
+static struct stream_out* adev_get_stream_out_by_io_handle_l(
+ struct audio_device* adev, audio_io_handle_t handle) {
+ struct listnode *node;
+ list_for_each (node, &adev->output_stream_list) {
+ struct stream_out *out = node_to_item(node, struct stream_out, list_node);
+ if (out->handle == handle) {
+ return out;
+ }
+ }
+ return NULL;
+}
- list_remove(stream_node);
+static struct stream_in* adev_get_stream_in_by_io_handle_l(
+ struct audio_device* adev, audio_io_handle_t handle) {
+ struct listnode *node;
+ list_for_each (node, &adev->input_stream_list) {
+ struct stream_in *in = node_to_item(node, struct stream_in, list_node);
+ if (in->handle == handle) {
+ return in;
+ }
+ }
+ return NULL;
+}
- device_unlock(adev);
+static struct stream_out* adev_get_stream_out_by_patch_handle_l(
+ struct audio_device* adev, audio_patch_handle_t patch_handle) {
+ struct listnode *node;
+ list_for_each (node, &adev->output_stream_list) {
+ struct stream_out *out = node_to_item(node, struct stream_out, list_node);
+ if (out->patch_handle == patch_handle) {
+ return out;
+ }
+ }
+ return NULL;
+}
+
+static struct stream_in* adev_get_stream_in_by_patch_handle_l(
+ struct audio_device* adev, audio_patch_handle_t patch_handle) {
+ struct listnode *node;
+ list_for_each (node, &adev->input_stream_list) {
+ struct stream_in *in = node_to_item(node, struct stream_in, list_node);
+ if (in->patch_handle == patch_handle) {
+ return in;
+ }
+ }
+ return NULL;
}
/*
@@ -278,6 +401,65 @@ static char *device_get_parameters(const alsa_device_profile *profile, const cha
return result_str;
}
+static audio_format_t audio_format_from(enum pcm_format format)
+{
+ switch (format) {
+ case PCM_FORMAT_S16_LE:
+ return AUDIO_FORMAT_PCM_16_BIT;
+ case PCM_FORMAT_S32_LE:
+ return AUDIO_FORMAT_PCM_32_BIT;
+ case PCM_FORMAT_S8:
+ return AUDIO_FORMAT_PCM_8_BIT;
+ case PCM_FORMAT_S24_LE:
+ return AUDIO_FORMAT_PCM_8_24_BIT;
+ case PCM_FORMAT_S24_3LE:
+ return AUDIO_FORMAT_PCM_24_BIT_PACKED;
+ default:
+ return AUDIO_FORMAT_INVALID;
+ }
+}
+
+static unsigned int populate_channel_mask_from_profile(const alsa_device_profile* profile,
+ bool is_output,
+ audio_channel_mask_t channel_masks[])
+{
+ unsigned int num_channel_masks = 0;
+ const audio_channel_mask_t* channel_masks_map =
+ is_output ? OUT_CHANNEL_MASKS_MAP : IN_CHANNEL_MASKS_MAP;
+ int channel_masks_size = is_output ? OUT_CHANNEL_MASKS_SIZE : IN_CHANNEL_MASKS_SIZE;
+ if (channel_masks_size > FCC_LIMIT + 1) {
+ channel_masks_size = FCC_LIMIT + 1;
+ }
+ unsigned int channel_count = 0;
+ for (size_t i = 0; i < min(channel_masks_size, AUDIO_PORT_MAX_CHANNEL_MASKS) &&
+ (channel_count = profile->channel_counts[i]) != 0 &&
+ num_channel_masks < AUDIO_PORT_MAX_CHANNEL_MASKS; ++i) {
+ if (channel_count < channel_masks_size &&
+ channel_masks_map[channel_count] != AUDIO_CHANNEL_NONE) {
+ channel_masks[num_channel_masks++] = channel_masks_map[channel_count];
+ if (num_channel_masks >= AUDIO_PORT_MAX_CHANNEL_MASKS) {
+ break;
+ }
+ }
+ if (channel_count < CHANNEL_INDEX_MASKS_SIZE &&
+ CHANNEL_INDEX_MASKS_MAP[channel_count] != AUDIO_CHANNEL_NONE) {
+ channel_masks[num_channel_masks++] = CHANNEL_INDEX_MASKS_MAP[channel_count];
+ }
+ }
+ return num_channel_masks;
+}
+
+static unsigned int populate_sample_rates_from_profile(const alsa_device_profile* profile,
+ unsigned int sample_rates[])
+{
+ unsigned int num_sample_rates = 0;
+ for (;num_sample_rates < min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) &&
+ profile->sample_rates[num_sample_rates] != 0; num_sample_rates++) {
+ sample_rates[num_sample_rates] = profile->sample_rates[num_sample_rates];
+ }
+ return num_sample_rates;
+}
+
/*
* HAl Functions
*/
@@ -286,12 +468,106 @@ static char *device_get_parameters(const alsa_device_profile *profile, const cha
* following order: hw device > out stream
*/
+static struct alsa_device_info* stream_get_first_alsa_device(const struct listnode *alsa_devices) {
+ if (list_empty(alsa_devices)) {
+ return NULL;
+ }
+ return node_to_item(list_head(alsa_devices), struct alsa_device_info, list_node);
+}
+
+/**
+ * Must be called with holding the stream's lock.
+ */
+static void stream_standby_l(struct listnode *alsa_devices, bool *standby)
+{
+ if (!*standby) {
+ struct listnode *node;
+ list_for_each (node, alsa_devices) {
+ struct alsa_device_info *device_info =
+ node_to_item(node, struct alsa_device_info, list_node);
+ proxy_close(&device_info->proxy);
+ }
+ *standby = true;
+ }
+}
+
+static void stream_clear_devices(struct listnode *alsa_devices)
+{
+ struct listnode *node, *temp;
+ struct alsa_device_info *device_info = NULL;
+ list_for_each_safe (node, temp, alsa_devices) {
+ device_info = node_to_item(node, struct alsa_device_info, list_node);
+ if (device_info != NULL) {
+ list_remove(&device_info->list_node);
+ free(device_info);
+ }
+ }
+}
+
+static int stream_set_new_devices(struct pcm_config *config,
+ struct listnode *alsa_devices,
+ unsigned int num_devices,
+ const int cards[],
+ const int devices[],
+ int direction)
+{
+ int status = 0;
+ stream_clear_devices(alsa_devices);
+
+ for (unsigned int i = 0; i < num_devices; ++i) {
+ struct alsa_device_info *device_info =
+ (struct alsa_device_info *) calloc(1, sizeof(struct alsa_device_info));
+ profile_init(&device_info->profile, direction);
+ device_info->profile.card = cards[i];
+ device_info->profile.device = devices[i];
+ status = profile_read_device_info(&device_info->profile) ? 0 : -EINVAL;
+ if (status != 0) {
+ ALOGE("%s failed to read device info card=%d;device=%d",
+ __func__, cards[i], devices[i]);
+ goto exit;
+ }
+ status = proxy_prepare(&device_info->proxy, &device_info->profile, config);
+ if (status != 0) {
+ ALOGE("%s failed to prepare device card=%d;device=%d",
+ __func__, cards[i], devices[i]);
+ goto exit;
+ }
+ list_add_tail(alsa_devices, &device_info->list_node);
+ }
+
+exit:
+ if (status != 0) {
+ stream_clear_devices(alsa_devices);
+ }
+ return status;
+}
+
+static void stream_dump_alsa_devices(const struct listnode *alsa_devices, int fd) {
+ struct listnode *node;
+ size_t i = 0;
+ list_for_each(node, alsa_devices) {
+ struct alsa_device_info *device_info =
+ node_to_item(node, struct alsa_device_info, list_node);
+ dprintf(fd, "Output Profile %zu:\n", i);
+ profile_dump(&device_info->profile, fd);
+
+ dprintf(fd, "Output Proxy %zu:\n", i);
+ proxy_dump(&device_info->proxy, fd);
+ }
+}
+
/*
* OUT functions
*/
static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
- uint32_t rate = proxy_get_sample_rate(&((struct stream_out*)stream)->proxy);
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(
+ &((struct stream_out*)stream)->alsa_devices);
+ if (device_info == NULL) {
+ ALOGW("%s device info is null", __func__);
+ return 0;
+ }
+ uint32_t rate = proxy_get_sample_rate(&device_info->proxy);
ALOGV("out_get_sample_rate() = %d", rate);
return rate;
}
@@ -304,9 +580,12 @@ 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)
{
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));
- return buffer_size;
+ const struct alsa_device_info* device_info = stream_get_first_alsa_device(&out->alsa_devices);
+ if (device_info == NULL) {
+ ALOGW("%s device info is null", __func__);
+ return 0;
+ }
+ return proxy_get_period_size(&device_info->proxy) * audio_stream_out_frame_size(&(out->stream));
}
static uint32_t out_get_channels(const struct audio_stream *stream)
@@ -321,8 +600,13 @@ static audio_format_t out_get_format(const struct audio_stream *stream)
* 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));
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(
+ &((struct stream_out*)stream)->alsa_devices);
+ if (device_info == NULL) {
+ ALOGW("%s device info is null", __func__);
+ return AUDIO_FORMAT_DEFAULT;
+ }
+ audio_format_t format = audio_format_from_pcm_format(proxy_get_format(&device_info->proxy));
return format;
}
@@ -336,10 +620,9 @@ static int out_standby(struct audio_stream *stream)
struct stream_out *out = (struct stream_out *)stream;
stream_lock(&out->lock);
- if (!out->standby) {
- proxy_close(&out->proxy);
- out->standby = true;
- }
+ device_lock(out->adev);
+ stream_standby_l(&out->alsa_devices, &out->standby);
+ device_unlock(out->adev);
stream_unlock(&out->lock);
return 0;
}
@@ -348,67 +631,44 @@ static int out_dump(const struct audio_stream *stream, int fd) {
const struct stream_out* out_stream = (const struct stream_out*) stream;
if (out_stream != NULL) {
- dprintf(fd, "Output Profile:\n");
- profile_dump(&out_stream->profile, fd);
-
- dprintf(fd, "Output Proxy:\n");
- proxy_dump(&out_stream->proxy, fd);
+ stream_dump_alsa_devices(&out_stream->alsa_devices, fd);
}
return 0;
}
-static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+static int out_set_parameters(struct audio_stream *stream __unused, const char *kvpairs)
{
ALOGV("out_set_parameters() keys:%s", kvpairs);
- struct stream_out *out = (struct stream_out *)stream;
-
- int ret_value = 0;
- int card = -1;
- int device = -1;
-
- if (!parse_card_device_params(kvpairs, &card, &device)) {
- // nothing to do
- return ret_value;
- }
-
- stream_lock(&out->lock);
- if (!profile_is_cached_for(&out->profile, card, device)) {
- /* cannot read pcm device info if playback is active */
- if (!out->standby)
- ret_value = -ENOSYS;
- else {
- int saved_card = out->profile.card;
- int saved_device = out->profile.device;
- out->profile.card = card;
- out->profile.device = device;
- ret_value = profile_read_device_info(&out->profile) ? 0 : -EINVAL;
- if (ret_value != 0) {
- out->profile.card = saved_card;
- out->profile.device = saved_device;
- }
- }
- }
-
- stream_unlock(&out->lock);
-
- return ret_value;
+ // The set parameters here only matters when the routing devices are changed.
+ // When the device version is not less than 3.0, the framework will use create
+ // audio patch API instead of set parameters to chanage audio routing.
+ return 0;
}
static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
{
struct stream_out *out = (struct stream_out *)stream;
stream_lock(&out->lock);
- char * params_str = device_get_parameters(&out->profile, keys);
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(&out->alsa_devices);
+ char *params_str = NULL;
+ if (device_info != NULL) {
+ params_str = device_get_parameters(&device_info->profile, keys);
+ }
stream_unlock(&out->lock);
return params_str;
}
static uint32_t out_get_latency(const struct audio_stream_out *stream)
{
- alsa_device_proxy * proxy = &((struct stream_out*)stream)->proxy;
- return proxy_get_latency(proxy);
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(
+ &((struct stream_out*)stream)->alsa_devices);
+ if (device_info == NULL) {
+ ALOGW("%s device info is null", __func__);
+ return 0;
+ }
+ return proxy_get_latency(&device_info->proxy);
}
static int out_set_volume(struct audio_stream_out *stream, float left, float right)
@@ -419,9 +679,31 @@ 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)
{
- ALOGV("start_output_stream(card:%d device:%d)", out->profile.card, out->profile.device);
+ int status = 0;
+ struct listnode *node;
+ list_for_each(node, &out->alsa_devices) {
+ struct alsa_device_info *device_info =
+ node_to_item(node, struct alsa_device_info, list_node);
+ ALOGV("start_output_stream(card:%d device:%d)",
+ device_info->profile.card, device_info->profile.device);
+ status = proxy_open(&device_info->proxy);
+ if (status != 0) {
+ ALOGE("%s failed to open device(card: %d device: %d)",
+ __func__, device_info->profile.card, device_info->profile.device);
+ goto exit;
+ }
+ }
+
+exit:
+ if (status != 0) {
+ list_for_each(node, &out->alsa_devices) {
+ struct alsa_device_info *device_info =
+ node_to_item(node, struct alsa_device_info, list_node);
+ proxy_close(&device_info->proxy);
+ }
- return proxy_open(&out->proxy);
+ }
+ return status;
}
static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, size_t bytes)
@@ -438,32 +720,37 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, si
out->standby = false;
}
- alsa_device_proxy* proxy = &out->proxy;
- const void * write_buff = buffer;
- int num_write_buff_bytes = bytes;
- const int num_device_channels = proxy_get_channel_count(proxy); /* what we told alsa */
- const int num_req_channels = out->hal_channel_count; /* what we told AudioFlinger */
- if (num_device_channels != num_req_channels) {
- /* allocate buffer */
- const size_t required_conversion_buffer_size =
- bytes * num_device_channels / num_req_channels;
- if (required_conversion_buffer_size > out->conversion_buffer_size) {
- out->conversion_buffer_size = required_conversion_buffer_size;
- out->conversion_buffer = realloc(out->conversion_buffer,
- out->conversion_buffer_size);
+ struct listnode* node;
+ list_for_each(node, &out->alsa_devices) {
+ struct alsa_device_info* device_info =
+ node_to_item(node, struct alsa_device_info, list_node);
+ alsa_device_proxy* proxy = &device_info->proxy;
+ const void * write_buff = buffer;
+ int num_write_buff_bytes = bytes;
+ const int num_device_channels = proxy_get_channel_count(proxy); /* what we told alsa */
+ const int num_req_channels = out->hal_channel_count; /* what we told AudioFlinger */
+ if (num_device_channels != num_req_channels) {
+ /* allocate buffer */
+ const size_t required_conversion_buffer_size =
+ bytes * num_device_channels / num_req_channels;
+ if (required_conversion_buffer_size > out->conversion_buffer_size) {
+ out->conversion_buffer_size = required_conversion_buffer_size;
+ out->conversion_buffer = realloc(out->conversion_buffer,
+ out->conversion_buffer_size);
+ }
+ /* convert data */
+ const audio_format_t audio_format = out_get_format(&(out->stream.common));
+ const unsigned sample_size_in_bytes = audio_bytes_per_sample(audio_format);
+ num_write_buff_bytes =
+ adjust_channels(write_buff, num_req_channels,
+ out->conversion_buffer, num_device_channels,
+ sample_size_in_bytes, num_write_buff_bytes);
+ write_buff = out->conversion_buffer;
}
- /* convert data */
- const audio_format_t audio_format = out_get_format(&(out->stream.common));
- const unsigned sample_size_in_bytes = audio_bytes_per_sample(audio_format);
- num_write_buff_bytes =
- adjust_channels(write_buff, num_req_channels,
- out->conversion_buffer, num_device_channels,
- sample_size_in_bytes, num_write_buff_bytes);
- write_buff = out->conversion_buffer;
- }
- if (write_buff != NULL && num_write_buff_bytes != 0) {
- proxy_write(&out->proxy, write_buff, num_write_buff_bytes);
+ if (write_buff != NULL && num_write_buff_bytes != 0) {
+ proxy_write(proxy, write_buff, num_write_buff_bytes);
+ }
}
stream_unlock(&out->lock);
@@ -491,9 +778,9 @@ static int out_get_presentation_position(const struct audio_stream_out *stream,
struct stream_out *out = (struct stream_out *)stream; // discard const qualifier
stream_lock(&out->lock);
- const alsa_device_proxy *proxy = &out->proxy;
- const int ret = proxy_get_presentation_position(proxy, frames, timestamp);
-
+ const struct alsa_device_info* device_info = stream_get_first_alsa_device(&out->alsa_devices);
+ const int ret = device_info == NULL ? -ENODEV :
+ proxy_get_presentation_position(&device_info->proxy, frames, timestamp);
stream_unlock(&out->lock);
return ret;
}
@@ -551,30 +838,35 @@ static int adev_open_output_stream(struct audio_hw_device *hw_dev,
out->stream.get_presentation_position = out_get_presentation_position;
out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
+ out->handle = handle;
+
stream_lock_init(&out->lock);
out->adev = (struct audio_device *)hw_dev;
- profile_init(&out->profile, PCM_OUT);
+ list_init(&out->alsa_devices);
+ struct alsa_device_info *device_info =
+ (struct alsa_device_info *)calloc(1, sizeof(struct alsa_device_info));
+ profile_init(&device_info->profile, PCM_OUT);
// build this to hand to the alsa_device_proxy
- struct pcm_config proxy_config;
- memset(&proxy_config, 0, sizeof(proxy_config));
+ struct pcm_config proxy_config = {};
/* Pull out the card/device pair */
- parse_card_device_params(address, &out->profile.card, &out->profile.device);
+ parse_card_device_params(address, &device_info->profile.card, &device_info->profile.device);
- profile_read_device_info(&out->profile);
+ profile_read_device_info(&device_info->profile);
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 = profile_get_default_sample_rate(&device_info->profile);
+ } else if (profile_is_sample_rate_valid(&device_info->profile, config->sample_rate)) {
proxy_config.rate = config->sample_rate;
} else {
- proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(&out->profile);
+ proxy_config.rate = config->sample_rate =
+ profile_get_default_sample_rate(&device_info->profile);
ret = -EINVAL;
}
@@ -585,14 +877,14 @@ static int adev_open_output_stream(struct audio_hw_device *hw_dev,
/* Format */
if (config->format == AUDIO_FORMAT_DEFAULT) {
- proxy_config.format = profile_get_default_format(&out->profile);
+ proxy_config.format = profile_get_default_format(&device_info->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)) {
+ if (profile_is_format_valid(&device_info->profile, fmt)) {
proxy_config.format = fmt;
} else {
- proxy_config.format = profile_get_default_format(&out->profile);
+ proxy_config.format = profile_get_default_format(&device_info->profile);
config->format = audio_format_from_pcm_format(proxy_config.format);
ret = -EINVAL;
}
@@ -602,7 +894,7 @@ static int adev_open_output_stream(struct audio_hw_device *hw_dev,
bool calc_mask = false;
if (config->channel_mask == AUDIO_CHANNEL_NONE) {
/* query case */
- out->hal_channel_count = profile_get_default_channel_count(&out->profile);
+ out->hal_channel_count = profile_get_default_channel_count(&device_info->profile);
calc_mask = true;
} else {
/* explicit case */
@@ -610,19 +902,19 @@ static int adev_open_output_stream(struct audio_hw_device *hw_dev,
}
/* The Framework is currently limited to no more than this number of channels */
- if (out->hal_channel_count > FCC_8) {
- out->hal_channel_count = FCC_8;
+ if (out->hal_channel_count > FCC_LIMIT) {
+ out->hal_channel_count = FCC_LIMIT;
calc_mask = true;
}
if (calc_mask) {
/* need to calculate the mask from channel count either because this is the query case
- * or the specified mask isn't valid for this device, or is more then the FW can handle */
+ * or the specified mask isn't valid for this device, or is more than the FW can handle */
config->channel_mask = out->hal_channel_count <= FCC_2
- /* position mask for mono and stereo*/
- ? audio_channel_out_mask_from_count(out->hal_channel_count)
- /* otherwise indexed */
- : audio_channel_mask_for_index_assignment_from_count(out->hal_channel_count);
+ /* position mask for mono and stereo*/
+ ? audio_channel_out_mask_from_count(out->hal_channel_count)
+ /* otherwise indexed */
+ : audio_channel_mask_for_index_assignment_from_count(out->hal_channel_count);
}
out->hal_channel_mask = config->channel_mask;
@@ -631,8 +923,11 @@ static int adev_open_output_stream(struct audio_hw_device *hw_dev,
// if they differ, choose the "actual" number of channels *closest* to the "logical".
// and store THAT in proxy_config.channels
proxy_config.channels =
- profile_get_closest_channel_count(&out->profile, out->hal_channel_count);
- proxy_prepare(&out->proxy, &out->profile, &proxy_config);
+ profile_get_closest_channel_count(&device_info->profile, out->hal_channel_count);
+ proxy_prepare(&device_info->proxy, &device_info->profile, &proxy_config);
+ out->config = proxy_config;
+
+ list_add_tail(&out->alsa_devices, &device_info->list_node);
/* TODO The retry mechanism isn't implemented in AudioPolicyManager/AudioFlinger
* So clear any errors that may have occurred above.
@@ -656,21 +951,22 @@ static void adev_close_output_stream(struct audio_hw_device *hw_dev,
struct audio_stream_out *stream)
{
struct stream_out *out = (struct stream_out *)stream;
- ALOGV("adev_close_output_stream(c:%d d:%d)", out->profile.card, out->profile.device);
+ stream_lock(&out->lock);
/* Close the pcm device */
- out_standby(&stream->common);
+ stream_standby_l(&out->alsa_devices, &out->standby);
+ stream_clear_devices(&out->alsa_devices);
free(out->conversion_buffer);
out->conversion_buffer = NULL;
out->conversion_buffer_size = 0;
- adev_remove_stream_from_list(out->adev, &out->list_node);
-
device_lock(out->adev);
+ list_remove(&out->list_node);
out->adev->device_sample_rate = 0;
device_unlock(out->adev);
+ stream_unlock(&out->lock);
free(stream);
}
@@ -687,7 +983,13 @@ static size_t adev_get_input_buffer_size(const struct audio_hw_device *hw_dev,
*/
static uint32_t in_get_sample_rate(const struct audio_stream *stream)
{
- uint32_t rate = proxy_get_sample_rate(&((const struct stream_in *)stream)->proxy);
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(
+ &((const struct stream_in *)stream)->alsa_devices);
+ if (device_info == NULL) {
+ ALOGW("%s device info is null", __func__);
+ return 0;
+ }
+ uint32_t rate = proxy_get_sample_rate(&device_info->proxy);
ALOGV("in_get_sample_rate() = %d", rate);
return rate;
}
@@ -701,7 +1003,12 @@ static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
static size_t in_get_buffer_size(const struct audio_stream *stream)
{
const struct stream_in * in = ((const struct stream_in*)stream);
- return proxy_get_period_size(&in->proxy) * audio_stream_in_frame_size(&(in->stream));
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices);
+ if (device_info == NULL) {
+ ALOGW("%s device info is null", __func__);
+ return 0;
+ }
+ return proxy_get_period_size(&device_info->proxy) * audio_stream_in_frame_size(&(in->stream));
}
static uint32_t in_get_channels(const struct audio_stream *stream)
@@ -712,7 +1019,13 @@ static uint32_t in_get_channels(const struct audio_stream *stream)
static audio_format_t in_get_format(const struct audio_stream *stream)
{
- alsa_device_proxy *proxy = &((struct stream_in*)stream)->proxy;
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(
+ &((const struct stream_in *)stream)->alsa_devices);
+ if (device_info == NULL) {
+ ALOGW("%s device info is null", __func__);
+ return AUDIO_FORMAT_DEFAULT;
+ }
+ alsa_device_proxy *proxy = &device_info->proxy;
audio_format_t format = audio_format_from_pcm_format(proxy_get_format(proxy));
return format;
}
@@ -729,12 +1042,10 @@ static int in_standby(struct audio_stream *stream)
struct stream_in *in = (struct stream_in *)stream;
stream_lock(&in->lock);
- if (!in->standby) {
- proxy_close(&in->proxy);
- in->standby = true;
- }
+ device_lock(in->adev);
+ stream_standby_l(&in->alsa_devices, &in->standby);
+ device_unlock(in->adev);
stream_unlock(&in->lock);
-
return 0;
}
@@ -742,11 +1053,7 @@ static int in_dump(const struct audio_stream *stream, int fd)
{
const struct stream_in* in_stream = (const struct stream_in*)stream;
if (in_stream != NULL) {
- dprintf(fd, "Input Profile:\n");
- profile_dump(&in_stream->profile, fd);
-
- dprintf(fd, "Input Proxy:\n");
- proxy_dump(&in_stream->proxy, fd);
+ stream_dump_alsa_devices(&in_stream->alsa_devices, fd);
}
return 0;
@@ -756,42 +1063,10 @@ static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
ALOGV("in_set_parameters() keys:%s", kvpairs);
- struct stream_in *in = (struct stream_in *)stream;
-
- int ret_value = 0;
- int card = -1;
- int device = -1;
-
- if (!parse_card_device_params(kvpairs, &card, &device)) {
- // nothing to do
- return ret_value;
- }
-
- stream_lock(&in->lock);
- device_lock(in->adev);
-
- if (card >= 0 && device >= 0 && !profile_is_cached_for(&in->profile, card, device)) {
- /* cannot read pcm device info if capture is active, or more than one open stream */
- if (!in->standby || in->adev->inputs_open > 1)
- ret_value = -ENOSYS;
- else {
- int saved_card = in->profile.card;
- int saved_device = in->profile.device;
- in->profile.card = card;
- in->profile.device = device;
- ret_value = profile_read_device_info(&in->profile) ? 0 : -EINVAL;
- if (ret_value != 0) {
- ALOGE("Can't read device profile. card:%d, device:%d", card, device);
- in->profile.card = saved_card;
- in->profile.device = saved_device;
- }
- }
- }
-
- device_unlock(in->adev);
- stream_unlock(&in->lock);
-
- return ret_value;
+ // The set parameters here only matters when the routing devices are changed.
+ // When the device version higher than 3.0, the framework will use create_audio_patch
+ // API instead of set_parameters to change audio routing.
+ return 0;
}
static char * in_get_parameters(const struct audio_stream *stream, const char *keys)
@@ -799,7 +1074,11 @@ static char * in_get_parameters(const struct audio_stream *stream, const char *k
struct stream_in *in = (struct stream_in *)stream;
stream_lock(&in->lock);
- char * params_str = device_get_parameters(&in->profile, keys);
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices);
+ char *params_str = NULL;
+ if (device_info != NULL) {
+ params_str = device_get_parameters(&device_info->profile, keys);
+ }
stream_unlock(&in->lock);
return params_str;
@@ -823,9 +1102,15 @@ 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)
{
- ALOGV("start_input_stream(card:%d device:%d)", in->profile.card, in->profile.device);
+ // Only care about the first device as only one input device is allowed.
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices);
+ if (device_info == NULL) {
+ return -ENODEV;
+ }
- return proxy_open(&in->proxy);
+ ALOGV("start_input_stream(card:%d device:%d)",
+ device_info->profile.card, device_info->profile.device);
+ return proxy_open(&device_info->proxy);
}
/* TODO mutex stuff here (see out_write) */
@@ -847,12 +1132,18 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t byte
in->standby = false;
}
+ // Only care about the first device as only one input device is allowed.
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices);
+ if (device_info == NULL) {
+ return 0;
+ }
+
/*
* OK, we need to figure out how much data to read to be able to output the requested
* number of bytes in the HAL format (16-bit, stereo).
*/
num_read_buff_bytes = bytes;
- int num_device_channels = proxy_get_channel_count(&in->proxy); /* what we told Alsa */
+ int num_device_channels = proxy_get_channel_count(&device_info->proxy); /* what we told Alsa */
int num_req_channels = in->hal_channel_count; /* what we told AudioFlinger */
if (num_device_channels != num_req_channels) {
@@ -870,7 +1161,7 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t byte
read_buff = in->conversion_buffer;
}
- ret = proxy_read(&in->proxy, read_buff, num_read_buff_bytes);
+ ret = proxy_read(&device_info->proxy, read_buff, num_read_buff_bytes);
if (ret == 0) {
if (num_device_channels != num_req_channels) {
// ALOGV("chans dev:%d req:%d", num_device_channels, num_req_channels);
@@ -911,8 +1202,10 @@ static int in_get_capture_position(const struct audio_stream_in *stream,
struct stream_in *in = (struct stream_in *)stream; // discard const qualifier
stream_lock(&in->lock);
- const alsa_device_proxy *proxy = &in->proxy;
- const int ret = proxy_get_capture_position(proxy, frames, time);
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices);
+
+ const int ret = device_info == NULL ? -ENODEV
+ : proxy_get_capture_position(&device_info->proxy, frames, time);
stream_unlock(&in->lock);
return ret;
@@ -991,14 +1284,18 @@ static int adev_open_input_stream(struct audio_hw_device *hw_dev,
in->stream.set_microphone_direction = in_set_microphone_direction;
in->stream.set_microphone_field_dimension = in_set_microphone_field_dimension;
+ in->handle = handle;
+
stream_lock_init(&in->lock);
in->adev = (struct audio_device *)hw_dev;
- profile_init(&in->profile, PCM_IN);
+ list_init(&in->alsa_devices);
+ struct alsa_device_info *device_info =
+ (struct alsa_device_info *)calloc(1, sizeof(struct alsa_device_info));
+ profile_init(&device_info->profile, PCM_IN);
- struct pcm_config proxy_config;
- memset(&proxy_config, 0, sizeof(proxy_config));
+ memset(&in->config, 0, sizeof(in->config));
int ret = 0;
device_lock(in->adev);
@@ -1007,16 +1304,16 @@ static int adev_open_input_stream(struct audio_hw_device *hw_dev,
/* Check if an input stream is already open */
if (num_open_inputs > 0) {
- if (!profile_is_cached_for(&in->profile, card, device)) {
+ if (!profile_is_cached_for(&device_info->profile, card, device)) {
ALOGW("%s fail - address card:%d device:%d doesn't match existing profile",
__func__, card, device);
ret = -EINVAL;
}
} else {
/* Read input profile only if necessary */
- in->profile.card = card;
- in->profile.device = device;
- if (!profile_read_device_info(&in->profile)) {
+ device_info->profile.card = card;
+ device_info->profile.device = device;
+ if (!profile_read_device_info(&device_info->profile)) {
ALOGW("%s fail - cannot read profile", __func__);
ret = -EINVAL;
}
@@ -1030,19 +1327,19 @@ static int adev_open_input_stream(struct audio_hw_device *hw_dev,
/* Rate */
int request_config_rate = config->sample_rate;
if (config->sample_rate == 0) {
- config->sample_rate = profile_get_default_sample_rate(&in->profile);
+ config->sample_rate = profile_get_default_sample_rate(&device_info->profile);
}
if (in->adev->device_sample_rate != 0 && /* we are playing, so lock the rate if possible */
in->adev->device_sample_rate >= RATELOCK_THRESHOLD) {/* but only for high sample rates */
if (config->sample_rate != in->adev->device_sample_rate) {
- unsigned highest_rate = profile_get_highest_sample_rate(&in->profile);
+ unsigned highest_rate = profile_get_highest_sample_rate(&device_info->profile);
if (highest_rate == 0) {
ret = -EINVAL; /* error with device */
} else {
- proxy_config.rate = config->sample_rate =
+ in->config.rate = config->sample_rate =
min(highest_rate, in->adev->device_sample_rate);
- if (request_config_rate != 0 && proxy_config.rate != config->sample_rate) {
+ if (request_config_rate != 0 && in->config.rate != config->sample_rate) {
/* Changing the requested rate */
ret = -EINVAL;
} else {
@@ -1051,24 +1348,25 @@ static int adev_open_input_stream(struct audio_hw_device *hw_dev,
}
}
}
- } else if (profile_is_sample_rate_valid(&in->profile, config->sample_rate)) {
- proxy_config.rate = config->sample_rate;
+ } else if (profile_is_sample_rate_valid(&device_info->profile, config->sample_rate)) {
+ in->config.rate = config->sample_rate;
} else {
- proxy_config.rate = config->sample_rate = profile_get_default_sample_rate(&in->profile);
+ in->config.rate = config->sample_rate =
+ profile_get_default_sample_rate(&device_info->profile);
ret = -EINVAL;
}
/* Format */
if (config->format == AUDIO_FORMAT_DEFAULT) {
- proxy_config.format = profile_get_default_format(&in->profile);
- config->format = audio_format_from_pcm_format(proxy_config.format);
+ in->config.format = profile_get_default_format(&device_info->profile);
+ config->format = audio_format_from_pcm_format(in->config.format);
} else {
enum pcm_format fmt = pcm_format_from_audio_format(config->format);
- if (profile_is_format_valid(&in->profile, fmt)) {
- proxy_config.format = fmt;
+ if (profile_is_format_valid(&device_info->profile, fmt)) {
+ in->config.format = fmt;
} else {
- proxy_config.format = profile_get_default_format(&in->profile);
- config->format = audio_format_from_pcm_format(proxy_config.format);
+ in->config.format = profile_get_default_format(&device_info->profile);
+ config->format = audio_format_from_pcm_format(in->config.format);
ret = -EINVAL;
}
}
@@ -1077,7 +1375,7 @@ static int adev_open_input_stream(struct audio_hw_device *hw_dev,
bool calc_mask = false;
if (config->channel_mask == AUDIO_CHANNEL_NONE) {
/* query case */
- in->hal_channel_count = profile_get_default_channel_count(&in->profile);
+ in->hal_channel_count = profile_get_default_channel_count(&device_info->profile);
calc_mask = true;
} else {
/* explicit case */
@@ -1085,14 +1383,14 @@ static int adev_open_input_stream(struct audio_hw_device *hw_dev,
}
/* The Framework is currently limited to no more than this number of channels */
- if (in->hal_channel_count > FCC_8) {
- in->hal_channel_count = FCC_8;
+ if (in->hal_channel_count > FCC_LIMIT) {
+ in->hal_channel_count = FCC_LIMIT;
calc_mask = true;
}
if (calc_mask) {
/* need to calculate the mask from channel count either because this is the query case
- * or the specified mask isn't valid for this device, or is more then the FW can handle */
+ * or the specified mask isn't valid for this device, or is more than the FW can handle */
in->hal_channel_mask = in->hal_channel_count <= FCC_2
/* position mask for mono & stereo */
? audio_channel_in_mask_from_count(in->hal_channel_count)
@@ -1113,9 +1411,9 @@ static int adev_open_input_stream(struct audio_hw_device *hw_dev,
// Validate the "logical" channel count against support in the "actual" profile.
// if they differ, choose the "actual" number of channels *closest* to the "logical".
// and store THAT in proxy_config.channels
- proxy_config.channels =
- profile_get_closest_channel_count(&in->profile, in->hal_channel_count);
- ret = proxy_prepare(&in->proxy, &in->profile, &proxy_config);
+ in->config.channels =
+ profile_get_closest_channel_count(&device_info->profile, in->hal_channel_count);
+ ret = proxy_prepare(&device_info->proxy, &device_info->profile, &in->config);
if (ret == 0) {
in->standby = true;
@@ -1128,12 +1426,12 @@ static int adev_open_input_stream(struct audio_hw_device *hw_dev,
adev_add_stream_to_list(in->adev, &in->adev->input_stream_list, &in->list_node);
} else {
ALOGW("proxy_prepare error %d", ret);
- unsigned channel_count = proxy_get_channel_count(&in->proxy);
+ unsigned channel_count = proxy_get_channel_count(&device_info->proxy);
config->channel_mask = channel_count <= FCC_2
? audio_channel_in_mask_from_count(channel_count)
: audio_channel_mask_for_index_assignment_from_count(channel_count);
- config->format = audio_format_from_pcm_format(proxy_get_format(&in->proxy));
- config->sample_rate = proxy_get_sample_rate(&in->proxy);
+ config->format = audio_format_from_pcm_format(proxy_get_format(&device_info->proxy));
+ config->sample_rate = proxy_get_sample_rate(&device_info->proxy);
}
}
@@ -1145,6 +1443,8 @@ static int adev_open_input_stream(struct audio_hw_device *hw_dev,
return ret;
}
+ list_add_tail(&in->alsa_devices, &device_info->list_node);
+
device_lock(in->adev);
++in->adev->inputs_open;
device_unlock(in->adev);
@@ -1156,18 +1456,25 @@ static void adev_close_input_stream(struct audio_hw_device *hw_dev,
struct audio_stream_in *stream)
{
struct stream_in *in = (struct stream_in *)stream;
- ALOGV("adev_close_input_stream(c:%d d:%d)", in->profile.card, in->profile.device);
-
- adev_remove_stream_from_list(in->adev, &in->list_node);
+ stream_lock(&in->lock);
device_lock(in->adev);
+ list_remove(&in->list_node);
--in->adev->inputs_open;
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(&in->alsa_devices);
+ if (device_info != NULL) {
+ ALOGV("adev_close_input_stream(c:%d d:%d)",
+ device_info->profile.card, device_info->profile.device);
+ }
LOG_ALWAYS_FATAL_IF(in->adev->inputs_open < 0,
"invalid inputs_open: %d", in->adev->inputs_open);
+
+ stream_standby_l(&in->alsa_devices, &in->standby);
+
device_unlock(in->adev);
- /* Close the pcm device */
- in_standby(&stream->common);
+ stream_clear_devices(&in->alsa_devices);
+ stream_unlock(&in->lock);
free(in->conversion_buffer);
@@ -1221,6 +1528,282 @@ static int adev_get_mic_mute(const struct audio_hw_device *hw_dev, bool *state)
return -ENOSYS;
}
+static int adev_create_audio_patch(struct audio_hw_device *dev,
+ unsigned int num_sources,
+ const struct audio_port_config *sources,
+ unsigned int num_sinks,
+ const struct audio_port_config *sinks,
+ audio_patch_handle_t *handle) {
+ if (num_sources != 1 || num_sinks == 0 || num_sinks > AUDIO_PATCH_PORTS_MAX) {
+ // Only accept mix->device and device->mix cases. In that case, the number of sources
+ // must be 1. The number of sinks must be in the range of (0, AUDIO_PATCH_PORTS_MAX].
+ return -EINVAL;
+ }
+
+ if (sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+ // If source is a device, the number of sinks should be 1.
+ if (num_sinks != 1 || sinks[0].type != AUDIO_PORT_TYPE_MIX) {
+ return -EINVAL;
+ }
+ } else if (sources[0].type == AUDIO_PORT_TYPE_MIX) {
+ // If source is a mix, all sinks should be device.
+ for (unsigned int i = 0; i < num_sinks; i++) {
+ if (sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
+ ALOGE("%s() invalid sink type %#x for mix source", __func__, sinks[i].type);
+ return -EINVAL;
+ }
+ }
+ } else {
+ // All other cases are invalid.
+ return -EINVAL;
+ }
+
+ struct audio_device* adev = (struct audio_device*) dev;
+ bool generatedPatchHandle = false;
+ device_lock(adev);
+ if (*handle == AUDIO_PATCH_HANDLE_NONE) {
+ *handle = ++adev->next_patch_handle;
+ generatedPatchHandle = true;
+ }
+
+ int cards[AUDIO_PATCH_PORTS_MAX];
+ int devices[AUDIO_PATCH_PORTS_MAX];
+ const struct audio_port_config *port_configs =
+ sources[0].type == AUDIO_PORT_TYPE_DEVICE ? sources : sinks;
+ int num_configs = 0;
+ audio_io_handle_t io_handle = 0;
+ bool wasStandby = true;
+ int direction = PCM_OUT;
+ audio_patch_handle_t *patch_handle = NULL;
+ struct listnode *alsa_devices = NULL;
+ struct stream_lock *lock = NULL;
+ struct pcm_config *config = NULL;
+ struct stream_in *in = NULL;
+ struct stream_out *out = NULL;
+
+ unsigned int num_saved_devices = 0;
+ int saved_cards[AUDIO_PATCH_PORTS_MAX];
+ int saved_devices[AUDIO_PATCH_PORTS_MAX];
+
+ struct listnode *node;
+
+ // Only handle patches for mix->devices and device->mix case.
+ if (sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+ in = adev_get_stream_in_by_io_handle_l(adev, sinks[0].ext.mix.handle);
+ if (in == NULL) {
+ ALOGE("%s()can not find stream with handle(%d)", __func__, sinks[0].ext.mix.handle);
+ device_unlock(adev);
+ return -EINVAL;
+ }
+
+ direction = PCM_IN;
+ wasStandby = in->standby;
+ io_handle = in->handle;
+ num_configs = num_sources;
+ patch_handle = &in->patch_handle;
+ alsa_devices = &in->alsa_devices;
+ lock = &in->lock;
+ config = &in->config;
+ } else {
+ out = adev_get_stream_out_by_io_handle_l(adev, sources[0].ext.mix.handle);
+ if (out == NULL) {
+ ALOGE("%s()can not find stream with handle(%d)", __func__, sources[0].ext.mix.handle);
+ device_unlock(adev);
+ return -EINVAL;
+ }
+
+ direction = PCM_OUT;
+ wasStandby = out->standby;
+ io_handle = out->handle;
+ num_configs = num_sinks;
+ patch_handle = &out->patch_handle;
+ alsa_devices = &out->alsa_devices;
+ lock = &out->lock;
+ config = &out->config;
+ }
+
+ // Check if the patch handle match the recorded one if a valid patch handle is passed.
+ if (!generatedPatchHandle && *patch_handle != *handle) {
+ ALOGE("%s() the patch handle(%d) does not match recorded one(%d) for stream "
+ "with handle(%d) when creating audio patch",
+ __func__, *handle, *patch_handle, io_handle);
+ device_unlock(adev);
+ return -EINVAL;
+ }
+ device_unlock(adev);
+
+ for (unsigned int i = 0; i < num_configs; ++i) {
+ if (!parse_card_device_params(port_configs[i].ext.device.address, &cards[i], &devices[i])) {
+ ALOGE("%s, failed to parse card and device %s",
+ __func__, port_configs[i].ext.device.address);
+ return -EINVAL;
+ }
+ }
+
+ stream_lock(lock);
+ list_for_each (node, alsa_devices) {
+ struct alsa_device_info *device_info =
+ node_to_item(node, struct alsa_device_info, list_node);
+ saved_cards[num_saved_devices] = device_info->profile.card;
+ saved_devices[num_saved_devices++] = device_info->profile.device;
+ }
+
+ device_lock(adev);
+ stream_standby_l(alsa_devices, out == NULL ? &in->standby : &out->standby);
+ device_unlock(adev);
+
+ // Timestamps:
+ // Audio timestamps assume continuous PCM frame counts which are maintained
+ // with the device proxy.transferred variable. Technically it would be better
+ // associated with in or out stream, not the device; here we save and restore
+ // using the first alsa device as a simplification.
+ uint64_t saved_transferred_frames = 0;
+ struct alsa_device_info *device_info = stream_get_first_alsa_device(alsa_devices);
+ if (device_info != NULL) saved_transferred_frames = device_info->proxy.transferred;
+
+ int ret = stream_set_new_devices(config, alsa_devices, num_configs, cards, devices, direction);
+
+ if (ret != 0) {
+ *handle = generatedPatchHandle ? AUDIO_PATCH_HANDLE_NONE : *handle;
+ stream_set_new_devices(
+ config, alsa_devices, num_saved_devices, saved_cards, saved_devices, direction);
+ } else {
+ *patch_handle = *handle;
+ }
+
+ // Timestamps: Restore transferred frames.
+ if (saved_transferred_frames != 0) {
+ device_info = stream_get_first_alsa_device(alsa_devices);
+ if (device_info != NULL) device_info->proxy.transferred = saved_transferred_frames;
+ }
+
+ if (!wasStandby) {
+ device_lock(adev);
+ if (in != NULL) {
+ start_input_stream(in);
+ }
+ if (out != NULL) {
+ start_output_stream(out);
+ }
+ device_unlock(adev);
+ }
+ stream_unlock(lock);
+ return ret;
+}
+
+static int adev_release_audio_patch(struct audio_hw_device *dev,
+ audio_patch_handle_t patch_handle)
+{
+ struct audio_device* adev = (struct audio_device*) dev;
+
+ device_lock(adev);
+ struct stream_out *out = adev_get_stream_out_by_patch_handle_l(adev, patch_handle);
+ device_unlock(adev);
+ if (out != NULL) {
+ stream_lock(&out->lock);
+ device_lock(adev);
+ stream_standby_l(&out->alsa_devices, &out->standby);
+ device_unlock(adev);
+ out->patch_handle = AUDIO_PATCH_HANDLE_NONE;
+ stream_unlock(&out->lock);
+ return 0;
+ }
+
+ device_lock(adev);
+ struct stream_in *in = adev_get_stream_in_by_patch_handle_l(adev, patch_handle);
+ device_unlock(adev);
+ if (in != NULL) {
+ stream_lock(&in->lock);
+ device_lock(adev);
+ stream_standby_l(&in->alsa_devices, &in->standby);
+ device_unlock(adev);
+ in->patch_handle = AUDIO_PATCH_HANDLE_NONE;
+ stream_unlock(&in->lock);
+ return 0;
+ }
+
+ ALOGE("%s cannot find stream with patch handle as %d", __func__, patch_handle);
+ return -EINVAL;
+}
+
+static int adev_get_audio_port(struct audio_hw_device *dev, struct audio_port *port)
+{
+ if (port->type != AUDIO_PORT_TYPE_DEVICE) {
+ return -EINVAL;
+ }
+
+ alsa_device_profile profile;
+ const bool is_output = audio_is_output_device(port->ext.device.type);
+ profile_init(&profile, is_output ? PCM_OUT : PCM_IN);
+ if (!parse_card_device_params(port->ext.device.address, &profile.card, &profile.device)) {
+ return -EINVAL;
+ }
+
+ if (!profile_read_device_info(&profile)) {
+ return -ENOENT;
+ }
+
+ port->num_formats = 0;;
+ for (size_t i = 0; i < min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_FORMATS) &&
+ profile.formats[i] != 0; ++i) {
+ audio_format_t format = audio_format_from(profile.formats[i]);
+ if (format != AUDIO_FORMAT_INVALID) {
+ port->formats[port->num_formats++] = format;
+ }
+ }
+
+ port->num_sample_rates = populate_sample_rates_from_profile(&profile, port->sample_rates);
+ port->num_channel_masks = populate_channel_mask_from_profile(
+ &profile, is_output, port->channel_masks);
+
+ return 0;
+}
+
+static int adev_get_audio_port_v7(struct audio_hw_device *dev, struct audio_port_v7 *port)
+{
+ if (port->type != AUDIO_PORT_TYPE_DEVICE) {
+ return -EINVAL;
+ }
+
+ alsa_device_profile profile;
+ const bool is_output = audio_is_output_device(port->ext.device.type);
+ profile_init(&profile, is_output ? PCM_OUT : PCM_IN);
+ if (!parse_card_device_params(port->ext.device.address, &profile.card, &profile.device)) {
+ return -EINVAL;
+ }
+
+ if (!profile_read_device_info(&profile)) {
+ return -ENOENT;
+ }
+
+ audio_channel_mask_t channel_masks[AUDIO_PORT_MAX_CHANNEL_MASKS];
+ unsigned int num_channel_masks = populate_channel_mask_from_profile(
+ &profile, is_output, channel_masks);
+ unsigned int sample_rates[AUDIO_PORT_MAX_SAMPLING_RATES];
+ const unsigned int num_sample_rates =
+ populate_sample_rates_from_profile(&profile, sample_rates);
+ port->num_audio_profiles = 0;;
+ for (size_t i = 0; i < min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) &&
+ profile.formats[i] != 0; ++i) {
+ audio_format_t format = audio_format_from(profile.formats[i]);
+ if (format == AUDIO_FORMAT_INVALID) {
+ continue;
+ }
+ const unsigned int j = port->num_audio_profiles++;
+ port->audio_profiles[j].format = format;
+ port->audio_profiles[j].num_sample_rates = num_sample_rates;
+ memcpy(port->audio_profiles[j].sample_rates,
+ sample_rates,
+ num_sample_rates * sizeof(unsigned int));
+ port->audio_profiles[j].num_channel_masks = num_channel_masks;
+ memcpy(port->audio_profiles[j].channel_masks,
+ channel_masks,
+ num_channel_masks* sizeof(audio_channel_mask_t));
+ }
+
+ return 0;
+}
+
static int adev_dump(const struct audio_hw_device *device, int fd)
{
dprintf(fd, "\nUSB audio module:\n");
@@ -1290,7 +1873,7 @@ static int adev_open(const hw_module_t* module, const char* name, hw_device_t**
list_init(&adev->input_stream_list);
adev->hw_device.common.tag = HARDWARE_DEVICE_TAG;
- adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
+ adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_3_2;
adev->hw_device.common.module = (struct hw_module_t *)module;
adev->hw_device.common.close = adev_close;
@@ -1307,6 +1890,10 @@ static int adev_open(const hw_module_t* module, const char* name, hw_device_t**
adev->hw_device.close_output_stream = adev_close_output_stream;
adev->hw_device.open_input_stream = adev_open_input_stream;
adev->hw_device.close_input_stream = adev_close_input_stream;
+ adev->hw_device.create_audio_patch = adev_create_audio_patch;
+ adev->hw_device.release_audio_patch = adev_release_audio_patch;
+ adev->hw_device.get_audio_port = adev_get_audio_port;
+ adev->hw_device.get_audio_port_v7 = adev_get_audio_port_v7;
adev->hw_device.dump = adev_dump;
*device = &adev->hw_device.common;