alsa-util: Perform format and rate detection before setting HW params

Perform detection of supported sample format and rates just after device is
opened, before `snd_pcm_hw_params()` is called for the first time. This fixes a
problem where device restricts available sample rates after HW params are set
preventing sample rate detection (seen with UAC2 devices and kernel 6.1.9)

Bug: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/1414
Bug: https://github.com/alsa-project/alsa-lib/issues/119
Link: aed52c507f
[Wim: Apply to acp add_pro_profile(), enable pa_alsa_supported_*()]
This commit is contained in:
Igor V. Kovalenko 2023-03-20 18:22:09 +01:00 committed by Wim Taymans
parent 79ee2fac73
commit 082680d8a2
5 changed files with 37 additions and 5 deletions

View file

@ -386,7 +386,7 @@ static int add_pro_profile(pa_card *impl, uint32_t index)
devstr, NULL, &m->sample_spec, devstr, NULL, &m->sample_spec,
&m->channel_map, SND_PCM_STREAM_PLAYBACK, &m->channel_map, SND_PCM_STREAM_PLAYBACK,
&try_period_size, &try_buffer_size, &try_period_size, &try_buffer_size,
0, NULL, NULL, false))) { 0, NULL, NULL, NULL, NULL, false))) {
pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm); pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm);
pa_proplist_setf(m->output_proplist, "clock.name", "api.alsa.%u", index); pa_proplist_setf(m->output_proplist, "clock.name", "api.alsa.%u", index);
pa_proplist_setf(m->output_proplist, "device.profile.pro", "true"); pa_proplist_setf(m->output_proplist, "device.profile.pro", "true");
@ -418,7 +418,7 @@ static int add_pro_profile(pa_card *impl, uint32_t index)
devstr, NULL, &m->sample_spec, devstr, NULL, &m->sample_spec,
&m->channel_map, SND_PCM_STREAM_CAPTURE, &m->channel_map, SND_PCM_STREAM_CAPTURE,
&try_period_size, &try_buffer_size, &try_period_size, &try_buffer_size,
0, NULL, NULL, false))) { 0, NULL, NULL, NULL, NULL, false))) {
pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm); pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm);
pa_proplist_setf(m->input_proplist, "clock.name", "api.alsa.%u", index); pa_proplist_setf(m->input_proplist, "clock.name", "api.alsa.%u", index);
pa_proplist_setf(m->input_proplist, "device.profile.pro", "true"); pa_proplist_setf(m->input_proplist, "device.profile.pro", "true");

View file

