@@ -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);
+ pa_ucm_mapping_context_free(&m->ucm_context);
pa_xfree(m);
}
@@ -4450,17 +4450,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);
@@ -4495,6 +4485,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);
@@ -584,6 +584,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;
@@ -910,8 +911,8 @@ static int ucm_create_mapping_direction(pa_alsa_ucm_config *ucm,
priority = is_sink ? device->playback_priority : device->capture_priority;
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);
@@ -952,6 +953,24 @@ static int ucm_create_mapping(pa_alsa_ucm_config *ucm,
return ret;
}
+static pa_alsa_jack* ucm_get_jack(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(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps,
pa_alsa_ucm_verb *verb, const char *verb_name, const char *verb_desc) {
pa_alsa_profile *p;
@@ -1007,12 +1026,56 @@ static int ucm_create_profile(pa_alsa_ucm_config *ucm, pa_alsa_profile_set *ps,
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(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 pa_alsa_ucm_device *find_ucm_dev(pa_alsa_ucm_verb *verb, const char *dev_name) {
pa_alsa_ucm_device *dev;
@@ -1025,6 +1088,81 @@ static pa_alsa_ucm_device *find_ucm_dev(pa_alsa_ucm_verb *verb, const char *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;
+ 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(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* pa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map) {
pa_alsa_ucm_verb *verb;
pa_alsa_profile_set *ps;
@@ -1048,6 +1186,8 @@ pa_alsa_profile_set* pa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_
ucm_create_profile(ucm, ps, verb, verb_name, verb_desc);
}
+
+ ucm_probe_jacks(ucm, ps);
ps->probed = TRUE;
return ps;
@@ -1083,17 +1223,40 @@ static void free_verb(pa_alsa_ucm_verb *verb) {
void pa_ucm_free(pa_alsa_ucm_config *ucm) {
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);
ucm->ucm_mgr = NULL;
}
}
+void pa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
+ 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 (pa_alsa_ucm_verb *verb,
pa_alsa_ucm_modifier *mod, const char *mapping_name) {
int i;
@@ -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;
typedef struct pa_alsa_ucm_modifier pa_alsa_ucm_modifier;
@@ -34,6 +36,7 @@ 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;
pa_alsa_profile_set* pa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map);
int pa_ucm_set_profile(pa_alsa_ucm_config *ucm, const char *new_profile, const char *old_profile);
@@ -48,6 +51,7 @@ void pa_ucm_add_ports_combination(pa_hashmap *hash, pa_alsa_ucm_mapping_context
int pa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, int is_sink);
void pa_ucm_free(pa_alsa_ucm_config *ucm);
+void pa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context);
void pa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, const char *mapping_name, int is_sink);
void pa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, const char *mapping_name, int is_sink);
@@ -74,6 +78,7 @@ struct pa_alsa_ucm_device {
int n_suppdev;
char **conflicting_devices;
char **supported_devices;
+ pa_alsa_jack *jack;
};
struct pa_alsa_ucm_modifier {
@@ -98,14 +103,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,21 @@ 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 +366,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 +386,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 +407,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 (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 (jack->has_control)
- pa_hashmap_put(u->jacks, jack, jack);
+ 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 (jack->has_control)
+ pa_hashmap_put(u->jacks, jack, jack);
+ }
pa_log_debug("Found %d jacks.", pa_hashmap_size(u->jacks));
@@ -651,6 +674,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;
@@ -880,8 +905,6 @@ void pa__done(pa_module*m) {
pa_alsa_source_free(s);
}
- pa_ucm_free(&u->ucm);
-
if (u->card)
pa_card_free(u->card);
@@ -891,6 +914,8 @@ void pa__done(pa_module*m) {
if (u->profile_set)
pa_alsa_profile_set_free(u->profile_set);
+ pa_ucm_free(&u->ucm);
+
pa_xfree(u->device_id);
pa_xfree(u);