From f62d75ebc600a0e56db3350cf5c972b1efc0acdc Mon Sep 17 00:00:00 2001 From: Paul McLean Date: Fri, 11 Jul 2014 15:14:19 -0700 Subject: Added enumeration of legitimate sample rates for input and output. Added device_profile to enable refactoring of query methods. Refactored query methods (in_get_parameters/out_getParameters) in terms of device_get_parameters. Freeing ALSA parameter structures (pcm_params_free) Eliminated unnecessay input & output card/device (to support other changes). Change-Id: I771cf4681a628f87ca6d6ee3f85f76ae4d7f1504 --- modules/usbaudio/audio_hw.c | 289 +++++++++++++++++++++++--------------------- 1 file changed, 154 insertions(+), 135 deletions(-) diff --git a/modules/usbaudio/audio_hw.c b/modules/usbaudio/audio_hw.c index 2085268..b2005c6 100644 --- a/modules/usbaudio/audio_hw.c +++ b/modules/usbaudio/audio_hw.c @@ -16,6 +16,7 @@ #define LOG_TAG "usb_audio_hw" /*#define LOG_NDEBUG 0*/ +/*#define LOG_PCM_PARAMS 0*/ #include #include @@ -36,6 +37,8 @@ #include +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + /* This is the default configuration to hand to The Framework on the initial * adev_open_output_stream(). Actual device attributes will be used on the subsequent * adev_open_output_stream() after the card and device number have been set in out_set_parameters() @@ -69,18 +72,22 @@ struct pcm_config default_alsa_in_config = { .stop_threshold = (IN_PERIOD_SIZE * IN_PERIOD_COUNT), }; +struct audio_device_profile { + int card; + int device; + int direction; /* PCM_OUT or PCM_IN */ +}; + struct audio_device { struct audio_hw_device hw_device; pthread_mutex_t lock; /* see note below on mutex acquisition order */ /* output */ - int out_card; - int out_device; + struct audio_device_profile out_profile; /* input */ - int in_card; - int in_device; + struct audio_device_profile in_profile; bool standby; }; @@ -93,6 +100,7 @@ struct stream_out { bool standby; struct audio_device *dev; /* hardware information */ + struct audio_device_profile * profile; void * conversion_buffer; /* any conversions are put into here * they could come from here too if @@ -116,6 +124,8 @@ struct stream_in { struct audio_device *dev; + struct audio_device_profile * profile; + struct audio_config hal_pcm_config; /* this is the format the framework thinks it's using. We may need to convert from the actual @@ -566,7 +576,7 @@ static int get_pcm_format_for_mask(struct pcm_mask* mask) { /* just return the first one */ return table_index < table_size ? pcm_format_value_map[table_index] - : AUDIO_FORMAT_INVALID; + : (int)AUDIO_FORMAT_INVALID; } bit_mask <<= 1; table_index++; @@ -576,6 +586,54 @@ static int get_pcm_format_for_mask(struct pcm_mask* mask) { return 0; // is this right? } +static bool test_out_sample_rate(struct audio_device_profile* dev_profile, unsigned rate) { + struct pcm_config local_config = cached_output_hardware_config; + local_config.rate = rate; + + bool works = false; /* let's be pessimistic */ + struct pcm * pcm = + pcm_open(dev_profile->card, dev_profile->device, dev_profile->direction, &local_config); + + if (pcm != NULL) { + works = pcm_is_ready(pcm); + pcm_close(pcm); + } + + return works; +} + +/* sort these highest -> lowest */ +static const unsigned std_sample_rates[] = + {48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000}; + +static char* enum_std_sample_rates(struct audio_device_profile* dev_profile, + unsigned min, unsigned max) +{ + char buffer[128]; + buffer[0] = '\0'; + int buffSize = ARRAY_SIZE(buffer); + + char numBuffer[32]; + + int numEntries = 0; + unsigned index; + for(index = 0; index < ARRAY_SIZE(std_sample_rates); index++) { + if (std_sample_rates[index] >= min && std_sample_rates[index] <= max && + test_out_sample_rate(dev_profile, std_sample_rates[index])) { + if (numEntries++ != 0) { + strncat(buffer, "|", buffSize); + } + snprintf(numBuffer, sizeof(numBuffer), "%u", std_sample_rates[index]); + strncat(buffer, numBuffer, buffSize); + } + } + + return strdup(buffer); +} + +/* + * Logging + */ static void log_pcm_mask(const char* mask_name, struct pcm_mask* mask) { char buff[512]; char bit_buff[32]; @@ -665,15 +723,18 @@ static unsigned int calc_min_period_size(unsigned int sample_rate) { /* * Reads and decodes configuration info from the specified ALSA card/device */ -static int read_alsa_device_config(int card, int device, int io_type, struct pcm_config * config) +static int read_alsa_device_config(struct audio_device_profile * dev_profile, + struct pcm_config * config) { - ALOGV("usb:audio_hw - read_alsa_device_config(c:%d d:%d t:0x%X)",card, device, io_type); + ALOGV("usb:audio_hw - read_alsa_device_config(c:%d d:%d t:0x%X)", + dev_profile->card, dev_profile->device, dev_profile->direction); - if (card < 0 || device < 0) { + if (dev_profile->card < 0 || dev_profile->device < 0) { return -EINVAL; } - struct pcm_params * alsa_hw_params = pcm_params_get(card, device, io_type); + struct pcm_params * alsa_hw_params = + pcm_params_get(dev_profile->card, dev_profile->device, dev_profile->direction); if (alsa_hw_params == NULL) { return -EINVAL; } @@ -681,7 +742,9 @@ static int read_alsa_device_config(int card, int device, int io_type, struct pcm /* * This Logging will be useful when testing new USB devices. */ - /* log_pcm_params(alsa_hw_params); */ +#ifdef LOG_PCM_PARAMS + log_pcm_params(alsa_hw_params); +#endif config->channels = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS); config->rate = pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE); @@ -696,6 +759,9 @@ static int read_alsa_device_config(int card, int device, int io_type, struct pcm config->period_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS); config->format = get_pcm_format_for_mask(pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT)); + + pcm_params_free(alsa_hw_params); + return 0; } @@ -772,7 +838,6 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) ALOGV("usb:audio_hw::out out_set_parameters() keys:%s", kvpairs); struct stream_out *out = (struct stream_out *)stream; - struct audio_device *adev = out->dev; struct str_parms *parms; char value[32]; int param_val; @@ -780,45 +845,41 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) int ret_value = 0; parms = str_parms_create_str(kvpairs); - pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&out->dev->lock); + pthread_mutex_lock(&out->lock); bool recache_device_params = false; param_val = str_parms_get_str(parms, "card", value, sizeof(value)); if (param_val >= 0) { - adev->out_card = atoi(value); + out->profile->card = atoi(value); recache_device_params = true; } param_val = str_parms_get_str(parms, "device", value, sizeof(value)); if (param_val >= 0) { - adev->out_device = atoi(value); + out->profile->device = atoi(value); recache_device_params = true; } - if (recache_device_params && adev->out_card >= 0 && adev->out_device >= 0) { - ret_value = read_alsa_device_config(adev->out_card, adev->out_device, PCM_OUT, - &cached_output_hardware_config); + if (recache_device_params && out->profile->card >= 0 && out->profile->device >= 0) { + ret_value = read_alsa_device_config(out->profile, &cached_output_hardware_config); output_hardware_config_is_cached = (ret_value == 0); } - pthread_mutex_unlock(&adev->lock); + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&out->dev->lock); str_parms_destroy(parms); return ret_value; } -/*TODO it seems like both out_get_parameters() and in_get_parameters() - could be written in terms of a get_device_parameters(io_type) */ - -static char * out_get_parameters(const struct audio_stream *stream, const char *keys) +static char * device_get_parameters(struct audio_device_profile * dev_profile, const char *keys) { - ALOGV("usb:audio_hw::out out_get_parameters() keys:%s", keys); - - struct stream_out *out = (struct stream_out *) stream; - struct audio_device *adev = out->dev; + ALOGV("usb:audio_hw::device_get_parameters() keys:%s", keys); - if (adev->out_card < 0 || adev->out_device < 0) + if (dev_profile->card < 0 || dev_profile->device < 0) { return strdup(""); + } unsigned min, max; @@ -830,58 +891,74 @@ static char * out_get_parameters(const struct audio_stream *stream, const char * int buffer_size = sizeof(buffer) / sizeof(buffer[0]); char* result_str = NULL; - struct pcm_params * alsa_hw_params = pcm_params_get(adev->out_card, adev->out_device, PCM_OUT); + struct pcm_params * alsa_hw_params = + pcm_params_get(dev_profile->card, dev_profile->device, dev_profile->direction); // These keys are from hardware/libhardware/include/audio.h // supported sample rates if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES)) { - // pcm_hw_params doesn't have a list of supported samples rates, just a min and a max, so - // if they are different, return a list containing those two values, otherwise just the one. min = pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE); max = pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE); - num_written = snprintf(buffer, buffer_size, "%u", min); - if (min != max) { - snprintf(buffer + num_written, buffer_size - num_written, "|%u", max); - } - str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, - buffer); + + char* rates_list = enum_std_sample_rates(dev_profile, min, max); + str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, rates_list); + free(rates_list); } // AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES // supported channel counts if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS)) { - // Similarly for output channels count - /* TODO - This is wrong, we need format strings, not numbers (another CL) */ - min = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS); - max = pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS); - num_written = snprintf(buffer, buffer_size, "%u", min); - if (min != max) { - snprintf(buffer + num_written, buffer_size - num_written, "|%u", max); - } - str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, buffer); + // TODO remove this hack when it is superceeded by proper multi-channel support + str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, + dev_profile->direction == PCM_OUT + ? "AUDIO_CHANNEL_OUT_STEREO" + : "AUDIO_CHANNEL_IN_STEREO"); } // AUDIO_PARAMETER_STREAM_SUP_CHANNELS // supported sample formats if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) { - char * format_params = - get_format_str_for_mask(pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT)); - str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_FORMATS, format_params); - free(format_params); + // TODO remove this hack when we have support for input in non PCM16 formats + if (dev_profile->direction == PCM_IN) { + str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_FORMATS, "AUDIO_FORMAT_PCM_16_BIT"); + } else { + struct pcm_mask * format_mask = pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT); + char * format_params = get_format_str_for_mask(format_mask); + str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_FORMATS, format_params); + free(format_params); + } } // AUDIO_PARAMETER_STREAM_SUP_FORMATS + pcm_params_free(alsa_hw_params); + result_str = str_parms_to_str(result); // done with these... str_parms_destroy(query); str_parms_destroy(result); - ALOGV("usb:audio_hw::out out_get_parameters() = %s", result_str); + ALOGV("usb:audio_hw::device_get_parameters = %s", result_str); return result_str; } -static uint32_t out_get_latency(const struct audio_stream_out *stream) +static char * out_get_parameters(const struct audio_stream *stream, const char *keys) { + ALOGV("usb:audio_hw::out out_get_parameters() keys:%s", keys); + struct stream_out *out = (struct stream_out *) stream; + pthread_mutex_lock(&out->dev->lock); + pthread_mutex_lock(&out->lock); + + char * params_str = device_get_parameters(out->profile, keys); + + pthread_mutex_unlock(&out->lock); + pthread_mutex_unlock(&out->dev->lock); + + return params_str; +} + +static uint32_t out_get_latency(const struct audio_stream_out *stream) +{ + // struct stream_out *out = (struct stream_out *) stream; /*TODO Do we need a term here for the USB latency (as reported in the USB descriptors)? */ uint32_t latency = (cached_output_hardware_config.period_size @@ -898,13 +975,13 @@ 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) { - struct audio_device *adev = out->dev; int return_val = 0; ALOGV("usb:audio_hw::out start_output_stream(card:%d device:%d)", - adev->out_card, adev->out_device); + out->profile->card, out->profile->device); - out->pcm = pcm_open(adev->out_card, adev->out_device, PCM_OUT, &cached_output_hardware_config); + out->pcm = pcm_open(out->profile->card, out->profile->device, PCM_OUT, + &cached_output_hardware_config); if (out->pcm == NULL) { return -ENOMEM; @@ -1041,6 +1118,9 @@ static int adev_open_output_stream(struct audio_hw_device *dev, out->dev = adev; + out->profile = &(adev->out_profile); + out->profile->direction = PCM_OUT; + if (output_hardware_config_is_cached) { config->sample_rate = cached_output_hardware_config.rate; @@ -1207,7 +1287,6 @@ static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) ALOGV("usb: audio_hw::in in_set_parameters() keys:%s", kvpairs); struct stream_in *in = (struct stream_in *)stream; - struct audio_device *adev = in->dev; struct str_parms *parms; char value[32]; int param_val; @@ -1215,112 +1294,49 @@ static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) int ret_value = 0; parms = str_parms_create_str(kvpairs); - pthread_mutex_lock(&adev->lock); + pthread_mutex_lock(&in->dev->lock); + pthread_mutex_lock(&in->lock); bool recache_device_params = false; // Card/Device param_val = str_parms_get_str(parms, "card", value, sizeof(value)); if (param_val >= 0) { - adev->in_card = atoi(value); + in->profile->card = atoi(value); recache_device_params = true; } param_val = str_parms_get_str(parms, "device", value, sizeof(value)); if (param_val >= 0) { - adev->in_device = atoi(value); + in->profile->device = atoi(value); recache_device_params = true; } - if (recache_device_params && adev->in_card >= 0 && adev->in_device >= 0) { - ret_value = read_alsa_device_config(adev->in_card, adev->in_device, - PCM_IN, &(cached_input_hardware_config)); + if (recache_device_params && in->profile->card >= 0 && in->profile->device >= 0) { + ret_value = read_alsa_device_config(in->profile, &cached_input_hardware_config); input_hardware_config_is_cached = (ret_value == 0); } - pthread_mutex_unlock(&adev->lock); + pthread_mutex_unlock(&in->lock); + pthread_mutex_unlock(&in->dev->lock); str_parms_destroy(parms); return ret_value; } -/*TODO it seems like both out_get_parameters() and in_get_parameters() - could be written in terms of a get_device_parameters(io_type) */ - static char * in_get_parameters(const struct audio_stream *stream, const char *keys) { ALOGV("usb:audio_hw::in in_get_parameters() keys:%s", keys); struct stream_in *in = (struct stream_in *)stream; - struct audio_device *adev = in->dev; - - if (adev->in_card < 0 || adev->in_device < 0) - return strdup(""); - - struct pcm_params * alsa_hw_params = pcm_params_get(adev->in_card, adev->in_device, PCM_IN); - if (alsa_hw_params == NULL) - return strdup(""); - - struct str_parms *query = str_parms_create_str(keys); - struct str_parms *result = str_parms_create(); - - int num_written = 0; - char buffer[256]; - int buffer_size = sizeof(buffer) / sizeof(buffer[0]); - char* result_str = NULL; - - unsigned min, max; - - // These keys are from hardware/libhardware/include/audio.h - // supported sample rates - if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES)) { - // pcm_hw_params doesn't have a list of supported samples rates, just a min and a max, so - // if they are different, return a list containing those two values, otherwise just the one. - min = pcm_params_get_min(alsa_hw_params, PCM_PARAM_RATE); - max = pcm_params_get_max(alsa_hw_params, PCM_PARAM_RATE); - num_written = snprintf(buffer, buffer_size, "%u", min); - if (min != max) { - snprintf(buffer + num_written, buffer_size - num_written, "|%u", max); - } - str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SAMPLING_RATE, buffer); - } // AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES - - // supported channel counts - if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS)) { - // Similarly for output channels count - // TODO This is wrong, we need format strings, not numbers (another CL) - min = pcm_params_get_min(alsa_hw_params, PCM_PARAM_CHANNELS); - max = pcm_params_get_max(alsa_hw_params, PCM_PARAM_CHANNELS); - num_written = snprintf(buffer, buffer_size, "%u", min); - if (min != max) { - snprintf(buffer + num_written, buffer_size - num_written, "|%u", max); - } - str_parms_add_str(result, AUDIO_PARAMETER_STREAM_CHANNELS, buffer); - } // AUDIO_PARAMETER_STREAM_SUP_CHANNELS - - // supported sample formats - if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) { - struct pcm_mask * format_mask = pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT); - char * format_params = get_format_str_for_mask(format_mask); - if (!mask_has_pcm_16(format_mask)) { - /* For now, always support PCM_16 and convert locally if necessary */ - char buff[256]; - snprintf(buff, sizeof(buff), "AUDIO_FORMAT_PCM_16_BIT|%s", format_params); - free(format_params); - str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_FORMATS, buff); - } else { - str_parms_add_str(result, AUDIO_PARAMETER_STREAM_SUP_FORMATS, format_params); - } - } // AUDIO_PARAMETER_STREAM_SUP_FORMATS - - result_str = str_parms_to_str(result); + pthread_mutex_lock(&in->dev->lock); + pthread_mutex_lock(&in->lock); - // done with these... - str_parms_destroy(query); - str_parms_destroy(result); + char * params_str = device_get_parameters(in->profile, keys); - ALOGV("usb:audio_hw::in in_get_parameters() = %s", result_str); + pthread_mutex_unlock(&in->lock); + pthread_mutex_unlock(&in->dev->lock); - return result_str; + return params_str; } static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) @@ -1340,13 +1356,13 @@ 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) { - struct audio_device *adev = in->dev; int return_val = 0; ALOGV("usb:audio_hw::start_input_stream(card:%d device:%d)", - adev->in_card, adev->in_device); + in->profile->card, in->profile->device); - in->pcm = pcm_open(adev->in_card, adev->in_device, PCM_IN, &cached_input_hardware_config); + in->pcm = pcm_open(in->profile->card, in->profile->device, PCM_IN, + &cached_input_hardware_config); if (in->pcm == NULL) { ALOGE("usb:audio_hw pcm_open() in->pcm == NULL"); return -ENOMEM; @@ -1499,6 +1515,9 @@ static int adev_open_input_stream(struct audio_hw_device *dev, in->dev = (struct audio_device *)dev; + in->profile = &(in->dev->in_profile); + in->profile->direction = PCM_IN; + if (!input_hardware_config_is_cached) { // just return defaults until we can actually query the device. cached_input_hardware_config = default_alsa_in_config; -- cgit v1.1 From e83d10c1fc61ac7921d58d8818a9ad4926a207ad Mon Sep 17 00:00:00 2001 From: Dan Stoza Date: Tue, 6 May 2014 15:56:59 -0700 Subject: hwcomposer: Allow display configuration selection Adds two functions to the HWC interface: one for querying the current display configuration, and one for setting a new configuration. This will enable DisplayManager to change the mode on demand. Bug: 14320401 Change-Id: I556c509192b877b38b2103a78f937b3687f35546 --- include/hardware/hwcomposer.h | 65 +++++++++++++++++++++++++++++++++------- tests/hardware/struct-offset.cpp | 4 ++- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/include/hardware/hwcomposer.h b/include/hardware/hwcomposer.h index f647ab3..882bade 100644 --- a/include/hardware/hwcomposer.h +++ b/include/hardware/hwcomposer.h @@ -678,16 +678,24 @@ typedef struct hwc_composer_device_1 { * total number of configurations available for the display is returned in * *numConfigs. If *numConfigs is zero on entry, then configs may be NULL. * - * HWC_DEVICE_API_VERSION_1_1 does not provide a way to choose a config. - * For displays that support multiple configurations, the h/w composer - * implementation should choose one and report it as the first config in - * the list. Reporting the not-chosen configs is not required. - * - * Returns 0 on success or -errno on error. If disp is a hotpluggable - * display type and no display is connected, an error should be returned. + * Hardware composers implementing HWC_DEVICE_API_VERSION_1_3 or prior + * shall choose one configuration to activate and report it as the first + * entry in the returned list. Reporting the inactive configurations is not + * required. + * + * HWC_DEVICE_API_VERSION_1_4 and later provide configuration management + * through SurfaceFlinger, and hardware composers implementing these APIs + * must also provide getActiveConfig and setActiveConfig. Hardware composers + * implementing these API versions may choose not to activate any + * configuration, leaving configuration selection to higher levels of the + * framework. + * + * Returns 0 on success or a negative error code on error. If disp is a + * hotpluggable display type and no display is connected, an error shall be + * returned. * * This field is REQUIRED for HWC_DEVICE_API_VERSION_1_1 and later. - * It should be NULL for previous versions. + * It shall be NULL for previous versions. */ int (*getDisplayConfigs)(struct hwc_composer_device_1* dev, int disp, uint32_t* configs, size_t* numConfigs); @@ -704,19 +712,54 @@ typedef struct hwc_composer_device_1 { * array will have one less value than the attributes array. * * This field is REQUIRED for HWC_DEVICE_API_VERSION_1_1 and later. - * It should be NULL for previous versions. + * It shall be NULL for previous versions. * * If disp is a hotpluggable display type and no display is connected, * or if config is not a valid configuration for the display, a negative - * value should be returned. + * error code shall be returned. */ int (*getDisplayAttributes)(struct hwc_composer_device_1* dev, int disp, uint32_t config, const uint32_t* attributes, int32_t* values); /* + * (*getActiveConfig)() returns the index of the configuration that is + * currently active on the connected display. The index is relative to + * the list of configuration handles returned by getDisplayConfigs. If there + * is no active configuration, -1 shall be returned. + * + * Returns the configuration index on success or -1 on error. + * + * This field is REQUIRED for HWC_DEVICE_API_VERSION_1_4 and later. + * It shall be NULL for previous versions. + */ + int (*getActiveConfig)(struct hwc_composer_device_1* dev, int disp); + + /* + * (*setActiveConfig)() instructs the hardware composer to switch to the + * display configuration at the given index in the list of configuration + * handles returned by getDisplayConfigs. + * + * If this function returns without error, any subsequent calls to + * getActiveConfig shall return the index set by this function until one + * of the following occurs: + * 1) Another successful call of this function + * 2) The display is disconnected + * + * Returns 0 on success or a negative error code on error. If disp is a + * hotpluggable display type and no display is connected, or if index is + * outside of the range of hardware configurations returned by + * getDisplayConfigs, an error shall be returned. + * + * This field is REQUIRED for HWC_DEVICE_API_VERSION_1_4 and later. + * It shall be NULL for previous versions. + */ + int (*setActiveConfig)(struct hwc_composer_device_1* dev, int disp, + int index); + + /* * Reserved for future use. Must be NULL. */ - void* reserved_proc[4]; + void* reserved_proc[2]; } hwc_composer_device_1_t; diff --git a/tests/hardware/struct-offset.cpp b/tests/hardware/struct-offset.cpp index 8e5aa40..2354408 100644 --- a/tests/hardware/struct-offset.cpp +++ b/tests/hardware/struct-offset.cpp @@ -167,7 +167,9 @@ void CheckOffsets(void) { CHECK_MEMBER_AT(hwc_composer_device_1_t, dump, 88, 168); CHECK_MEMBER_AT(hwc_composer_device_1_t, getDisplayConfigs, 92, 176); CHECK_MEMBER_AT(hwc_composer_device_1_t, getDisplayAttributes, 96, 184); - CHECK_MEMBER_AT(hwc_composer_device_1_t, reserved_proc, 100, 192); + CHECK_MEMBER_AT(hwc_composer_device_1_t, getActiveConfig, 100, 192); + CHECK_MEMBER_AT(hwc_composer_device_1_t, setActiveConfig, 104, 200); + CHECK_MEMBER_AT(hwc_composer_device_1_t, reserved_proc, 108, 208); //Types defined in gralloc.h CHECK_MEMBER_AT(gralloc_module_t, common, 0, 0); -- cgit v1.1 From 497abe3d9983bd0d1122b99a29a4c290c48245e6 Mon Sep 17 00:00:00 2001 From: Paul McLean Date: Tue, 15 Jul 2014 12:35:06 -0700 Subject: Added enumeration of legitimate sample rates for input and output. Change-Id: Ib53ec5254bd47761dbce84646a841eb9b32fd62a --- modules/usbaudio/audio_hw.c | 254 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 215 insertions(+), 39 deletions(-) diff --git a/modules/usbaudio/audio_hw.c b/modules/usbaudio/audio_hw.c index b2005c6..8945791 100644 --- a/modules/usbaudio/audio_hw.c +++ b/modules/usbaudio/audio_hw.c @@ -231,7 +231,7 @@ static size_t convert_32_to_16(const int32_t * in_buff, size_t num_in_samples, s * in_buff_channels Specifies the number of channels in the input buffer. * out_buff points to the buffer to receive converted PCM16 samples. * out_buff_channels Specifies the number of channels in the output buffer. - * num_in_samples size of input buffer in SAMPLES + * num_in_bytes size of input buffer in BYTES * returns * the number of BYTES of output data. * NOTE @@ -241,16 +241,18 @@ static size_t convert_32_to_16(const int32_t * in_buff, size_t num_in_samples, s * support 4-channel devices. * TODO Move this to a utilities module. */ -static size_t expand_channels_16(const short* in_buff, int in_buff_chans, - short* out_buff, int out_buff_chans, - size_t num_in_samples) +static size_t expand_channels_16(const int16_t* in_buff, int in_buff_chans, + int16_t* out_buff, int out_buff_chans, + size_t num_in_bytes) { /* * Move from back to front so that the conversion can be done in-place * i.e. in_buff == out_buff * NOTE: num_in_samples * out_buff_channels must be an even multiple of in_buff_chans */ - int num_out_samples = (num_in_samples * out_buff_chans)/in_buff_chans; + size_t num_in_samples = num_in_bytes / sizeof(int16_t); + + size_t num_out_samples = (num_in_samples * out_buff_chans) / in_buff_chans; short* dst_ptr = out_buff + num_out_samples - 1; size_t src_index; @@ -267,7 +269,7 @@ static size_t expand_channels_16(const short* in_buff, int in_buff_chans, } /* return number of *bytes* generated */ - return num_out_samples * sizeof(short); + return num_out_samples * sizeof(int16_t); } /* @@ -277,7 +279,7 @@ static size_t expand_channels_16(const short* in_buff, int in_buff_chans, * in_buff_channels Specifies the number of channels in the input buffer. * out_buff points to the buffer to receive converted PCM16 samples. * out_buff_channels Specifies the number of channels in the output buffer. - * num_in_samples size of input buffer in SAMPLES + * num_in_bytes size of input buffer in BYTES * returns * the number of BYTES of output data. * NOTE @@ -287,24 +289,26 @@ static size_t expand_channels_16(const short* in_buff, int in_buff_chans, * support 4-channel devices. * TODO Move this to a utilities module. */ -static size_t contract_channels_16(short* in_buff, int in_buff_chans, - short* out_buff, int out_buff_chans, - size_t num_in_samples) +static size_t contract_channels_16(const int16_t* in_buff, size_t in_buff_chans, + int16_t* out_buff, size_t out_buff_chans, + size_t num_in_bytes) { /* * Move from front to back so that the conversion can be done in-place * i.e. in_buff == out_buff * NOTE: num_in_samples * out_buff_channels must be an even multiple of in_buff_chans */ - int num_out_samples = (num_in_samples * out_buff_chans)/in_buff_chans; + size_t num_in_samples = num_in_bytes / sizeof(int16_t); + + size_t num_out_samples = (num_in_samples * out_buff_chans) / in_buff_chans; - int num_skip_samples = in_buff_chans - out_buff_chans; + size_t num_skip_samples = in_buff_chans - out_buff_chans; - short* dst_ptr = out_buff; - short* src_ptr = in_buff; + int16_t* dst_ptr = out_buff; + const int16_t* src_ptr = in_buff; size_t src_index; for (src_index = 0; src_index < num_in_samples; src_index += in_buff_chans) { - int dst_offset; + size_t dst_offset; for (dst_offset = 0; dst_offset < out_buff_chans; dst_offset++) { *dst_ptr++ = *src_ptr++; } @@ -312,7 +316,169 @@ static size_t contract_channels_16(short* in_buff, int in_buff_chans, } /* return number of *bytes* generated */ - return num_out_samples * sizeof(short); + return num_out_samples * sizeof(int16_t); +} + +/* + * Convert a buffer of N-channel, interleaved PCM32 samples to M-channel PCM32 channels + * (where N < M). + * in_buff points to the buffer of PCM32 samples + * in_buff_channels Specifies the number of channels in the input buffer. + * out_buff points to the buffer to receive converted PCM32 samples. + * out_buff_channels Specifies the number of channels in the output buffer. + * num_in_bytes size of input buffer in BYTES + * returns + * the number of BYTES of output data. + * NOTE + * channels > N are filled with silence. + * This conversion is safe to do in-place (in_buff == out_buff) + * We are doing this since we *always* present to The Framework as STEREO device, but need to + * support 4-channel devices. + * TODO Move this to a utilities module. + */ +static size_t expand_channels_32(const int32_t* in_buff, size_t in_buff_chans, + int32_t* out_buff, size_t out_buff_chans, + size_t num_in_bytes) +{ + /* + * Move from back to front so that the conversion can be done in-place + * i.e. in_buff == out_buff + * NOTE: num_in_samples * out_buff_channels must be an even multiple of in_buff_chans + */ + size_t num_in_samples = num_in_bytes / sizeof(int32_t); + + size_t num_out_samples = (num_in_samples * out_buff_chans) / in_buff_chans; + + int32_t* dst_ptr = out_buff + num_out_samples - 1; + const int32_t* src_ptr = in_buff + num_in_samples - 1; + size_t num_zero_chans = out_buff_chans - in_buff_chans; + size_t src_index; + for (src_index = 0; src_index < num_in_samples; src_index += in_buff_chans) { + size_t dst_offset; + for (dst_offset = 0; dst_offset < num_zero_chans; dst_offset++) { + *dst_ptr-- = 0; + } + for (; dst_offset < out_buff_chans; dst_offset++) { + *dst_ptr-- = *src_ptr--; + } + } + + /* return number of *bytes* generated */ + return num_out_samples * sizeof(int32_t); +} + +/* + * Convert a buffer of N-channel, interleaved PCM32 samples to M-channel PCM16 channels + * (where N > M). + * in_buff points to the buffer of PCM32 samples + * in_buff_channels Specifies the number of channels in the input buffer. + * out_buff points to the buffer to receive converted PCM16 samples. + * out_buff_channels Specifies the number of channels in the output buffer. + * num_in_bytes size of input buffer in BYTES + * returns + * the number of BYTES of output data. + * NOTE + * channels > N are thrown away. + * This conversion is safe to do in-place (in_buff == out_buff) + * We are doing this since we *always* present to The Framework as STEREO device, but need to + * support 4-channel devices. + * TODO Move this to a utilities module. + */ +static size_t contract_channels_32(const int32_t* in_buff, size_t in_buff_chans, + int32_t* out_buff, size_t out_buff_chans, + size_t num_in_bytes) +{ + /* + * Move from front to back so that the conversion can be done in-place + * i.e. in_buff == out_buff + * NOTE: num_in_samples * out_buff_channels must be an even multiple of in_buff_chans + */ + size_t num_in_samples = num_in_bytes / sizeof(int32_t); + + size_t num_out_samples = (num_in_samples * out_buff_chans) / in_buff_chans; + + size_t num_skip_samples = in_buff_chans - out_buff_chans; + + int32_t* dst_ptr = out_buff; + const int32_t* src_ptr = in_buff; + size_t src_index; + for (src_index = 0; src_index < num_in_samples; src_index += in_buff_chans) { + size_t dst_offset; + for (dst_offset = 0; dst_offset < out_buff_chans; dst_offset++) { + *dst_ptr++ = *src_ptr++; + } + src_ptr += num_skip_samples; + } + + /* return number of *bytes* generated */ + return num_out_samples * sizeof(int32_t); +} + +static size_t contract_channels(const void* in_buff, size_t in_buff_chans, + void* out_buff, size_t out_buff_chans, + unsigned sample_size_in_bytes, size_t num_in_bytes) +{ + switch (sample_size_in_bytes) { + case 2: + return contract_channels_16((const int16_t*)in_buff, in_buff_chans, + (int16_t*)out_buff, out_buff_chans, + num_in_bytes); + + /* TODO - do this conversion when we have a device to test it with */ + case 3: + ALOGE("24-bit channel contraction not supported."); + return 0; + + case 4: + return contract_channels_32((const int32_t*)in_buff, in_buff_chans, + (int32_t*)out_buff, out_buff_chans, + num_in_bytes); + + default: + return 0; + } +} + +static size_t expand_channels(const void* in_buff, size_t in_buff_chans, + void* out_buff, size_t out_buff_chans, + unsigned sample_size_in_bytes, size_t num_in_bytes) +{ + switch (sample_size_in_bytes) { + case 2: + return expand_channels_16((const int16_t*)in_buff, in_buff_chans, + (int16_t*)out_buff, out_buff_chans, + num_in_bytes); + + /* TODO - do this conversion when we have a device to test it with */ + case 3: + ALOGE("24-bit channel expansion not supported."); + return 0; + + case 4: + return expand_channels_32((const int32_t*)in_buff, in_buff_chans, + (int32_t*)out_buff, out_buff_chans, + num_in_bytes); + + default: + return 0; + } +} + +static size_t adjust_channels(const void* in_buff, size_t in_buff_chans, + void* out_buff, size_t out_buff_chans, + unsigned sample_size_in_bytes, size_t num_in_bytes) +{ + if (out_buff_chans > in_buff_chans) { + return expand_channels(in_buff, in_buff_chans, out_buff, out_buff_chans, + sample_size_in_bytes, num_in_bytes); + } else if (out_buff_chans < in_buff_chans) { + return contract_channels(in_buff, in_buff_chans, out_buff, out_buff_chans, + sample_size_in_bytes, num_in_bytes); + } else if (in_buff != out_buff) { + memcpy(out_buff, in_buff, num_in_bytes); + } + + return num_in_bytes; } /* @@ -477,7 +643,7 @@ static bool mask_has_pcm_16(struct pcm_mask* mask) { return (mask->bits[0] & 0x0004) != 0; } -static int get_format_for_mask(struct pcm_mask* mask) +static audio_format_t get_format_for_mask(struct pcm_mask* mask) { int num_slots = sizeof(mask->bits)/ sizeof(mask->bits[0]); int bits_per_slot = sizeof(mask->bits[0]) * 8; @@ -560,6 +726,10 @@ static int const pcm_format_value_map[] = { 0 /* 49 - SNDRV_PCM_FORMAT_DSD_U16_LE */ }; +/* + * Scans the provided format mask and returns the first non-8 bit sample + * format supported by the devices. + */ static int get_pcm_format_for_mask(struct pcm_mask* mask) { int num_slots = sizeof(mask->bits)/ sizeof(mask->bits[0]); int bits_per_slot = sizeof(mask->bits[0]) * 8; @@ -569,21 +739,21 @@ static int get_pcm_format_for_mask(struct pcm_mask* mask) { int slot_index, bit_index, table_index; table_index = 0; int num_written = 0; - for (slot_index = 0; slot_index < num_slots; slot_index++) { + for (slot_index = 0; slot_index < num_slots && table_index < table_size; slot_index++) { unsigned bit_mask = 1; - for (bit_index = 0; bit_index < bits_per_slot; bit_index++) { + for (bit_index = 0; bit_index < bits_per_slot && table_index < table_size; bit_index++) { if ((mask->bits[slot_index] & bit_mask) != 0) { - /* just return the first one */ - return table_index < table_size - ? pcm_format_value_map[table_index] - : (int)AUDIO_FORMAT_INVALID; + if (table_index != 0) { /* Don't pick 8-bit */ + /* just return the first one */ + return pcm_format_value_map[table_index]; + } } bit_mask <<= 1; table_index++; } } - return 0; // is this right? + return -1; /* error */ } static bool test_out_sample_rate(struct audio_device_profile* dev_profile, unsigned rate) { @@ -739,6 +909,8 @@ static int read_alsa_device_config(struct audio_device_profile * dev_profile, return -EINVAL; } + int ret = 0; + /* * This Logging will be useful when testing new USB devices. */ @@ -758,11 +930,15 @@ static int read_alsa_device_config(struct audio_device_profile * dev_profile, } config->period_count = pcm_params_get_min(alsa_hw_params, PCM_PARAM_PERIODS); - config->format = get_pcm_format_for_mask(pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT)); + int format = get_pcm_format_for_mask(pcm_params_get_mask(alsa_hw_params, PCM_PARAM_FORMAT)); + if (format == -1) { + ret = -EINVAL; + } else { + config->format = format; + } pcm_params_free(alsa_hw_params); - - return 0; + return ret; } /* @@ -1032,10 +1208,12 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, si int num_device_channels = cached_output_hardware_config.channels; int num_req_channels = 2; /* always, for now */ if (num_device_channels != num_req_channels) { + audio_format_t audio_format = out_get_format(&(out->stream.common)); + unsigned sample_size_in_bytes = audio_bytes_per_sample(audio_format); num_write_buff_bytes = - expand_channels_16(write_buff, num_req_channels, - out->conversion_buffer, num_device_channels, - num_write_buff_bytes / sizeof(short)); + 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; } @@ -1452,16 +1630,14 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t byte if (num_device_channels != num_req_channels) { out_buff = buffer; /* Num Channels conversion */ - if (num_device_channels < num_req_channels) { - num_read_buff_bytes = - expand_channels_16(read_buff, num_device_channels, - out_buff, num_req_channels, - num_read_buff_bytes / sizeof(short)); - } else { + if (num_device_channels != num_req_channels) { + audio_format_t audio_format = in_get_format(&(in->stream.common)); + unsigned sample_size_in_bytes = audio_bytes_per_sample(audio_format); + num_read_buff_bytes = - contract_channels_16(read_buff, num_device_channels, - out_buff, num_req_channels, - num_read_buff_bytes / sizeof(short)); + adjust_channels(read_buff, num_device_channels, + out_buff, num_req_channels, + sample_size_in_bytes, num_read_buff_bytes); } } } -- cgit v1.1