@ -4972,7 +4972,7 @@ static snd_pcm_t* mapping_open_pcm(pa_alsa_mapping *m,
handle = pa_alsa_open_by_template( handle = pa_alsa_open_by_template(
m->device_strings, dev_id, NULL, &try_ss, m->device_strings, dev_id, NULL, &try_ss,
&try_map, mode, &try_period_size, &try_map, mode, &try_period_size,
&try_buffer_size, 0, NULL, NULL, exact_channels); &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels);
if (handle && !exact_channels && m->channel_map.channels != try_map.channels) { if (handle && !exact_channels && m->channel_map.channels != try_map.channels) {
char buf[PA_CHANNEL_MAP_SNPRINT_MAX]; char buf[PA_CHANNEL_MAP_SNPRINT_MAX];
pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name, pa_log_debug("Channel map for mapping '%s' permanently changed to '%s'", m->name,

View file

@ -1958,7 +1958,7 @@ static snd_pcm_t* mapping_open_pcm(pa_alsa_ucm_config *ucm, pa_alsa_mapping *m,
try_buffer_size = ucm->default_n_fragments * try_period_size; try_buffer_size = ucm->default_n_fragments * try_period_size;
pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss, pcm = pa_alsa_open_by_device_string(m->device_strings[0], NULL, &try_ss,
&try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, exact_channels); &try_map, mode, &try_period_size, &try_buffer_size, 0, NULL, NULL, NULL, NULL, exact_channels);
if (pcm) { if (pcm) {
if (!exact_channels) if (!exact_channels)

View file

@ -505,6 +505,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
bool *use_mmap, bool *use_mmap,
bool *use_tsched, bool *use_tsched,
pa_sample_format_t **query_supported_formats,
unsigned int **query_supported_rates,
pa_alsa_profile_set *ps, pa_alsa_profile_set *ps,
pa_alsa_mapping **mapping) { pa_alsa_mapping **mapping) {
@ -543,6 +545,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
tsched_size, tsched_size,
use_mmap, use_mmap,
use_tsched, use_tsched,
query_supported_formats,
query_supported_rates,
m); m);
if (pcm_handle) { if (pcm_handle) {
@ -570,6 +574,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
tsched_size, tsched_size,
use_mmap, use_mmap,
use_tsched, use_tsched,
query_supported_formats,
query_supported_rates,
m); m);
if (pcm_handle) { if (pcm_handle) {
@ -594,6 +600,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
tsched_size, tsched_size,
use_mmap, use_mmap,
use_tsched, use_tsched,
query_supported_formats,
query_supported_rates,
false); false);
pa_xfree(d); pa_xfree(d);
@ -615,6 +623,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping(
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
bool *use_mmap, bool *use_mmap,
bool *use_tsched, bool *use_tsched,
pa_sample_format_t **query_supported_formats,
unsigned int **query_supported_rates,
pa_alsa_mapping *m) { pa_alsa_mapping *m) {
snd_pcm_t *pcm_handle; snd_pcm_t *pcm_handle;
@ -644,6 +654,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping(
tsched_size, tsched_size,
use_mmap, use_mmap,
use_tsched, use_tsched,
query_supported_formats,
query_supported_rates,
pa_channel_map_valid(&m->channel_map) /* Query the channel count if we don't know what we want */); pa_channel_map_valid(&m->channel_map) /* Query the channel count if we don't know what we want */);
if (!pcm_handle) if (!pcm_handle)
@ -681,6 +693,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
bool *use_mmap, bool *use_mmap,
bool *use_tsched, bool *use_tsched,
pa_sample_format_t **query_supported_formats,
unsigned int **query_supported_rates,
bool require_exact_channel_number) { bool require_exact_channel_number) {
int err; int err;
@ -708,6 +722,12 @@ snd_pcm_t *pa_alsa_open_by_device_string(
pa_log_info("ALSA device open '%s' %s: %p", d, pa_log_info("ALSA device open '%s' %s: %p", d,
mode == SND_PCM_STREAM_CAPTURE ? "capture" : "playback", pcm_handle); mode == SND_PCM_STREAM_CAPTURE ? "capture" : "playback", pcm_handle);
if (query_supported_formats)
*query_supported_formats = pa_alsa_get_supported_formats(pcm_handle, ss->format);
if (query_supported_rates)
*query_supported_rates = pa_alsa_get_supported_rates(pcm_handle, ss->rate);
if ((err = pa_alsa_set_hw_params( if ((err = pa_alsa_set_hw_params(
pcm_handle, pcm_handle,
ss, ss,
@ -781,6 +801,8 @@ snd_pcm_t *pa_alsa_open_by_template(
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
bool *use_mmap, bool *use_mmap,
bool *use_tsched, bool *use_tsched,
pa_sample_format_t **query_supported_formats,
unsigned int **query_supported_rates,
bool require_exact_channel_number) { bool require_exact_channel_number) {
snd_pcm_t *pcm_handle; snd_pcm_t *pcm_handle;
@ -802,6 +824,8 @@ snd_pcm_t *pa_alsa_open_by_template(
tsched_size, tsched_size,
use_mmap, use_mmap,
use_tsched, use_tsched,
query_supported_formats,
query_supported_rates,
require_exact_channel_number); require_exact_channel_number);
pa_xfree(d); pa_xfree(d);
@ -1411,6 +1435,7 @@ char *pa_alsa_get_reserve_name(const char *device) {
return pa_sprintf_malloc("Audio%i", i); return pa_sprintf_malloc("Audio%i", i);
} }
#endif
static void dump_supported_rates(unsigned int* values) static void dump_supported_rates(unsigned int* values)
{ {
@ -1559,7 +1584,6 @@ pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_form
return formats; return formats;
} }
#endif
bool pa_alsa_pcm_is_hw(snd_pcm_t *pcm) { bool pa_alsa_pcm_is_hw(snd_pcm_t *pcm) {
snd_pcm_info_t* info; snd_pcm_info_t* info;

View file

@ -64,6 +64,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
bool *use_mmap, /* modified at return */ bool *use_mmap, /* modified at return */
bool *use_tsched, /* modified at return */ bool *use_tsched, /* modified at return */
pa_sample_format_t **query_supported_formats, /* modified at return */
unsigned int **query_supported_rates, /* modified at return */
pa_alsa_profile_set *ps, pa_alsa_profile_set *ps,
pa_alsa_mapping **mapping); /* modified at return */ pa_alsa_mapping **mapping); /* modified at return */
#endif #endif
@ -80,6 +82,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping(
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
bool *use_mmap, /* modified at return */ bool *use_mmap, /* modified at return */
bool *use_tsched, /* modified at return */ bool *use_tsched, /* modified at return */
pa_sample_format_t **query_supported_formats, /* modified at return */
unsigned int **query_supported_rates, /* modified at return */
pa_alsa_mapping *mapping); pa_alsa_mapping *mapping);
/* Opens the explicit ALSA device */ /* Opens the explicit ALSA device */
@ -94,6 +98,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
bool *use_mmap, /* modified at return */ bool *use_mmap, /* modified at return */
bool *use_tsched, /* modified at return */ bool *use_tsched, /* modified at return */
pa_sample_format_t **query_supported_formats, /* modified at return */
unsigned int **query_supported_rates, /* modified at return */
bool require_exact_channel_number); bool require_exact_channel_number);
/* Opens the explicit ALSA device with a fallback list */ /* Opens the explicit ALSA device with a fallback list */
@ -109,6 +115,8 @@ snd_pcm_t *pa_alsa_open_by_template(
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
bool *use_mmap, /* modified at return */ bool *use_mmap, /* modified at return */
bool *use_tsched, /* modified at return */ bool *use_tsched, /* modified at return */
pa_sample_format_t **query_supported_formats, /* modified at return */
unsigned int **query_supported_rates, /* modified at return */
bool require_exact_channel_number); bool require_exact_channel_number);
#if 0 #if 0