mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-03 09:01:50 -05:00
alsa: add card ports and path probe cache
To be able to add ports to all profiles, we need to probe all profiles at startup. To speed this up, we now have a cache of probes paths which is owned by the profile set. Since paths are now owned by the profile set, the path set must now have a hashmap of paths instead of a linked list. Signed-off-by: David Henningsson <david.henningsson@canonical.com>
This commit is contained in:
parent
078a39af88
commit
426daaf6bb
5 changed files with 268 additions and 200 deletions
|
|
@ -561,13 +561,10 @@ void pa_alsa_path_free(pa_alsa_path *p) {
|
|||
}
|
||||
|
||||
void pa_alsa_path_set_free(pa_alsa_path_set *ps) {
|
||||
pa_alsa_path *p;
|
||||
pa_assert(ps);
|
||||
|
||||
while ((p = ps->paths)) {
|
||||
PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
|
||||
pa_alsa_path_free(p);
|
||||
}
|
||||
if (ps->paths)
|
||||
pa_hashmap_free(ps->paths, NULL, NULL);
|
||||
|
||||
pa_xfree(ps);
|
||||
}
|
||||
|
|
@ -2575,7 +2572,8 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
|
|||
pa_assert(m);
|
||||
|
||||
if (p->probed)
|
||||
return 0;
|
||||
return p->supported ? 0 : -1;
|
||||
p->probed = TRUE;
|
||||
|
||||
pa_zero(min_dB);
|
||||
pa_zero(max_dB);
|
||||
|
|
@ -2650,7 +2648,6 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) {
|
|||
path_create_settings(p);
|
||||
|
||||
p->supported = TRUE;
|
||||
p->probed = TRUE;
|
||||
|
||||
p->min_dB = INFINITY;
|
||||
p->max_dB = -INFINITY;
|
||||
|
|
@ -2767,12 +2764,13 @@ void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_c
|
|||
|
||||
void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata) {
|
||||
pa_alsa_path *p;
|
||||
void *state;
|
||||
|
||||
pa_assert(ps);
|
||||
pa_assert(m);
|
||||
pa_assert(cb);
|
||||
|
||||
PA_LLIST_FOREACH(p, ps->paths)
|
||||
PA_HASHMAP_FOREACH(p, ps->paths, state)
|
||||
pa_alsa_path_set_callback(p, m, cb, userdata);
|
||||
}
|
||||
|
||||
|
|
@ -2780,7 +2778,8 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d
|
|||
pa_alsa_path_set *ps;
|
||||
char **pn = NULL, **en = NULL, **ie;
|
||||
pa_alsa_decibel_fix *db_fix;
|
||||
void *state;
|
||||
void *state, *state2;
|
||||
pa_hashmap *cache;
|
||||
|
||||
pa_assert(m);
|
||||
pa_assert(m->profile_set);
|
||||
|
|
@ -2792,19 +2791,24 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d
|
|||
|
||||
ps = pa_xnew0(pa_alsa_path_set, 1);
|
||||
ps->direction = direction;
|
||||
ps->paths = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
|
||||
|
||||
if (direction == PA_ALSA_DIRECTION_OUTPUT)
|
||||
if (direction == PA_ALSA_DIRECTION_OUTPUT) {
|
||||
pn = m->output_path_names;
|
||||
else if (direction == PA_ALSA_DIRECTION_INPUT)
|
||||
cache = m->profile_set->output_paths;
|
||||
}
|
||||
else if (direction == PA_ALSA_DIRECTION_INPUT) {
|
||||
pn = m->input_path_names;
|
||||
cache = m->profile_set->input_paths;
|
||||
}
|
||||
|
||||
if (pn) {
|
||||
char **in;
|
||||
|
||||
for (in = pn; *in; in++) {
|
||||
pa_alsa_path *p;
|
||||
pa_alsa_path *p = NULL;
|
||||
pa_bool_t duplicate = FALSE;
|
||||
char **kn, *fn;
|
||||
char **kn;
|
||||
|
||||
for (kn = pn; kn < in; kn++)
|
||||
if (pa_streq(*kn, *in)) {
|
||||
|
|
@ -2815,15 +2819,18 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d
|
|||
if (duplicate)
|
||||
continue;
|
||||
|
||||
fn = pa_sprintf_malloc("%s.conf", *in);
|
||||
|
||||
if ((p = pa_alsa_path_new(paths_dir, fn, direction))) {
|
||||
p->path_set = ps;
|
||||
PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
|
||||
ps->last_path = p;
|
||||
}
|
||||
|
||||
p = pa_hashmap_get(cache, *in);
|
||||
if (!p) {
|
||||
char *fn = pa_sprintf_malloc("%s.conf", *in);
|
||||
p = pa_alsa_path_new(paths_dir, fn, direction);
|
||||
pa_xfree(fn);
|
||||
if (p)
|
||||
pa_hashmap_put(cache, *in, p);
|
||||
}
|
||||
pa_assert(pa_hashmap_get(cache, *in) == p);
|
||||
if (p)
|
||||
pa_hashmap_put(ps->paths, p, p);
|
||||
|
||||
}
|
||||
|
||||
goto finish;
|
||||
|
|
@ -2844,7 +2851,6 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d
|
|||
pa_alsa_path *p;
|
||||
|
||||
p = pa_alsa_path_synthesize(*ie, direction);
|
||||
p->path_set = ps;
|
||||
|
||||
/* Mark all other passed elements for require-absent */
|
||||
for (je = en; *je; je++) {
|
||||
|
|
@ -2864,8 +2870,7 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d
|
|||
p->last_element = e;
|
||||
}
|
||||
|
||||
PA_LLIST_INSERT_AFTER(pa_alsa_path, ps->paths, ps->last_path, p);
|
||||
ps->last_path = p;
|
||||
pa_hashmap_put(ps->paths, *ie, p);
|
||||
}
|
||||
|
||||
finish:
|
||||
|
|
@ -2873,7 +2878,7 @@ finish:
|
|||
PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) {
|
||||
pa_alsa_path *p;
|
||||
|
||||
PA_LLIST_FOREACH(p, ps->paths) {
|
||||
PA_HASHMAP_FOREACH(p, ps->paths, state2) {
|
||||
pa_alsa_element *e;
|
||||
|
||||
PA_LLIST_FOREACH(e, p->elements) {
|
||||
|
|
@ -2895,6 +2900,7 @@ finish:
|
|||
|
||||
void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
|
||||
pa_alsa_path *p;
|
||||
void *state;
|
||||
pa_assert(ps);
|
||||
|
||||
pa_log_debug("Path Set %p, direction=%i, probed=%s",
|
||||
|
|
@ -2902,7 +2908,7 @@ void pa_alsa_path_set_dump(pa_alsa_path_set *ps) {
|
|||
ps->direction,
|
||||
pa_yes_no(ps->probed));
|
||||
|
||||
PA_LLIST_FOREACH(p, ps->paths)
|
||||
PA_HASHMAP_FOREACH(p, ps->paths, state)
|
||||
pa_alsa_path_dump(p);
|
||||
}
|
||||
|
||||
|
|
@ -3048,20 +3054,21 @@ static pa_bool_t element_is_subset(pa_alsa_element *a, pa_alsa_element *b, snd_m
|
|||
}
|
||||
|
||||
static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
|
||||
pa_alsa_path *p, *np;
|
||||
pa_alsa_path *p;
|
||||
void *state;
|
||||
|
||||
pa_assert(ps);
|
||||
pa_assert(m);
|
||||
|
||||
/* If we only have one path, then don't bother */
|
||||
if (!ps->paths || !ps->paths->next)
|
||||
if (pa_hashmap_size(ps->paths) < 2)
|
||||
return;
|
||||
|
||||
for (p = ps->paths; p; p = np) {
|
||||
PA_HASHMAP_FOREACH(p, ps->paths, state) {
|
||||
pa_alsa_path *p2;
|
||||
np = p->next;
|
||||
void *state2;
|
||||
|
||||
PA_LLIST_FOREACH(p2, ps->paths) {
|
||||
PA_HASHMAP_FOREACH(p2, ps->paths, state2) {
|
||||
pa_alsa_element *ea, *eb;
|
||||
pa_bool_t is_subset = TRUE;
|
||||
|
||||
|
|
@ -3090,24 +3097,33 @@ static void path_set_condense(pa_alsa_path_set *ps, snd_mixer_t *m) {
|
|||
|
||||
if (is_subset) {
|
||||
pa_log_debug("Removing path '%s' as it is a subset of '%s'.", p->name, p2->name);
|
||||
PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
|
||||
pa_alsa_path_free(p);
|
||||
pa_hashmap_remove(ps->paths, p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static pa_alsa_path* path_set_find_path_by_name(pa_alsa_path_set *ps, const char* name, pa_alsa_path *ignore)
|
||||
{
|
||||
pa_alsa_path* p;
|
||||
void *state;
|
||||
|
||||
PA_HASHMAP_FOREACH(p, ps->paths, state)
|
||||
if (p != ignore && pa_streq(p->name, name))
|
||||
return p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
|
||||
pa_alsa_path *p, *q;
|
||||
void *state, *state2;
|
||||
|
||||
PA_LLIST_FOREACH(p, ps->paths) {
|
||||
PA_HASHMAP_FOREACH(p, ps->paths, state) {
|
||||
unsigned i;
|
||||
char *m;
|
||||
|
||||
for (q = p->next; q; q = q->next)
|
||||
if (pa_streq(q->name, p->name))
|
||||
break;
|
||||
q = path_set_find_path_by_name(ps, p->name, p);
|
||||
|
||||
if (!q)
|
||||
continue;
|
||||
|
|
@ -3115,7 +3131,8 @@ static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
|
|||
m = pa_xstrdup(p->name);
|
||||
|
||||
/* OK, this name is not unique, hence let's rename */
|
||||
for (i = 1, q = p; q; q = q->next) {
|
||||
i = 1;
|
||||
PA_HASHMAP_FOREACH(q, ps->paths, state2) {
|
||||
char *nn, *nd;
|
||||
|
||||
if (!pa_streq(q->name, m))
|
||||
|
|
@ -3136,34 +3153,6 @@ static void path_set_make_paths_unique(pa_alsa_path_set *ps) {
|
|||
}
|
||||
}
|
||||
|
||||
void pa_alsa_path_set_probe(pa_alsa_path_set *ps, snd_mixer_t *m, pa_bool_t ignore_dB) {
|
||||
pa_alsa_path *p, *n;
|
||||
|
||||
pa_assert(ps);
|
||||
|
||||
if (ps->probed)
|
||||
return;
|
||||
|
||||
for (p = ps->paths; p; p = n) {
|
||||
n = p->next;
|
||||
|
||||
if (pa_alsa_path_probe(p, m, ignore_dB) < 0) {
|
||||
PA_LLIST_REMOVE(pa_alsa_path, ps->paths, p);
|
||||
pa_alsa_path_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
pa_log_debug("Found mixer paths (before tidying):");
|
||||
pa_alsa_path_set_dump(ps);
|
||||
|
||||
path_set_condense(ps, m);
|
||||
path_set_make_paths_unique(ps);
|
||||
ps->probed = TRUE;
|
||||
|
||||
pa_log_debug("Available mixer paths (after tidying):");
|
||||
pa_alsa_path_set_dump(ps);
|
||||
}
|
||||
|
||||
static void mapping_free(pa_alsa_mapping *m) {
|
||||
pa_assert(m);
|
||||
|
||||
|
|
@ -3175,6 +3164,10 @@ static void mapping_free(pa_alsa_mapping *m) {
|
|||
pa_xstrfreev(m->output_path_names);
|
||||
pa_xstrfreev(m->input_element);
|
||||
pa_xstrfreev(m->output_element);
|
||||
if (m->input_path_set)
|
||||
pa_alsa_path_set_free(m->input_path_set);
|
||||
if (m->output_path_set)
|
||||
pa_alsa_path_set_free(m->output_path_set);
|
||||
|
||||
pa_assert(!m->input_pcm);
|
||||
pa_assert(!m->output_pcm);
|
||||
|
|
@ -3203,6 +3196,24 @@ static void profile_free(pa_alsa_profile *p) {
|
|||
void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) {
|
||||
pa_assert(ps);
|
||||
|
||||
if (ps->input_paths) {
|
||||
pa_alsa_path *p;
|
||||
|
||||
while ((p = pa_hashmap_steal_first(ps->input_paths)))
|
||||
pa_alsa_path_free(p);
|
||||
|
||||
pa_hashmap_free(ps->input_paths, NULL, NULL);
|
||||
}
|
||||
|
||||
if (ps->output_paths) {
|
||||
pa_alsa_path *p;
|
||||
|
||||
while ((p = pa_hashmap_steal_first(ps->output_paths)))
|
||||
pa_alsa_path_free(p);
|
||||
|
||||
pa_hashmap_free(ps->output_paths, NULL, NULL);
|
||||
}
|
||||
|
||||
if (ps->profiles) {
|
||||
pa_alsa_profile *p;
|
||||
|
||||
|
|
@ -3688,6 +3699,53 @@ fail:
|
|||
return -1;
|
||||
}
|
||||
|
||||
static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
|
||||
pa_alsa_direction_t direction) {
|
||||
|
||||
pa_alsa_path *p;
|
||||
void *state;
|
||||
snd_pcm_t *pcm_handle;
|
||||
pa_alsa_path_set *ps;
|
||||
snd_mixer_t *mixer_handle;
|
||||
|
||||
if (direction == PA_ALSA_DIRECTION_OUTPUT) {
|
||||
if (m->output_path_set)
|
||||
return; /* Already probed */
|
||||
m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
|
||||
pcm_handle = m->output_pcm;
|
||||
} else {
|
||||
if (m->input_path_set)
|
||||
return; /* Already probed */
|
||||
m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
|
||||
pcm_handle = m->input_pcm;
|
||||
}
|
||||
|
||||
if (!ps)
|
||||
return; /* No paths */
|
||||
|
||||
pa_assert(pcm_handle);
|
||||
|
||||
mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL);
|
||||
if (!mixer_handle)
|
||||
return; /* Cannot open mixer :-( */
|
||||
|
||||
PA_HASHMAP_FOREACH(p, ps->paths, state) {
|
||||
if (pa_alsa_path_probe(p, mixer_handle, m->profile_set->ignore_dB) < 0) {
|
||||
pa_hashmap_remove(ps->paths, p);
|
||||
}
|
||||
}
|
||||
|
||||
path_set_condense(ps, mixer_handle);
|
||||
path_set_make_paths_unique(ps);
|
||||
ps->probed = TRUE;
|
||||
|
||||
if (mixer_handle)
|
||||
snd_mixer_close(mixer_handle);
|
||||
|
||||
pa_log_debug("Available mixer paths (after tidying):");
|
||||
pa_alsa_path_set_dump(ps);
|
||||
}
|
||||
|
||||
static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
|
||||
|
||||
static const struct description_map well_known_descriptions[] = {
|
||||
|
|
@ -4051,6 +4109,8 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel
|
|||
ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
ps->input_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
ps->output_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
|
||||
items[0].data = &ps->auto_profiles;
|
||||
|
||||
|
|
@ -4213,8 +4273,21 @@ void pa_alsa_profile_set_probe(
|
|||
|
||||
last = p;
|
||||
|
||||
if (p->supported)
|
||||
if (!p->supported)
|
||||
continue;
|
||||
|
||||
pa_log_debug("Profile %s supported.", p->name);
|
||||
|
||||
if (p->output_mappings)
|
||||
PA_IDXSET_FOREACH(m, p->output_mappings, idx)
|
||||
if (m->output_pcm)
|
||||
mapping_paths_probe(m, p, PA_ALSA_DIRECTION_OUTPUT);
|
||||
|
||||
if (p->input_mappings)
|
||||
PA_IDXSET_FOREACH(m, p->input_mappings, idx)
|
||||
if (m->input_pcm)
|
||||
mapping_paths_probe(m, p, PA_ALSA_DIRECTION_INPUT);
|
||||
|
||||
}
|
||||
|
||||
/* Clean up */
|
||||
|
|
@ -4286,74 +4359,70 @@ void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
|
|||
pa_alsa_decibel_fix_dump(db_fix);
|
||||
}
|
||||
|
||||
void pa_alsa_add_ports(pa_core *c, pa_hashmap **p, pa_alsa_path_set *ps) {
|
||||
pa_alsa_path *path;
|
||||
static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
|
||||
const char* name,
|
||||
const char* description,
|
||||
pa_alsa_path *path,
|
||||
pa_alsa_setting *setting,
|
||||
pa_card_profile *cp,
|
||||
pa_hashmap *extra,
|
||||
pa_core *core) {
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(p);
|
||||
pa_assert(!*p);
|
||||
pa_assert(ps);
|
||||
|
||||
/* if there is no path, we don't want a port list */
|
||||
if (!ps->paths)
|
||||
return;
|
||||
|
||||
if (!ps->paths->next){
|
||||
pa_alsa_setting *s;
|
||||
|
||||
/* If there is only one path, but no or only one setting, then
|
||||
* we want a port list either */
|
||||
if (!ps->paths->settings || !ps->paths->settings->next)
|
||||
return;
|
||||
|
||||
/* Ok, there is only one path, however with multiple settings,
|
||||
* so let's create a port for each setting */
|
||||
*p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
|
||||
PA_LLIST_FOREACH(s, ps->paths->settings) {
|
||||
pa_device_port *port;
|
||||
pa_device_port * p = pa_hashmap_get(ports, name);
|
||||
if (!p) {
|
||||
pa_alsa_port_data *data;
|
||||
|
||||
port = pa_device_port_new(c, s->name, s->description, sizeof(pa_alsa_port_data));
|
||||
port->priority = s->priority;
|
||||
p = pa_device_port_new(core, name, description, sizeof(pa_alsa_port_data));
|
||||
pa_assert(p);
|
||||
pa_hashmap_put(ports, name, p);
|
||||
p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
|
||||
data = PA_DEVICE_PORT_DATA(port);
|
||||
data->path = ps->paths;
|
||||
data->setting = s;
|
||||
|
||||
pa_hashmap_put(*p, port->name, port);
|
||||
data = PA_DEVICE_PORT_DATA(p);
|
||||
data->path = path;
|
||||
data->setting = setting;
|
||||
}
|
||||
|
||||
} else {
|
||||
p->is_input |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_INPUT;
|
||||
p->is_output |= path->direction == PA_ALSA_DIRECTION_ANY || path->direction == PA_ALSA_DIRECTION_OUTPUT;
|
||||
|
||||
/* We have multiple paths, so let's create a port for each
|
||||
* one, and each of each settings */
|
||||
*p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
if (cp)
|
||||
pa_hashmap_put(p->profiles, cp->name, cp);
|
||||
|
||||
PA_LLIST_FOREACH(path, ps->paths) {
|
||||
if (extra) {
|
||||
pa_hashmap_put(extra, name, p);
|
||||
pa_device_port_ref(p);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void pa_alsa_path_set_add_ports(
|
||||
pa_alsa_path_set *ps,
|
||||
pa_card_profile *cp,
|
||||
pa_hashmap *ports,
|
||||
pa_hashmap *extra,
|
||||
pa_core *core) {
|
||||
|
||||
pa_alsa_path *path;
|
||||
void *state;
|
||||
|
||||
pa_assert(ports);
|
||||
|
||||
if (!ps)
|
||||
return;
|
||||
|
||||
PA_HASHMAP_FOREACH(path, ps->paths, state) {
|
||||
if (!path->settings || !path->settings->next) {
|
||||
pa_device_port *port;
|
||||
pa_alsa_port_data *data;
|
||||
|
||||
/* If there is no or just one setting we only need a
|
||||
* single entry */
|
||||
|
||||
port = pa_device_port_new(c, path->name, path->description, sizeof(pa_alsa_port_data));
|
||||
pa_device_port *port = device_port_alsa_init(ports, path->name,
|
||||
path->description, path, path->settings, cp, extra, core);
|
||||
port->priority = path->priority * 100;
|
||||
|
||||
|
||||
data = PA_DEVICE_PORT_DATA(port);
|
||||
data->path = path;
|
||||
data->setting = path->settings;
|
||||
|
||||
pa_hashmap_put(*p, port->name, port);
|
||||
} else {
|
||||
pa_alsa_setting *s;
|
||||
|
||||
PA_LLIST_FOREACH(s, path->settings) {
|
||||
pa_device_port *port;
|
||||
pa_alsa_port_data *data;
|
||||
char *n, *d;
|
||||
|
||||
n = pa_sprintf_malloc("%s;%s", path->name, s->name);
|
||||
|
|
@ -4363,21 +4432,27 @@ void pa_alsa_add_ports(pa_core *c, pa_hashmap **p, pa_alsa_path_set *ps) {
|
|||
else
|
||||
d = pa_xstrdup(path->description);
|
||||
|
||||
port = pa_device_port_new(c, n, d, sizeof(pa_alsa_port_data));
|
||||
port = device_port_alsa_init(ports, n, d, path, s, cp, extra, core);
|
||||
port->priority = path->priority * 100 + s->priority;
|
||||
|
||||
pa_xfree(n);
|
||||
pa_xfree(d);
|
||||
|
||||
data = PA_DEVICE_PORT_DATA(port);
|
||||
data->path = path;
|
||||
data->setting = s;
|
||||
|
||||
pa_hashmap_put(*p, port->name, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pa_log_debug("Added %u ports", pa_hashmap_size(*p));
|
||||
void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps, pa_card *card) {
|
||||
|
||||
pa_assert(p);
|
||||
pa_assert(!*p);
|
||||
pa_assert(ps);
|
||||
|
||||
if (ps->paths && pa_hashmap_size(ps->paths) > 0) {
|
||||
pa_assert(card);
|
||||
*p = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
pa_alsa_path_set_add_ports(ps, NULL, card->ports, *p, card->core);
|
||||
}
|
||||
|
||||
pa_log_debug("Added %u ports", *p ? pa_hashmap_size(*p) : 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,9 +158,6 @@ struct pa_alsa_element {
|
|||
* used to control it as if it had a single volume slider, a single
|
||||
* mute switch and a single list of selectable options. */
|
||||
struct pa_alsa_path {
|
||||
pa_alsa_path_set *path_set;
|
||||
PA_LLIST_FIELDS(pa_alsa_path);
|
||||
|
||||
pa_alsa_direction_t direction;
|
||||
|
||||
char *name;
|
||||
|
|
@ -192,13 +189,9 @@ struct pa_alsa_path {
|
|||
/* A path set is simply a set of paths that are applicable to a
|
||||
* device */
|
||||
struct pa_alsa_path_set {
|
||||
PA_LLIST_HEAD(pa_alsa_path, paths);
|
||||
pa_hashmap *paths;
|
||||
pa_alsa_direction_t direction;
|
||||
pa_bool_t probed:1;
|
||||
|
||||
/* This is used during parsing only, as a shortcut so that we
|
||||
* don't have to iterate the list all the time */
|
||||
pa_alsa_path *last_path;
|
||||
};
|
||||
|
||||
int pa_alsa_setting_select(pa_alsa_setting *s, snd_mixer_t *m);
|
||||
|
|
@ -242,6 +235,8 @@ struct pa_alsa_mapping {
|
|||
char **output_path_names;
|
||||
char **input_element; /* list of fallbacks */
|
||||
char **output_element;
|
||||
pa_alsa_path_set *input_path_set;
|
||||
pa_alsa_path_set *output_path_set;
|
||||
|
||||
unsigned supported;
|
||||
|
||||
|
|
@ -289,8 +284,11 @@ struct pa_alsa_profile_set {
|
|||
pa_hashmap *mappings;
|
||||
pa_hashmap *profiles;
|
||||
pa_hashmap *decibel_fixes;
|
||||
pa_hashmap *input_paths;
|
||||
pa_hashmap *output_paths;
|
||||
|
||||
pa_bool_t auto_profiles;
|
||||
pa_bool_t ignore_dB:1;
|
||||
pa_bool_t probed:1;
|
||||
};
|
||||
|
||||
|
|
@ -323,6 +321,7 @@ struct pa_alsa_port_data {
|
|||
pa_alsa_setting *setting;
|
||||
};
|
||||
|
||||
void pa_alsa_add_ports(pa_core *c, pa_hashmap **p, pa_alsa_path_set *ps);
|
||||
void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps, pa_card *card);
|
||||
void pa_alsa_path_set_add_ports(pa_alsa_path_set *ps, pa_card_profile *cp, pa_hashmap *ports, pa_hashmap *extra, pa_core *core);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1860,22 +1860,14 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char
|
|||
|
||||
pa_log_debug("Probed mixer path %s:", u->mixer_path->name);
|
||||
pa_alsa_path_dump(u->mixer_path);
|
||||
} else {
|
||||
|
||||
if (!(u->mixer_path_set = pa_alsa_path_set_new(mapping, PA_ALSA_DIRECTION_OUTPUT, u->paths_dir)))
|
||||
} else if (!(u->mixer_path_set = mapping->output_path_set))
|
||||
goto fail;
|
||||
|
||||
pa_alsa_path_set_probe(u->mixer_path_set, u->mixer_handle, ignore_dB);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
|
||||
if (u->mixer_path_set) {
|
||||
pa_alsa_path_set_free(u->mixer_path_set);
|
||||
u->mixer_path_set = NULL;
|
||||
} else if (u->mixer_path) {
|
||||
if (u->mixer_path) {
|
||||
pa_alsa_path_free(u->mixer_path);
|
||||
u->mixer_path = NULL;
|
||||
}
|
||||
|
|
@ -1912,7 +1904,7 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
|
|||
} else {
|
||||
|
||||
if (!u->mixer_path && u->mixer_path_set)
|
||||
u->mixer_path = u->mixer_path_set->paths;
|
||||
u->mixer_path = pa_hashmap_first(u->mixer_path_set->paths);
|
||||
|
||||
if (u->mixer_path) {
|
||||
/* Hmm, we have only a single path, then let's activate it */
|
||||
|
|
@ -1930,8 +1922,9 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
|
|||
/* Will we need to register callbacks? */
|
||||
if (u->mixer_path_set && u->mixer_path_set->paths) {
|
||||
pa_alsa_path *p;
|
||||
void *state;
|
||||
|
||||
PA_LLIST_FOREACH(p, u->mixer_path_set->paths) {
|
||||
PA_HASHMAP_FOREACH(p, u->mixer_path_set->paths, state) {
|
||||
if (p->has_volume || p->has_mute)
|
||||
need_mixer_callback = TRUE;
|
||||
}
|
||||
|
|
@ -2224,7 +2217,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
|
|||
}
|
||||
|
||||
if (u->mixer_path_set)
|
||||
pa_alsa_add_ports(u->core, &data.ports, u->mixer_path_set);
|
||||
pa_alsa_add_ports(&data.ports, u->mixer_path_set, card);
|
||||
|
||||
u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE | PA_SINK_LATENCY | (u->use_tsched ? PA_SINK_DYNAMIC_LATENCY : 0) |
|
||||
(set_formats ? PA_SINK_SET_FORMATS : 0));
|
||||
|
|
@ -2389,9 +2382,7 @@ static void userdata_free(struct userdata *u) {
|
|||
if (u->mixer_fdl)
|
||||
pa_alsa_fdlist_free(u->mixer_fdl);
|
||||
|
||||
if (u->mixer_path_set)
|
||||
pa_alsa_path_set_free(u->mixer_path_set);
|
||||
else if (u->mixer_path)
|
||||
if (u->mixer_path && !u->mixer_path_set)
|
||||
pa_alsa_path_free(u->mixer_path);
|
||||
|
||||
if (u->mixer_handle)
|
||||
|
|
|
|||
|
|
@ -1598,22 +1598,14 @@ static void find_mixer(struct userdata *u, pa_alsa_mapping *mapping, const char
|
|||
|
||||
pa_log_debug("Probed mixer path %s:", u->mixer_path->name);
|
||||
pa_alsa_path_dump(u->mixer_path);
|
||||
} else {
|
||||
|
||||
if (!(u->mixer_path_set = pa_alsa_path_set_new(mapping, PA_ALSA_DIRECTION_INPUT, u->paths_dir)))
|
||||
} else if (!(u->mixer_path_set = mapping->input_path_set))
|
||||
goto fail;
|
||||
|
||||
pa_alsa_path_set_probe(u->mixer_path_set, u->mixer_handle, ignore_dB);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
|
||||
if (u->mixer_path_set) {
|
||||
pa_alsa_path_set_free(u->mixer_path_set);
|
||||
u->mixer_path_set = NULL;
|
||||
} else if (u->mixer_path) {
|
||||
if (u->mixer_path) {
|
||||
pa_alsa_path_free(u->mixer_path);
|
||||
u->mixer_path = NULL;
|
||||
}
|
||||
|
|
@ -1649,7 +1641,7 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
|
|||
} else {
|
||||
|
||||
if (!u->mixer_path && u->mixer_path_set)
|
||||
u->mixer_path = u->mixer_path_set->paths;
|
||||
u->mixer_path = pa_hashmap_first(u->mixer_path_set->paths);
|
||||
|
||||
if (u->mixer_path) {
|
||||
/* Hmm, we have only a single path, then let's activate it */
|
||||
|
|
@ -1667,8 +1659,9 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
|
|||
/* Will we need to register callbacks? */
|
||||
if (u->mixer_path_set && u->mixer_path_set->paths) {
|
||||
pa_alsa_path *p;
|
||||
void *state;
|
||||
|
||||
PA_LLIST_FOREACH(p, u->mixer_path_set->paths) {
|
||||
PA_HASHMAP_FOREACH(p, u->mixer_path_set->paths, state) {
|
||||
if (p->has_volume || p->has_mute)
|
||||
need_mixer_callback = TRUE;
|
||||
}
|
||||
|
|
@ -1950,7 +1943,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
|
|||
}
|
||||
|
||||
if (u->mixer_path_set)
|
||||
pa_alsa_add_ports(u->core, &data.ports, u->mixer_path_set);
|
||||
pa_alsa_add_ports(&data.ports, u->mixer_path_set, card);
|
||||
|
||||
u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|(u->use_tsched ? PA_SOURCE_DYNAMIC_LATENCY : 0));
|
||||
pa_source_new_data_done(&data);
|
||||
|
|
@ -2089,9 +2082,7 @@ static void userdata_free(struct userdata *u) {
|
|||
if (u->mixer_fdl)
|
||||
pa_alsa_fdlist_free(u->mixer_fdl);
|
||||
|
||||
if (u->mixer_path_set)
|
||||
pa_alsa_path_set_free(u->mixer_path_set);
|
||||
else if (u->mixer_path)
|
||||
if (u->mixer_path && !u->mixer_path_set)
|
||||
pa_alsa_path_free(u->mixer_path);
|
||||
|
||||
if (u->mixer_handle)
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ struct profile_data {
|
|||
pa_alsa_profile *profile;
|
||||
};
|
||||
|
||||
static void add_profiles(struct userdata *u, pa_hashmap *h) {
|
||||
static void add_profiles(struct userdata *u, pa_hashmap *h, pa_hashmap *ports) {
|
||||
pa_alsa_profile *ap;
|
||||
void *state;
|
||||
|
||||
|
|
@ -136,18 +136,22 @@ static void add_profiles(struct userdata *u, pa_hashmap *h) {
|
|||
if (ap->output_mappings) {
|
||||
cp->n_sinks = pa_idxset_size(ap->output_mappings);
|
||||
|
||||
PA_IDXSET_FOREACH(m, ap->output_mappings, idx)
|
||||
PA_IDXSET_FOREACH(m, ap->output_mappings, idx) {
|
||||
pa_alsa_path_set_add_ports(m->output_path_set, cp, ports, NULL, u->core);
|
||||
if (m->channel_map.channels > cp->max_sink_channels)
|
||||
cp->max_sink_channels = m->channel_map.channels;
|
||||
}
|
||||
}
|
||||
|
||||
if (ap->input_mappings) {
|
||||
cp->n_sources = pa_idxset_size(ap->input_mappings);
|
||||
|
||||
PA_IDXSET_FOREACH(m, ap->input_mappings, idx)
|
||||
PA_IDXSET_FOREACH(m, ap->input_mappings, idx) {
|
||||
pa_alsa_path_set_add_ports(m->input_path_set, cp, ports, NULL, u->core);
|
||||
if (m->channel_map.channels > cp->max_source_channels)
|
||||
cp->max_source_channels = m->channel_map.channels;
|
||||
}
|
||||
}
|
||||
|
||||
d = PA_CARD_PROFILE_DATA(cp);
|
||||
d->profile = ap;
|
||||
|
|
@ -293,6 +297,7 @@ int pa__init(pa_module *m) {
|
|||
pa_card_new_data data;
|
||||
pa_modargs *ma;
|
||||
int alsa_card_index;
|
||||
pa_bool_t ignore_dB = FALSE;
|
||||
struct userdata *u;
|
||||
pa_reserve_wrapper *reserve = NULL;
|
||||
const char *description;
|
||||
|
|
@ -309,6 +314,11 @@ int pa__init(pa_module *m) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) {
|
||||
pa_log("Failed to parse ignore_dB argument.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
m->userdata = u = pa_xnew0(struct userdata, 1);
|
||||
u->core = m->core;
|
||||
u->module = m;
|
||||
|
|
@ -344,6 +354,8 @@ int pa__init(pa_module *m) {
|
|||
u->profile_set = pa_alsa_profile_set_new(fn, &u->core->default_channel_map);
|
||||
pa_xfree(fn);
|
||||
|
||||
u->profile_set->ignore_dB = ignore_dB;
|
||||
|
||||
if (!u->profile_set)
|
||||
goto fail;
|
||||
|
||||
|
|
@ -377,7 +389,7 @@ int pa__init(pa_module *m) {
|
|||
pa_reserve_wrapper_set_application_device_name(reserve, description);
|
||||
|
||||
data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
add_profiles(u, data.profiles);
|
||||
add_profiles(u, data.profiles, data.ports);
|
||||
|
||||
if (pa_hashmap_isempty(data.profiles)) {
|
||||
pa_log("Failed to find a working profile.");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue