acp: update to latest

This commit is contained in:
Wim Taymans 2021-01-05 10:08:31 +01:00
parent 962bf4d897
commit 62065ac263
13 changed files with 238 additions and 84 deletions

View file

@ -630,6 +630,7 @@ static void init_jacks(pa_card *impl)
void *state;
pa_alsa_path* path;
pa_alsa_jack* jack;
char buf[64];
impl->jacks = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
@ -673,9 +674,10 @@ static void init_jacks(pa_card *impl)
}
pa_alsa_mixer_use_for_poll(impl->ucm.mixers, jack->mixer_handle);
jack->melem = pa_alsa_mixer_find_card(jack->mixer_handle, jack->alsa_name, 0);
jack->melem = pa_alsa_mixer_find_card(jack->mixer_handle, &jack->alsa_id, 0);
if (!jack->melem) {
pa_log_warn("Jack '%s' seems to have disappeared.", jack->alsa_name);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &jack->alsa_id);
pa_log_warn("Jack '%s' seems to have disappeared.", buf);
pa_alsa_jack_set_has_control(jack, false);
continue;
}

View file

@ -99,7 +99,7 @@ struct description2_map {
pa_device_port_type_t type;
};
static char *alsa_id_str(char *dst, size_t dst_len, pa_alsa_mixer_id *id) {
char *pa_alsa_mixer_id_to_string(char *dst, size_t dst_len, pa_alsa_mixer_id *id) {
if (id->index > 0) {
snprintf(dst, dst_len, "'%s',%d", id->name, id->index);
} else {
@ -139,7 +139,7 @@ static int alsa_id_decode(const char *src, char *name, int *index) {
return 0;
}
pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name) {
pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name, int index) {
pa_alsa_jack *jack;
pa_assert(name);
@ -148,7 +148,8 @@ pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name
jack->path = path;
jack->mixer_device_name = pa_xstrdup(mixer_device_name);
jack->name = pa_xstrdup(name);
jack->alsa_name = pa_sprintf_malloc("%s Jack", name);
jack->alsa_id.name = pa_sprintf_malloc("%s Jack", name);
jack->alsa_id.index = index;
jack->state_unplugged = PA_AVAILABLE_NO;
jack->state_plugged = PA_AVAILABLE_YES;
jack->ucm_devices = pa_dynarray_new(NULL);
@ -163,7 +164,7 @@ void pa_alsa_jack_free(pa_alsa_jack *jack) {
pa_dynarray_free(jack->ucm_hw_mute_devices);
pa_dynarray_free(jack->ucm_devices);
pa_xfree(jack->alsa_name);
pa_xfree(jack->alsa_id.name);
pa_xfree(jack->name);
pa_xfree(jack->mixer_device_name);
pa_xfree(jack);
@ -690,6 +691,20 @@ static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_M
[PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN
};
static snd_mixer_selem_channel_id_t alsa_channel_positions[POSITION_MASK_CHANNELS] = {
SND_MIXER_SCHN_FRONT_LEFT,
SND_MIXER_SCHN_FRONT_RIGHT,
SND_MIXER_SCHN_REAR_LEFT,
SND_MIXER_SCHN_REAR_RIGHT,
SND_MIXER_SCHN_FRONT_CENTER,
SND_MIXER_SCHN_WOOFER,
SND_MIXER_SCHN_SIDE_LEFT,
SND_MIXER_SCHN_SIDE_RIGHT,
#if POSITION_MASK_CHANNELS > 8
#error "Extend alsa_channel_positions[] array (9+)"
#endif
};
static void setting_free(pa_alsa_setting *s) {
pa_assert(s);
@ -822,7 +837,7 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@ -848,14 +863,14 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
if (value < e->db_fix->min_step) {
value = e->db_fix->min_step;
snd_mixer_selem_set_playback_volume(me, c, value);
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. "
"Volume reset to %0.2f dB.", buf, c,
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
} else if (value > e->db_fix->max_step) {
value = e->db_fix->max_step;
snd_mixer_selem_set_playback_volume(me, c, value);
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. "
"Volume reset to %0.2f dB.", buf, c,
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
@ -878,14 +893,14 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
if (value < e->db_fix->min_step) {
value = e->db_fix->min_step;
snd_mixer_selem_set_capture_volume(me, c, value);
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. "
"Volume reset to %0.2f dB.", buf, c,
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
} else if (value > e->db_fix->max_step) {
value = e->db_fix->max_step;
snd_mixer_selem_set_capture_volume(me, c, value);
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. "
"Volume reset to %0.2f dB.", buf, c,
e->db_fix->db_values[value - e->db_fix->min_step] / 100.0);
@ -993,7 +1008,7 @@ static int element_get_switch(pa_alsa_element *e, snd_mixer_t *m, bool *b) {
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@ -1159,7 +1174,7 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@ -1351,7 +1366,7 @@ static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, bool b) {
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@ -1362,7 +1377,7 @@ static int element_set_switch(pa_alsa_element *e, snd_mixer_t *m, bool b) {
r = snd_mixer_selem_set_capture_switch_all(me, b);
if (r < 0) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to set switch of %s: %s", buf, pa_alsa_strerror(errno));
}
@ -1406,7 +1421,7 @@ static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@ -1451,7 +1466,7 @@ static int element_set_constant_volume(pa_alsa_element *e, snd_mixer_t *m) {
}
if (r < 0) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to set volume of %s: %s", buf, pa_alsa_strerror(errno));
}
@ -1657,19 +1672,19 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
if (r < 0) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to get volume range of %s: %s", buf, pa_alsa_strerror(r));
return false;
}
if (e->min_volume >= e->max_volume) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Your kernel driver is broken for element %s: it reports a volume range from %li to %li which makes no sense.",
buf, e->min_volume, e->max_volume);
return false;
}
if (e->volume_use == PA_ALSA_VOLUME_CONSTANT && (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
e->constant_volume, buf, e->min_volume, e->max_volume);
return false;
@ -1677,7 +1692,7 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
if (e->db_fix && ((e->min_volume > e->db_fix->min_step) || (e->max_volume < e->db_fix->max_step))) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
"real hardware range (%li-%li). Disabling the decibel fix.", buf,
e->db_fix->min_step, e->db_fix->max_step, e->min_volume, e->max_volume);
@ -1704,19 +1719,19 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
long max_dB_checked = 0;
if (element_ask_vol_dB(me, e->direction, e->min_volume, &min_dB_checked) < 0) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to query the dB value for %s at volume level %li", buf, e->min_volume);
return false;
}
if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB_checked) < 0) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to query the dB value for %s at volume level %li", buf, e->max_volume);
return false;
}
if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
"doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
"%0.2f dB at level %li.", buf, min_dB / 100.0, max_dB / 100.0,
@ -1739,7 +1754,7 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
if (e->volume_limit >= 0) {
if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
"%li-%li. The volume limit is ignored.",
buf, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
@ -1751,7 +1766,7 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
e->db_fix->max_step = e->max_volume;
e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
} else if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB) < 0) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to get dB value of %s: %s", buf, pa_alsa_strerror(r));
e->has_dB = false;
} else
@ -1768,7 +1783,11 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
if (is_mono) {
e->n_channels = 1;
if (!e->override_map) {
if ((e->override_map & (1 << (e->n_channels-1))) && e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] == 0) {
pa_log_warn("Override map for mono element %s is invalid, ignoring override map", e->path->name);
e->override_map &= ~(1 << (e->n_channels-1));
}
if (!(e->override_map & (1 << (e->n_channels-1)))) {
for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
continue;
@ -1792,27 +1811,28 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
}
if (e->n_channels <= 0) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Volume element %s with no channels?", buf);
return false;
} else if (e->n_channels > 2) {
} else if (e->n_channels > POSITION_MASK_CHANNELS) {
/* FIXME: In some places code like this is used:
*
* e->masks[alsa_channel_ids[p]][e->n_channels-1]
*
* The definition of e->masks is
*
* pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
* pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][POSITION_MASK_CHANNELS];
*
* Since the array size is fixed at 2, we obviously
* don't support elements with more than two
* Since the array size is fixed at POSITION_MASK_CHANNELS, we obviously
* don't support elements with more than POSITION_MASK_CHANNELS
* channels... */
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", buf, e->n_channels);
return false;
}
if (!e->override_map) {
retry:
if (!(e->override_map & (1 << (e->n_channels-1)))) {
for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
bool has_channel;
@ -1835,6 +1855,17 @@ static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
}
if (e->merged_mask == 0) {
if (!(e->override_map & (1 << (e->n_channels-1)))) {
pa_log_warn("Channel map for element %s is invalid", e->path->name);
return false;
}
pa_log_warn("Override map for element %s has empty result, ignoring override map", e->path->name);
e->override_map &= ~(1 << (e->n_channels-1));
goto retry;
}
return true;
}
@ -1944,12 +1975,12 @@ static int jack_probe(pa_alsa_jack *j, pa_alsa_mapping *mapping, snd_mixer_t *m)
}
new_name = pa_sprintf_malloc("%s,pcm=%i Jack", j->name, mapping->hw_device_index);
pa_xfree(j->alsa_name);
j->alsa_name = new_name;
pa_xfree(j->alsa_id.name);
j->alsa_id.name = new_name;
j->append_pcm_to_name = false;
}
has_control = pa_alsa_mixer_find_card(m, j->alsa_name, 0) != NULL;
has_control = pa_alsa_mixer_find_card(m, &j->alsa_id, 0) != NULL;
pa_alsa_jack_set_has_control(j, has_control);
if (j->has_control) {
@ -2012,19 +2043,26 @@ finish:
static pa_alsa_jack* jack_get(pa_alsa_path *p, const char *section) {
pa_alsa_jack *j;
char *name;
int index;
if (!pa_startswith(section, "Jack "))
return NULL;
section += 5;
if (p->last_jack && pa_streq(p->last_jack->name, section))
name = alloca(strlen(section) + 1);
if (alsa_id_decode(section, name, &index))
return NULL;
if (p->last_jack && pa_streq(p->last_jack->name, name) &&
p->last_jack->alsa_id.index == index)
return p->last_jack;
PA_LLIST_FOREACH(j, p->jacks)
if (pa_streq(j->name, section))
if (pa_streq(j->name, name) && j->alsa_id.index == index)
goto finish;
j = pa_alsa_jack_new(p, NULL, section);
j = pa_alsa_jack_new(p, NULL, name, index);
PA_LLIST_INSERT_AFTER(pa_alsa_jack, p->jacks, p->last_jack, j);
finish:
@ -2428,6 +2466,16 @@ static int element_parse_volume_limit(pa_config_parser_state *state) {
return 0;
}
static unsigned int parse_channel_position(const char *m)
{
pa_channel_position_t p;
if ((p = pa_channel_position_from_string(m)) == PA_CHANNEL_POSITION_INVALID)
return SND_MIXER_SCHN_UNKNOWN;
return alsa_channel_ids[p];
}
static pa_channel_position_mask_t parse_mask(const char *m) {
pa_channel_position_mask_t v;
@ -2465,7 +2513,9 @@ static int element_parse_override_map(pa_config_parser_state *state) {
pa_alsa_path *p;
pa_alsa_element *e;
const char *split_state = NULL;
char *s;
unsigned i = 0;
int channel_count = 0;
char *n;
pa_assert(state);
@ -2477,30 +2527,59 @@ static int element_parse_override_map(pa_config_parser_state *state) {
return -1;
}
s = strstr(state->lvalue, ".");
if (s) {
pa_atoi(s + 1, &channel_count);
if (channel_count < 1 || channel_count > POSITION_MASK_CHANNELS) {
pa_log("[%s:%u] Override map index '%s' invalid in '%s'", state->filename, state->lineno, state->lvalue, state->section);
return 0;
}
} else {
pa_log("[%s:%u] Invalid override map syntax '%s' in '%s'", state->filename, state->lineno, state->lvalue, state->section);
return -1;
}
while ((n = pa_split(state->rvalue, ",", &split_state))) {
pa_channel_position_mask_t m;
snd_mixer_selem_channel_id_t channel_position;
if (i >= (unsigned)channel_count) {
pa_log("[%s:%u] Invalid override map size (>%d) in '%s'", state->filename, state->lineno, channel_count, state->section);
return -1;
}
channel_position = alsa_channel_positions[i];
if (!*n)
m = 0;
else {
if ((m = parse_mask(n)) == 0) {
pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
s = strstr(n, ":");
if (s) {
*s = '\0';
s++;
channel_position = parse_channel_position(n);
if (channel_position == SND_MIXER_SCHN_UNKNOWN) {
pa_log("[%s:%u] Override map position '%s' invalid in '%s'", state->filename, state->lineno, n, state->section);
pa_xfree(n);
return -1;
}
}
if ((m = parse_mask(s ? s : n)) == 0) {
pa_log("[%s:%u] Override map '%s' invalid in '%s'", state->filename, state->lineno, s ? s : n, state->section);
pa_xfree(n);
return -1;
}
}
if (pa_streq(state->lvalue, "override-map.1"))
e->masks[i++][0] = m;
else
e->masks[i++][1] = m;
/* Later on we might add override-map.3 and so on here ... */
if (e->masks[channel_position][channel_count-1]) {
pa_log("[%s:%u] Override map '%s' duplicate position '%s' in '%s'", state->filename, state->lineno, s ? s : n, snd_mixer_selem_channel_name(channel_position), state->section);
pa_xfree(n);
return -1;
}
e->override_map |= (1 << (channel_count - 1));
e->masks[channel_position][channel_count-1] = m;
pa_xfree(n);
i++;
}
e->override_map = true;
return 0;
}
@ -2575,7 +2654,7 @@ static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx)
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return -1;
}
@ -2588,7 +2667,7 @@ static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx)
r = snd_mixer_selem_set_capture_switch_all(me, alsa_idx);
if (r < 0) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to set switch of %s: %s", buf, pa_alsa_strerror(errno));
}
@ -2596,7 +2675,7 @@ static int element_set_option(pa_alsa_element *e, snd_mixer_t *m, int alsa_idx)
pa_assert(e->enumeration_use == PA_ALSA_ENUMERATION_SELECT);
if ((r = snd_mixer_selem_set_enum_item(me, 0, alsa_idx)) < 0) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Failed to set enumeration of %s: %s", buf, pa_alsa_strerror(errno));
}
}
@ -2653,7 +2732,7 @@ static int option_verify(pa_alsa_option *o) {
if (o->element->enumeration_use != PA_ALSA_ENUMERATION_SELECT &&
o->element->switch_use != PA_ALSA_SWITCH_SELECT) {
alsa_id_str(buf, sizeof(buf), &o->element->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &o->element->alsa_id);
pa_log("Element %s of option %s not set for select.", buf, o->name);
return -1;
}
@ -2661,7 +2740,7 @@ static int option_verify(pa_alsa_option *o) {
if (o->element->switch_use == PA_ALSA_SWITCH_SELECT &&
!pa_streq(o->alsa_name, "on") &&
!pa_streq(o->alsa_name, "off")) {
alsa_id_str(buf, sizeof(buf), &o->element->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &o->element->alsa_id);
pa_log("Switch %s options need be named off or on ", buf);
return -1;
}
@ -2687,13 +2766,13 @@ static int element_verify(pa_alsa_element *e) {
(e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) ||
(e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) ||
(e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log("Element %s cannot be required and absent at the same time.", buf);
return -1;
}
if (e->switch_use == PA_ALSA_SWITCH_SELECT && e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log("Element %s cannot set select for both switch and enumeration.", buf);
return -1;
}
@ -2719,6 +2798,7 @@ static int path_verify(pa_alsa_path *p) {
{ "analog-input-video", N_("Video"), PA_DEVICE_PORT_TYPE_VIDEO },
{ "analog-output", N_("Analog Output"), PA_DEVICE_PORT_TYPE_ANALOG },
{ "analog-output-headphones", N_("Headphones"), PA_DEVICE_PORT_TYPE_HEADPHONES },
{ "analog-output-headphones-2", N_("Headphones 2"), PA_DEVICE_PORT_TYPE_HEADPHONES },
{ "analog-output-headphones-mono", N_("Headphones Mono Output"), PA_DEVICE_PORT_TYPE_HEADPHONES },
{ "analog-output-lineout", N_("Line Out"), PA_DEVICE_PORT_TYPE_LINE },
{ "analog-output-mono", N_("Analog Mono Output"), PA_DEVICE_PORT_TYPE_ANALOG },
@ -2809,6 +2889,15 @@ pa_alsa_path* pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa
{ "enumeration", element_parse_enumeration, NULL, NULL },
{ "override-map.1", element_parse_override_map, NULL, NULL },
{ "override-map.2", element_parse_override_map, NULL, NULL },
{ "override-map.3", element_parse_override_map, NULL, NULL },
{ "override-map.4", element_parse_override_map, NULL, NULL },
{ "override-map.5", element_parse_override_map, NULL, NULL },
{ "override-map.6", element_parse_override_map, NULL, NULL },
{ "override-map.7", element_parse_override_map, NULL, NULL },
{ "override-map.8", element_parse_override_map, NULL, NULL },
#if POSITION_MASK_CHANNELS > 8
#error "Add override-map.9+ definitions"
#endif
/* ... later on we might add override-map.3 and so on here ... */
{ "required", element_parse_required, NULL, NULL },
{ "required-any", element_parse_required, NULL, NULL },
@ -3026,6 +3115,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m
double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX];
pa_channel_position_t t;
pa_channel_position_mask_t path_volume_channels = 0;
bool min_dB_set, max_dB_set;
char buf[64];
pa_assert(p);
@ -3041,22 +3131,23 @@ int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m
pa_log_debug("Probing path '%s'", p->name);
PA_LLIST_FOREACH(j, p->jacks) {
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &j->alsa_id);
if (jack_probe(j, mapping, m) < 0) {
p->supported = false;
pa_log_debug("Probe of jack '%s' failed.", j->alsa_name);
pa_log_debug("Probe of jack %s failed.", buf);
return -1;
}
pa_log_debug("Probe of jack '%s' succeeded (%s)", j->alsa_name, j->has_control ? "found!" : "not found");
pa_log_debug("Probe of jack %s succeeded (%s)", buf, j->has_control ? "found!" : "not found");
}
PA_LLIST_FOREACH(e, p->elements) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
if (element_probe(e, m) < 0) {
p->supported = false;
pa_log_debug("Probe of element %s failed.", buf);
return -1;
}
pa_log_debug("Probe of element %s succeeded (volume=%d, switch=%d, enumeration=%d).", buf, e->volume_use, e->switch_use, e->enumeration_use);
pa_log_debug("Probe of element %s succeeded (volume=%d, switch=%d, enumeration=%d, has_dB=%d).", buf, e->volume_use, e->switch_use, e->enumeration_use, e->has_dB);
if (ignore_dB)
e->has_dB = false;
@ -3120,17 +3211,29 @@ int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m
p->supported = true;
p->min_dB = INFINITY;
min_dB_set = false;
p->max_dB = -INFINITY;
max_dB_set = false;
for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) {
if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) {
if (p->min_dB > min_dB[t])
if (p->min_dB > min_dB[t]) {
p->min_dB = min_dB[t];
min_dB_set = true;
}
if (p->max_dB < max_dB[t])
if (p->max_dB < max_dB[t]) {
p->max_dB = max_dB[t];
max_dB_set = true;
}
}
}
/* this is probably a wrong prediction, but it should be safe */
if (!min_dB_set)
p->min_dB = -INFINITY;
if (!max_dB_set)
p->max_dB = 0;
return 0;
}
@ -3147,7 +3250,7 @@ void pa_alsa_setting_dump(pa_alsa_setting *s) {
void pa_alsa_jack_dump(pa_alsa_jack *j) {
pa_assert(j);
pa_log_debug("Jack %s, alsa_name='%s', detection %s", j->name, j->alsa_name, j->has_control ? "possible" : "unavailable");
pa_log_debug("Jack %s, alsa_name='%s', index='%d', detection %s", j->name, j->alsa_id.name, j->alsa_id.index, j->has_control ? "possible" : "unavailable");
}
void pa_alsa_option_dump(pa_alsa_option *o) {
@ -3167,8 +3270,8 @@ void pa_alsa_element_dump(pa_alsa_element *e) {
pa_alsa_option *o;
pa_assert(e);
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s",
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%02x",
buf,
e->direction,
e->switch_use,
@ -3180,7 +3283,7 @@ void pa_alsa_element_dump(pa_alsa_element *e) {
e->required_absent,
(long long unsigned) e->merged_mask,
e->n_channels,
pa_yes_no(e->override_map));
e->override_map);
PA_LLIST_FOREACH(o, e->options)
pa_alsa_option_dump(o);
@ -3227,7 +3330,7 @@ static void element_set_callback(pa_alsa_element *e, snd_mixer_t *m, snd_mixer_e
SELEM_INIT(sid, &e->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
alsa_id_str(buf, sizeof(buf), &e->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &e->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return;
}
@ -3522,7 +3625,7 @@ static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_
SELEM_INIT(sid, &a->alsa_id);
if (!(me = snd_mixer_find_selem(m, sid))) {
alsa_id_str(buf, sizeof(buf), &a->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &a->alsa_id);
pa_log_warn("Element %s seems to have disappeared.", buf);
return false;
}
@ -3553,7 +3656,7 @@ static bool element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_mixer_
return false;
for (s = 0; s <= SND_MIXER_SCHN_LAST; s++)
if (a->masks[s][a->n_channels-1] != b->masks[s][b->n_channels-1]) {
alsa_id_str(buf, sizeof(buf), &a->alsa_id);
pa_alsa_mixer_id_to_string(buf, sizeof(buf), &a->alsa_id);
pa_log_debug("Element %s is not a subset - mask a: 0x%" PRIx64 ", mask b: 0x%" PRIx64 ", at channel %d",
buf, a->masks[s][a->n_channels-1], b->masks[s][b->n_channels-1], s);
return false;
@ -3630,7 +3733,8 @@ static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
continue;
PA_LLIST_FOREACH(jb, p2->jacks) {
if (jb->has_control && pa_streq(jb->alsa_name, ja->alsa_name) &&
if (jb->has_control && pa_streq(ja->alsa_id.name, jb->alsa_id.name) &&
(ja->alsa_id.index == jb->alsa_id.index) &&
(ja->state_plugged == jb->state_plugged) &&
(ja->state_unplugged == jb->state_unplugged)) {
exists = true;
@ -4325,7 +4429,8 @@ static void profile_set_set_availability_groups(pa_alsa_profile_set *ps) {
PA_LLIST_FOREACH(j2, p2->jacks) {
if (!j2->has_control || j2->state_plugged == PA_AVAILABLE_NO)
continue;
if (pa_streq(j->alsa_name, j2->alsa_name)) {
if (pa_streq(j->alsa_id.name, j2->alsa_id.name) &&
j->alsa_id.index == j2->alsa_id.index) {
j->state_plugged = PA_AVAILABLE_UNKNOWN;
j2->state_plugged = PA_AVAILABLE_UNKNOWN;
found = p2->availability_group;

View file

@ -40,6 +40,8 @@ typedef struct pa_alsa_profile pa_alsa_profile;
typedef struct pa_alsa_profile pa_card_profile;
typedef struct pa_alsa_device pa_alsa_device;
#define POSITION_MASK_CHANNELS 8
typedef enum pa_alsa_switch_use {
PA_ALSA_SWITCH_IGNORE,
PA_ALSA_SWITCH_MUTE, /* make this switch follow mute status */
@ -110,6 +112,8 @@ struct pa_alsa_mixer_id {
int index;
};
char *pa_alsa_mixer_id_to_string(char *dst, size_t dst_len, pa_alsa_mixer_id *id);
/* An option belongs to an element and refers to one enumeration item
* of the element is an enumeration item, or a switch status if the
* element is a switch item. */
@ -149,7 +153,7 @@ struct pa_alsa_element {
long constant_volume;
bool override_map:1;
unsigned int override_map;
bool direction_try_other:1;
bool has_dB:1;
@ -157,7 +161,7 @@ struct pa_alsa_element {
long volume_limit; /* -1 for no configured limit */
double min_dB, max_dB;
pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][POSITION_MASK_CHANNELS];
unsigned n_channels;
pa_channel_position_mask_t merged_mask;
@ -174,6 +178,7 @@ struct pa_alsa_jack {
snd_mixer_t *mixer_handle;
char *mixer_device_name;
struct pa_alsa_mixer_id alsa_id;
char *name; /* E g "Headphone" */
char *alsa_name; /* E g "Headphone Jack" */
bool has_control; /* is the jack itself present? */
@ -191,7 +196,7 @@ struct pa_alsa_jack {
bool append_pcm_to_name;
};
pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name);
pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name, int index);
void pa_alsa_jack_free(pa_alsa_jack *jack);
void pa_alsa_jack_set_has_control(pa_alsa_jack *jack, bool has_control);
void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in);

View file

@ -1705,7 +1705,7 @@ static pa_alsa_jack* ucm_get_jack(pa_alsa_ucm_config *ucm, pa_alsa_ucm_device *d
j = NULL;
goto finish;
}
j = pa_alsa_jack_new(NULL, mixer_device_name, name);
j = pa_alsa_jack_new(NULL, mixer_device_name, name, 0);
PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
finish:
@ -1939,7 +1939,7 @@ static void ucm_mapping_jack_probe(pa_alsa_mapping *m, pa_hashmap *mixers) {
continue;
}
has_control = pa_alsa_mixer_find_card(mixer_handle, dev->jack->alsa_name, 0) != NULL;
has_control = pa_alsa_mixer_find_card(mixer_handle, &dev->jack->alsa_id, 0) != NULL;
pa_alsa_jack_set_has_control(dev->jack, has_control);
pa_log_info("UCM jack %s has_control=%d", dev->jack->name, dev->jack->has_control);
}

View file

@ -1609,8 +1609,8 @@ static snd_mixer_elem_t *pa_alsa_mixer_find(snd_mixer_t *mixer,
return NULL;
}
snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, const char *name, unsigned int device) {
return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, name, 0, device);
snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, struct pa_alsa_mixer_id *alsa_id, unsigned int device) {
return pa_alsa_mixer_find(mixer, SND_CTL_ELEM_IFACE_CARD, alsa_id->name, alsa_id->index, device);
}
snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device) {

View file

@ -154,7 +154,7 @@ const char* pa_alsa_strerror(int errnum);
bool pa_alsa_may_tsched(bool want);
#endif
snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, const char *name, unsigned int device);
snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, struct pa_alsa_mixer_id *alsa_id, unsigned int device);
snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device);
snd_mixer_t *pa_alsa_open_mixer(pa_hashmap *mixers, int alsa_card_index, bool probe);

View file

@ -13,17 +13,24 @@
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
; Path for mixers that have a 'Headphone2' control
; Path for the second headphone output on dual-headphone machines.
;
; See analog-output.conf.common for an explanation on the directives
[General]
priority = 98
description-key = analog-output-headphones
[Properties]
device.icon_name = audio-headphones
; HP EliteDesk 800 SFF Headphone
[Jack Front Headphone,1]
required-any = any
; HP EliteDesk 800 DM Headphone
[Jack Front Headphone Surround]
required-any = any
[Element Hardware Master]
switch = mute
volume = merge
@ -47,6 +54,13 @@ volume = off
switch = mute
volume = zero
[Element Headphone,1]
required-any = any
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Headphone+LO]
switch = mute
volume = zero
@ -56,7 +70,7 @@ switch = off
volume = off
[Element Headphone2]
required = any
required-any = any
switch = mute
volume = merge
override-map.1 = all

View file

@ -35,6 +35,10 @@ state.unplugged = unknown
[Jack Front Headphone]
required-any = any
; HP EliteDesk 800 DM Headset
[Jack Front Headphone Front]
required-any = any
[Jack Front Headphone Phantom]
required-any = any
state.plugged = unknown
@ -89,6 +93,13 @@ volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
; This path is intended to control the first headphones, not
; the second headphones. But it should not hurt if we leave the second
; headphone jack enabled nonetheless.
[Element Headphone,1]
switch = mute
volume = zero
[Element Headset]
required-any = any
switch = mute

View file

@ -127,6 +127,10 @@ required-any = any
switch = off
volume = off
[Element Headphone,1]
switch = off
volume = off
[Element Headphone2]
switch = off
volume = off

View file

@ -44,6 +44,10 @@ override-map.2 = all-left,all-right
switch = mute
volume = zero
[Element Headphone,1]
switch = mute
volume = zero
[Element Headphone+LO]
switch = mute
volume = zero

View file

@ -76,6 +76,10 @@ volume = off
switch = mute
volume = zero
[Element Headphone,1]
switch = mute
volume = zero
[Element Headphone2]
switch = mute
volume = zero

View file

@ -101,6 +101,10 @@ name = analog-output-speaker
switch = off
volume = off
[Element Headphone,1]
switch = off
volume = off
[Element Headphone2]
switch = off
volume = off

View file

@ -77,3 +77,4 @@ description = Analog Stereo Input Channel D
input-mappings = analog-stereo-d-input
priority = 1
skip-probe = yes