@@ -3294,7 +3294,7 @@ static void mapping_free(pa_alsa_mapping *m) {
pa_assert(!m->input_pcm);
pa_assert(!m->output_pcm);
- pa_xfree(m->ucm_context.ucm_devices);
+ ucm_mapping_context_free(&m->ucm_context);
pa_xfree(m);
}
@@ -4449,17 +4449,7 @@ void pa_alsa_profile_set_probe(
/* Clean up */
profile_finalize_probing(last, NULL);
- PA_HASHMAP_FOREACH(p, ps->profiles, state)
- if (!p->supported) {
- pa_hashmap_remove(ps->profiles, p->name);
- profile_free(p);
- }
-
- PA_HASHMAP_FOREACH(m, ps->mappings, state)
- if (m->supported <= 0) {
- pa_hashmap_remove(ps->mappings, m->name);
- mapping_free(m);
- }
+ pa_alsa_profile_set_drop_unsupported(ps);
paths_drop_unsupported(ps->input_paths);
paths_drop_unsupported(ps->output_paths);
@@ -4494,6 +4484,24 @@ void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) {
pa_alsa_decibel_fix_dump(db_fix);
}
+void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *ps) {
+ pa_alsa_profile *p;
+ pa_alsa_mapping *m;
+ void *state;
+
+ PA_HASHMAP_FOREACH(p, ps->profiles, state)
+ if (!p->supported) {
+ pa_hashmap_remove(ps->profiles, p->name);
+ profile_free(p);
+ }
+
+ PA_HASHMAP_FOREACH(m, ps->mappings, state)
+ if (m->supported <= 0) {
+ pa_hashmap_remove(ps->mappings, m->name);
+ mapping_free(m);
+ }
+}
+
static pa_device_port* device_port_alsa_init(pa_hashmap *ports,
const char* name,
const char* description,
@@ -323,6 +323,7 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel
void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec);
void pa_alsa_profile_set_free(pa_alsa_profile_set *s);
void pa_alsa_profile_set_dump(pa_alsa_profile_set *s);
+void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *s);
snd_mixer_t *pa_alsa_open_mixer_for_pcm(snd_pcm_t *pcm, char **ctl_device, snd_hctl_t **hctl);
@@ -590,6 +590,7 @@ static void ucm_add_port_combination(pa_hashmap *hash, pa_alsa_ucm_mapping_conte
pa_log_debug("Add port %s: %s", port->name, port->description);
port->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
}
+
port->priority = priority;
if (is_sink)
port->is_output = TRUE;
@@ -926,8 +927,8 @@ static int ucm_create_mapping_direction(struct pa_alsa_ucm_config *ucm,
channels = is_sink ? device->playback_channels : device->capture_channels;
if (m->ucm_context.ucm_devices_num == 0)
{ /* new mapping */
- m->supported = TRUE;
m->ucm_context.ucm = ucm;
+ m->ucm_context.direction = is_sink ? PA_ALSA_UCM_DIRECT_SINK : PA_ALSA_UCM_DIRECT_SOURCE;
m->device_strings = pa_xnew0(char*, 2);
m->device_strings[0] = pa_xstrdup(device_str);
@@ -970,6 +971,24 @@ static int ucm_create_mapping(struct pa_alsa_ucm_config *ucm,
return ret;
}
+static pa_alsa_jack* ucm_get_jack(struct pa_alsa_ucm_config *ucm, const char *dev_name) {
+ pa_alsa_jack *j;
+
+ PA_LLIST_FOREACH(j, ucm->jacks)
+ if (pa_streq(j->name, dev_name))
+ return j;
+
+ j = pa_xnew0(pa_alsa_jack, 1);
+ j->state_unplugged = PA_PORT_AVAILABLE_NO;
+ j->state_plugged = PA_PORT_AVAILABLE_YES;
+ j->name = pa_xstrdup(dev_name);
+ j->alsa_name = pa_sprintf_malloc("%s Jack", dev_name);
+
+ PA_LLIST_PREPEND(pa_alsa_jack, ucm->jacks, j);
+
+ return j;
+}
+
static int ucm_create_profile(struct pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps,
struct pa_alsa_ucm_verb *verb, const char *verb_name, const char *verb_desc) {
struct pa_alsa_profile *p;
@@ -991,7 +1010,7 @@ static int ucm_create_profile(struct pa_alsa_ucm_config *ucm, pa_alsa_profile_se
p->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
p->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- p->supported = 1;
+ p->supported = TRUE;
pa_hashmap_put(ps->profiles, p->name, p);
/* TODO: get profile priority from ucm info or policy management */
@@ -1026,12 +1045,146 @@ static int ucm_create_profile(struct pa_alsa_ucm_config *ucm, pa_alsa_profile_se
source = pa_proplist_gets(dev->proplist, PA_PROP_UCM_SOURCE);
ucm_create_mapping(ucm, ps, p, dev, verb_name, dev_name, sink, source);
+ dev->jack = ucm_get_jack(ucm, dev_name);
}
pa_alsa_profile_dump(p);
return 0;
}
+static snd_pcm_t* mapping_open_pcm(struct pa_alsa_ucm_config *ucm,
+ pa_alsa_mapping *m, int mode) {
+ pa_sample_spec try_ss = ucm->core->default_sample_spec;
+ pa_channel_map try_map = m->channel_map;
+ snd_pcm_uframes_t try_period_size, try_buffer_size;
+
+ try_ss.channels = try_map.channels;
+
+ try_period_size =
+ pa_usec_to_bytes(ucm->core->default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
+ pa_frame_size(&try_ss);
+ try_buffer_size = ucm->core->default_n_fragments * try_period_size;
+
+ return 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, TRUE);
+}
+
+static void profile_finalize_probing(pa_alsa_profile *p) {
+ pa_alsa_mapping *m;
+ uint32_t idx;
+
+ PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
+ if (!m->output_pcm)
+ continue;
+
+ if (p->supported)
+ m->supported++;
+
+ snd_pcm_close(m->output_pcm);
+ m->output_pcm = NULL;
+ }
+
+ PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
+ if (!m->input_pcm)
+ continue;
+
+ if (p->supported)
+ m->supported++;
+
+ snd_pcm_close(m->input_pcm);
+ m->input_pcm = NULL;
+ }
+}
+
+static struct pa_alsa_ucm_device *find_ucm_dev(
+ struct pa_alsa_ucm_verb *verb, const char *dev_name) {
+ struct pa_alsa_ucm_device *dev;
+
+ PA_LLIST_FOREACH(dev, verb->devices) {
+ const char *name = pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME);
+ if (pa_streq(name, dev_name))
+ return dev;
+ }
+
+ return NULL;
+}
+
+static void ucm_mapping_jack_probe(pa_alsa_mapping *m) {
+ snd_pcm_t *pcm_handle;
+ snd_mixer_t *mixer_handle;
+ snd_hctl_t *hctl_handle;
+ pa_alsa_ucm_mapping_context *context = &m->ucm_context;
+ struct pa_alsa_ucm_device *dev;
+ int i;
+
+ pcm_handle = m->direction == PA_ALSA_DIRECTION_OUTPUT ? m->output_pcm : m->input_pcm;
+ mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL, &hctl_handle);
+ if (!mixer_handle || !hctl_handle)
+ return;
+
+ for (i=0; i<context->ucm_devices_num; i++) {
+ dev = context->ucm_devices[i];
+ pa_assert (dev->jack);
+ dev->jack->has_control = pa_alsa_find_jack(hctl_handle, dev->jack->alsa_name) != NULL;
+ pa_log_info("ucm_mapping_jack_probe: %s has_control=%d", dev->jack->name, dev->jack->has_control);
+ }
+
+ snd_mixer_close(mixer_handle);
+}
+
+static void ucm_probe_jacks(struct pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps) {
+ void *state;
+ pa_alsa_profile *p;
+ pa_alsa_mapping *m;
+ uint32_t idx;
+
+ PA_HASHMAP_FOREACH(p, ps->profiles, state) {
+ /* change verb */
+ pa_log_info("ucm_probe_jacks: set ucm verb to %s", p->name);
+ if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) {
+ pa_log("ucm_probe_jacks: failed to set verb %s", p->name);
+ p->supported = FALSE;
+ continue;
+ }
+ PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
+ m->output_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_PLAYBACK);
+ if (!m->output_pcm) {
+ p->supported = FALSE;
+ break;
+ }
+ }
+ if (p->supported) {
+ PA_IDXSET_FOREACH(m, p->input_mappings, idx) {
+ m->input_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_CAPTURE);
+ if (!m->input_pcm) {
+ p->supported = FALSE;
+ break;
+ }
+ }
+ }
+ if (!p->supported) {
+ profile_finalize_probing(p);
+ continue;
+ }
+
+ pa_log_debug("Profile %s supported.", p->name);
+
+ PA_IDXSET_FOREACH(m, p->output_mappings, idx)
+ ucm_mapping_jack_probe(m);
+
+ PA_IDXSET_FOREACH(m, p->input_mappings, idx)
+ ucm_mapping_jack_probe(m);
+
+ profile_finalize_probing(p);
+ }
+
+ /* restore ucm state */
+ snd_use_case_set(ucm->ucm_mgr, "_verb", SND_USE_CASE_VERB_INACTIVE);
+
+ pa_alsa_profile_set_drop_unsupported(ps);
+}
+
pa_alsa_profile_set* ucm_add_profile_set(struct pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
struct pa_alsa_ucm_verb *verb;
pa_alsa_profile_set *ps;
@@ -1055,6 +1208,8 @@ pa_alsa_profile_set* ucm_add_profile_set(struct pa_alsa_ucm_config *ucm, pa_chan
ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
}
+
+ ucm_probe_jacks(ucm, ps);
ps->probed = TRUE;
return ps;
@@ -1090,11 +1245,18 @@ static void free_verb(struct pa_alsa_ucm_verb *verb) {
void ucm_free(struct pa_alsa_ucm_config *ucm) {
struct pa_alsa_ucm_verb *vi, *vn;
+ pa_alsa_jack *ji, *jn;
PA_LLIST_FOREACH_SAFE(vi, vn, ucm->verbs) {
PA_LLIST_REMOVE(pa_alsa_ucm_verb, ucm->verbs, vi);
free_verb(vi);
}
+ PA_LLIST_FOREACH_SAFE(ji, jn, ucm->jacks) {
+ PA_LLIST_REMOVE(pa_alsa_jack, ucm->jacks, ji);
+ pa_xfree(ji->alsa_name);
+ pa_xfree(ji->name);
+ pa_xfree(ji);
+ }
if (ucm->ucm_mgr)
{
snd_use_case_mgr_close(ucm->ucm_mgr);
@@ -1102,6 +1264,22 @@ void ucm_free(struct pa_alsa_ucm_config *ucm) {
}
}
+void ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
+ struct pa_alsa_ucm_device *dev;
+ int i;
+
+ /* clear ucm device pointer to mapping */
+ for (i=0; i<context->ucm_devices_num; i++) {
+ dev = context->ucm_devices[i];
+ if (context->direction == PA_ALSA_UCM_DIRECT_SINK)
+ dev->playback_mapping = NULL;
+ else
+ dev->capture_mapping = NULL;
+ }
+
+ pa_xfree(context->ucm_devices);
+}
+
static pa_bool_t stream_routed_to_mod_intent (struct pa_alsa_ucm_verb *verb,
struct pa_alsa_ucm_modifier *mod, const char *mapping_name) {
int i;
@@ -1113,15 +1291,12 @@ static pa_bool_t stream_routed_to_mod_intent (struct pa_alsa_ucm_verb *verb,
for (i=0; i<mod->n_suppdev; i++) {
dev_name = mod->supported_devices[i];
/* first find the supported device */
- PA_LLIST_FOREACH(dev, verb->devices) {
- const char *name = pa_proplist_gets(dev->proplist, PA_PROP_UCM_NAME);
- if (pa_streq(name, dev_name)) {
- /* then match the mapping name */
- mapping = mod->action_direct == PA_ALSA_UCM_DIRECT_SINK ? dev->playback_mapping : dev->capture_mapping;
- if (mapping && pa_streq(mapping->name, mapping_name))
- return TRUE;
- break;
- }
+ dev = find_ucm_dev(verb, dev_name);
+ if (dev) {
+ /* then match the mapping name */
+ mapping = mod->action_direct == PA_ALSA_UCM_DIRECT_SINK ? dev->playback_mapping : dev->capture_mapping;
+ if (mapping && pa_streq(mapping->name, mapping_name))
+ return TRUE;
}
}
@@ -27,6 +27,8 @@
#include <asoundlib.h>
#include <use-case.h>
+typedef struct pa_core pa_core;
+typedef struct pa_device_port pa_device_port;
typedef struct pa_alsa_mapping pa_alsa_mapping;
typedef struct pa_alsa_ucm_verb pa_alsa_ucm_verb;
@@ -35,9 +37,11 @@ typedef struct pa_alsa_ucm_device pa_alsa_ucm_device;
typedef struct pa_alsa_ucm_config pa_alsa_ucm_config;
typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context;
typedef struct pa_alsa_port_data_ucm pa_alsa_port_data_ucm;
+typedef struct pa_alsa_jack pa_alsa_jack;
int ucm_set_profile(struct pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile);
void ucm_free(struct pa_alsa_ucm_config *ucm);
+void ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context);
pa_alsa_profile_set* ucm_add_profile_set(struct pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map);
int ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, struct pa_alsa_ucm_verb ** p_verb);
void ucm_add_ports(pa_hashmap **p, pa_proplist *proplist, pa_alsa_ucm_mapping_context *context, int is_sink, pa_card *card);
@@ -70,6 +74,7 @@ struct pa_alsa_ucm_device {
int n_suppdev;
char **conflicting_devices;
char **supported_devices;
+ pa_alsa_jack *jack;
};
struct pa_alsa_ucm_modifier {
@@ -94,14 +99,17 @@ struct pa_alsa_ucm_verb {
};
struct pa_alsa_ucm_config {
+ pa_core *core;
snd_use_case_mgr_t *ucm_mgr;
pa_alsa_ucm_verb *active_verb;
PA_LLIST_HEAD(pa_alsa_ucm_verb, verbs);
+ PA_LLIST_HEAD(pa_alsa_jack, jacks);
};
struct pa_alsa_ucm_mapping_context {
pa_alsa_ucm_config *ucm;
+ int direction;
int ucm_devices_num;
pa_alsa_ucm_device **ucm_devices;
};
@@ -326,14 +326,22 @@ static void report_port_state(pa_device_port *p, struct userdata *u)
void *state;
pa_alsa_jack *jack;
pa_port_available_t pa = PA_PORT_AVAILABLE_UNKNOWN;
+ pa_device_port *port;
PA_HASHMAP_FOREACH(jack, u->jacks, state) {
pa_port_available_t cpa;
- if (!jack->path)
- continue;
+ if (u->use_ucm) {
+ port = pa_hashmap_get(u->card->ports, jack->name);
+ }
+ else {
+ if (jack->path)
+ port = jack->path->port;
+ else
+ continue;
+ }
- if (p != jack->path->port)
+ if (p != port)
continue;
cpa = jack->plugged_in ? jack->state_plugged : jack->state_unplugged;
@@ -359,6 +367,7 @@ static int report_jack_state(snd_hctl_elem_t *elem, unsigned int mask)
pa_bool_t plugged_in;
void *state;
pa_alsa_jack *jack;
+ pa_device_port *port;
pa_assert(u);
@@ -378,8 +387,16 @@ static int report_jack_state(snd_hctl_elem_t *elem, unsigned int mask)
PA_HASHMAP_FOREACH(jack, u->jacks, state)
if (jack->hctl_elem == elem) {
jack->plugged_in = plugged_in;
- pa_assert(jack->path && jack->path->port);
- report_port_state(jack->path->port, u);
+ if (u->use_ucm) {
+ pa_assert(u->card->ports);
+ port = pa_hashmap_get(u->card->ports, jack->name);
+ pa_assert(port);
+ }
+ else {
+ pa_assert(jack->path && jack->path->port);
+ port = jack->path->port;
+ }
+ report_port_state(port, u);
}
return 0;
}
@@ -391,18 +408,25 @@ static void init_jacks(struct userdata *u) {
u->jacks = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
- /* See if we have any jacks */
- if (u->profile_set->output_paths)
- PA_HASHMAP_FOREACH(path, u->profile_set->output_paths, state)
- PA_LLIST_FOREACH(jack, path->jacks)
+ if (u->use_ucm) {
+ PA_LLIST_FOREACH(jack, u->ucm.jacks)
+ if (jack->has_control)
+ pa_hashmap_put(u->jacks, jack, jack);
+ }
+ else {
+ /* See if we have any jacks */
+ if (u->profile_set->output_paths)
+ PA_HASHMAP_FOREACH(path, u->profile_set->output_paths, state)
+ PA_LLIST_FOREACH(jack, path->jacks)
if (jack->has_control)
pa_hashmap_put(u->jacks, jack, jack);
- if (u->profile_set->input_paths)
- PA_HASHMAP_FOREACH(path, u->profile_set->input_paths, state)
- PA_LLIST_FOREACH(jack, path->jacks)
+ if (u->profile_set->input_paths)
+ PA_HASHMAP_FOREACH(path, u->profile_set->input_paths, state)
+ PA_LLIST_FOREACH(jack, path->jacks)
if (jack->has_control)
pa_hashmap_put(u->jacks, jack, jack);
+ }
pa_log_debug("Found %d jacks.", pa_hashmap_size(u->jacks));
@@ -652,6 +676,8 @@ int pa__init(pa_module *m) {
u->device_id = pa_xstrdup(pa_modargs_get_value(ma, "device_id", DEFAULT_DEVICE_ID));
u->modargs = ma;
+ u->ucm.core = m->core;
+
if ((u->alsa_card_index = snd_card_get_index(u->device_id)) < 0) {
pa_log("Card '%s' doesn't exist: %s", u->device_id, pa_alsa_strerror(u->alsa_card_index));
goto fail;
@@ -881,8 +907,6 @@ void pa__done(pa_module*m) {
pa_alsa_source_free(s);
}
- ucm_free(&u->ucm);
-
if (u->card)
pa_card_free(u->card);
@@ -892,6 +916,8 @@ void pa__done(pa_module*m) {
if (u->profile_set)
pa_alsa_profile_set_free(u->profile_set);
+ ucm_free(&u->ucm);
+
pa_xfree(u->device_id);
pa_xfree(u);