Merge "audio HAL: new track metadata version"
diff --git a/modules/usbaudio/audio_hal.c b/modules/usbaudio/audio_hal.c
index 14ef993..cdcf254 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 @@
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 {
@@ -109,6 +112,12 @@
* 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 {
@@ -140,8 +149,55 @@
* 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[] = {
+ AUDIO_CHANNEL_NONE, /* 0 */
+ AUDIO_CHANNEL_OUT_MONO, /* 1 */
+ AUDIO_CHANNEL_OUT_STEREO, /* 2 */
+ AUDIO_CHANNEL_OUT_2POINT1, /* 3 */
+ AUDIO_CHANNEL_OUT_QUAD, /* 4 */
+ AUDIO_CHANNEL_OUT_PENTA, /* 5 */
+ AUDIO_CHANNEL_OUT_5POINT1, /* 6 */
+ AUDIO_CHANNEL_OUT_6POINT1, /* 7 */
+ AUDIO_CHANNEL_OUT_7POINT1 /* 8 */
+ /* channel counts greater than this are not considered */
+};
+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[] = {
+ AUDIO_CHANNEL_NONE, /* 0 */
+ AUDIO_CHANNEL_INDEX_MASK_1, /* 1 */
+ AUDIO_CHANNEL_INDEX_MASK_2, /* 2 */
+ AUDIO_CHANNEL_INDEX_MASK_3, /* 3 */
+ AUDIO_CHANNEL_INDEX_MASK_4, /* 4 */
+ AUDIO_CHANNEL_INDEX_MASK_5, /* 5 */
+ AUDIO_CHANNEL_INDEX_MASK_6, /* 6 */
+ AUDIO_CHANNEL_INDEX_MASK_7, /* 7 */
+ AUDIO_CHANNEL_INDEX_MASK_8 /* 8 */
+ /* channel counts greater than this are not considered */
+};
+static const int CHANNEL_INDEX_MASKS_SIZE = AUDIO_ARRAY_SIZE(CHANNEL_INDEX_MASKS_MAP);
+
+
+
/*
* Locking Helpers
*/
@@ -158,6 +214,9 @@
}
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 +250,52 @@
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 +376,24 @@
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;
+ }
+}
+
/*
* HAl Functions
*/
@@ -293,6 +409,73 @@
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;
@@ -371,15 +554,9 @@
struct stream_out *out = (struct stream_out *)stream;
stream_lock(&out->lock);
- if (!out->standby) {
- 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);
- proxy_close(&device_info->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;
}
@@ -394,43 +571,14 @@
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);
- struct alsa_device_info* device_info = stream_get_first_alsa_device(&out->alsa_devices);
- if (device_info != NULL && !profile_is_cached_for(&device_info->profile, card, device)) {
- /* cannot read pcm device info if playback is active */
- if (!out->standby)
- ret_value = -ENOSYS;
- else {
- int saved_card = device_info->profile.card;
- int saved_device = device_info->profile.device;
- device_info->profile.card = card;
- device_info->profile.device = device;
- ret_value = profile_read_device_info(&device_info->profile) ? 0 : -EINVAL;
- if (ret_value != 0) {
- device_info->profile.card = saved_card;
- device_info->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)
@@ -624,6 +772,8 @@
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;
@@ -709,6 +859,7 @@
proxy_config.channels =
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);
@@ -735,31 +886,21 @@
{
struct stream_out *out = (struct stream_out *)stream;
+ 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;
- struct listnode *node, *temp;
- struct alsa_device_info *item = NULL;
- stream_lock(&out->lock);
- list_for_each_safe (node, temp, &out->alsa_devices) {
- item = node_to_item(node, struct alsa_device_info, list_node);
- if (item != NULL) {
- list_remove(&item->list_node);
- free(item);
- }
- }
- stream_unlock(&out->lock);
-
- 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);
}
@@ -835,17 +976,10 @@
struct stream_in *in = (struct stream_in *)stream;
stream_lock(&in->lock);
- if (!in->standby) {
- struct listnode *node;
- list_for_each (node, &in->alsa_devices) {
- struct alsa_device_info *device_info =
- node_to_item(node, struct alsa_device_info, list_node);
- proxy_close(&device_info->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;
}
@@ -863,45 +997,10 @@
{
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);
-
- struct alsa_device_info* device_info = stream_get_first_alsa_device(&in->alsa_devices);
-
- if (card >= 0 && device >= 0 &&
- device_info != NULL && !profile_is_cached_for(&device_info->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 = device_info->profile.card;
- int saved_device = device_info->profile.device;
- device_info->profile.card = card;
- device_info->profile.device = device;
- ret_value = profile_read_device_info(&device_info->profile) ? 0 : -EINVAL;
- if (ret_value != 0) {
- ALOGE("Can't read device profile. card:%d, device:%d", card, device);
- device_info->profile.card = saved_card;
- device_info->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)
@@ -1119,6 +1218,8 @@
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;
@@ -1128,8 +1229,7 @@
(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);
@@ -1171,9 +1271,9 @@
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 {
@@ -1183,24 +1283,24 @@
}
}
} else if (profile_is_sample_rate_valid(&device_info->profile, config->sample_rate)) {
- proxy_config.rate = config->sample_rate;
+ in->config.rate = config->sample_rate;
} else {
- proxy_config.rate = config->sample_rate =
+ 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(&device_info->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(&device_info->profile, fmt)) {
- proxy_config.format = fmt;
+ in->config.format = fmt;
} else {
- proxy_config.format = profile_get_default_format(&device_info->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;
}
}
@@ -1245,9 +1345,9 @@
// 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 =
+ in->config.channels =
profile_get_closest_channel_count(&device_info->profile, in->hal_channel_count);
- ret = proxy_prepare(&device_info->proxy, &device_info->profile, &proxy_config);
+ ret = proxy_prepare(&device_info->proxy, &device_info->profile, &in->config);
if (ret == 0) {
in->standby = true;
@@ -1291,9 +1391,9 @@
{
struct stream_in *in = (struct stream_in *)stream;
- 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) {
@@ -1302,10 +1402,13 @@
}
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);
@@ -1359,6 +1462,240 @@
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);
+
+ 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;
+ }
+ 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 = min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES);
+ memcpy(port->sample_rates, profile.sample_rates, port->num_sample_rates * sizeof(unsigned int));
+
+ port->num_channel_masks = 0;
+ const audio_channel_mask_t* channel_masks_map =
+ is_output ? OUT_CHANNEL_MASKS_MAP : IN_CHANNEL_MASKS_MAP;
+ const int channel_masks_size = is_output ? OUT_CHANNEL_MASKS_SIZE : IN_CHANNEL_MASKS_SIZE;
+ 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 &&
+ port->num_channel_masks < AUDIO_PORT_MAX_CHANNEL_MASKS; ++i) {
+ if (channel_count < channel_masks_size &&
+ channel_masks_map[channel_count] != AUDIO_CHANNEL_NONE) {
+ port->channel_masks[port->num_channel_masks++] = channel_masks_map[channel_count];
+ if (port->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) {
+ port->channel_masks[port->num_channel_masks++] = CHANNEL_INDEX_MASKS_MAP[channel_count];
+ }
+ }
+ return 0;
+}
+
static int adev_dump(const struct audio_hw_device *device, int fd)
{
dprintf(fd, "\nUSB audio module:\n");
@@ -1428,7 +1765,7 @@
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_0;
adev->hw_device.common.module = (struct hw_module_t *)module;
adev->hw_device.common.close = adev_close;
@@ -1445,6 +1782,9 @@
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.dump = adev_dump;
*device = &adev->hw_device.common;