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 #include #include +#include #include #include @@ -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,8 +149,80 @@ 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; + } + } - return proxy_open(&out->proxy); +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 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;