/* Called from IO context */
static int suspend(struct userdata *u) {
+ const char *mod_name;
+
pa_assert(u);
pa_assert(u->pcm_handle);
snd_pcm_close(u->pcm_handle);
u->pcm_handle = NULL;
+ if ((mod_name = pa_proplist_gets(u->sink->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+ pa_log_info("Disable ucm modifier %s", mod_name);
+
+ if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_dismod", mod_name) < 0)
+ pa_log("Failed to disable ucm modifier %s", mod_name);
+ }
+
if (u->alsa_rtpoll_item) {
pa_rtpoll_item_free(u->alsa_rtpoll_item);
u->alsa_rtpoll_item = NULL;
pa_bool_t b, d;
snd_pcm_uframes_t period_size, buffer_size;
char *device_name = NULL;
+ const char *mod_name;
pa_assert(u);
pa_assert(!u->pcm_handle);
pa_log_info("Trying resume...");
+ if ((mod_name = pa_proplist_gets(u->sink->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+ pa_log_info("Enable ucm modifier %s", mod_name);
+
+ if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
+ pa_log("Failed to enable ucm modifier %s", mod_name);
+ }
+
if ((is_iec958(u) || is_hdmi(u)) && pa_sink_is_passthrough(u->sink)) {
/* Need to open device in NONAUDIO mode */
int len = strlen(u->device_name) + 8;
pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) {
struct userdata *u = NULL;
- const char *dev_id = NULL, *key;
+ const char *dev_id = NULL, *key, *mod_name;
pa_sample_spec ss;
uint32_t alternate_sample_rate;
pa_channel_map map;
goto fail;
}
+ if ((mod_name = pa_proplist_gets(mapping->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+ if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
+ pa_log("Failed to enable ucm modifier %s", mod_name);
+ else
+ pa_log_debug("Enabled ucm modifier %s", mod_name);
+ }
+
if (!(u->pcm_handle = pa_alsa_open_by_device_id_mapping(
dev_id,
&u->device_name,
/* Called from IO context */
static int suspend(struct userdata *u) {
+ const char *mod_name;
+
pa_assert(u);
pa_assert(u->pcm_handle);
snd_pcm_close(u->pcm_handle);
u->pcm_handle = NULL;
+ if ((mod_name = pa_proplist_gets(u->source->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+ pa_log_info("Disable ucm modifier %s", mod_name);
+
+ if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_dismod", mod_name) < 0)
+ pa_log("Failed to disable ucm modifier %s", mod_name);
+ }
+
if (u->alsa_rtpoll_item) {
pa_rtpoll_item_free(u->alsa_rtpoll_item);
u->alsa_rtpoll_item = NULL;
int err;
pa_bool_t b, d;
snd_pcm_uframes_t period_size, buffer_size;
+ const char *mod_name;
pa_assert(u);
pa_assert(!u->pcm_handle);
pa_log_info("Trying resume...");
+ if ((mod_name = pa_proplist_gets(u->source->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+ pa_log_info("Enable ucm modifier %s", mod_name);
+
+ if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
+ pa_log("Failed to enable ucm modifier %s", mod_name);
+ }
+
if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE,
SND_PCM_NONBLOCK|
SND_PCM_NO_AUTO_RESAMPLE|
pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping) {
struct userdata *u = NULL;
- const char *dev_id = NULL, *key;
+ const char *dev_id = NULL, *key, *mod_name;
pa_sample_spec ss;
uint32_t alternate_sample_rate;
pa_channel_map map;
goto fail;
}
+ if ((mod_name = pa_proplist_gets(mapping->proplist, PA_ALSA_PROP_UCM_MODIFIER))) {
+ if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0)
+ pa_log("Failed to enable ucm modifier %s", mod_name);
+ else
+ pa_log_debug("Enabled ucm modifier %s", mod_name);
+ }
+
if (!(u->pcm_handle = pa_alsa_open_by_device_id_mapping(
dev_id,
&u->device_name,
if (PA_UCM_PLAYBACK_PRIORITY_UNSET(device)) (device)->playback_priority = (priority); \
if (PA_UCM_CAPTURE_PRIORITY_UNSET(device)) (device)->capture_priority = (priority); \
} while (0)
+#define PA_UCM_IS_MODIFIER_MAPPING(m) ((pa_proplist_gets((m)->proplist, PA_ALSA_PROP_UCM_MODIFIER)) != NULL)
struct ucm_items {
const char *id;
device->capture_mapping = m;
}
+static void alsa_mapping_add_ucm_modifier(pa_alsa_mapping *m, pa_alsa_ucm_modifier *modifier) {
+ char *cur_desc;
+ const char *new_desc, *mod_name, *channel_str;
+ uint32_t channels = 0;
+
+ pa_idxset_put(m->ucm_context.ucm_modifiers, modifier, NULL);
+
+ new_desc = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_DESCRIPTION);
+ cur_desc = m->description;
+ if (cur_desc)
+ m->description = pa_sprintf_malloc("%s + %s", cur_desc, new_desc);
+ else
+ m->description = pa_xstrdup(new_desc);
+ pa_xfree(cur_desc);
+
+ if (!m->description)
+ pa_xstrdup("");
+
+ /* Modifier sinks should not be routed to by default */
+ m->priority = 0;
+
+ mod_name = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_NAME);
+ pa_proplist_sets(m->proplist, PA_ALSA_PROP_UCM_MODIFIER, mod_name);
+
+ /* save mapping to ucm modifier */
+ if (m->direction == PA_ALSA_DIRECTION_OUTPUT) {
+ modifier->playback_mapping = m;
+ channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS);
+ } else {
+ modifier->capture_mapping = m;
+ channel_str = pa_proplist_gets(modifier->proplist, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS);
+ }
+
+ if (channel_str) {
+ pa_assert_se(pa_atou(channel_str, &channels) == 0 && channels < PA_CHANNELS_MAX);
+ pa_log_debug("Got channel count %" PRIu32 " for modifier", channels);
+ }
+
+ if (channels)
+ pa_channel_map_init_extend(&m->channel_map, channels, PA_CHANNEL_MAP_ALSA);
+ else
+ pa_channel_map_init(&m->channel_map);
+}
+
static int ucm_create_mapping_direction(
pa_alsa_ucm_config *ucm,
pa_alsa_profile_set *ps,
return 0;
}
+static int ucm_create_mapping_for_modifier(
+ pa_alsa_ucm_config *ucm,
+ pa_alsa_profile_set *ps,
+ pa_alsa_profile *p,
+ pa_alsa_ucm_modifier *modifier,
+ const char *verb_name,
+ const char *mod_name,
+ const char *device_str,
+ bool is_sink) {
+
+ pa_alsa_mapping *m;
+ char *mapping_name;
+
+ mapping_name = pa_sprintf_malloc("Mapping %s: %s: %s", verb_name, device_str, is_sink ? "sink" : "source");
+
+ m = pa_alsa_mapping_get(ps, mapping_name);
+ if (!m) {
+ pa_log("no mapping for %s", mapping_name);
+ pa_xfree(mapping_name);
+ return -1;
+ }
+ pa_log_info("ucm mapping: %s modifier %s", mapping_name, mod_name);
+ pa_xfree(mapping_name);
+
+ if (!m->ucm_context.ucm_devices && !m->ucm_context.ucm_modifiers) { /* new mapping */
+ m->ucm_context.ucm_devices = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ m->ucm_context.ucm = ucm;
+ m->ucm_context.direction = is_sink ? PA_DIRECTION_OUTPUT : PA_DIRECTION_INPUT;
+
+ m->device_strings = pa_xnew0(char*, 2);
+ m->device_strings[0] = pa_xstrdup(device_str);
+ m->direction = is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT;
+ /* Modifier sinks should not be routed to by default */
+ m->priority = 0;
+
+ ucm_add_mapping(p, m);
+ } else if (!m->ucm_context.ucm_modifiers) /* share pcm with device */
+ m->ucm_context.ucm_modifiers = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+ alsa_mapping_add_ucm_modifier(m, modifier);
+
+ return 0;
+}
+
static int ucm_create_mapping(
pa_alsa_ucm_config *ucm,
pa_alsa_profile_set *ps,
pa_alsa_profile *p;
pa_alsa_ucm_device *dev;
+ pa_alsa_ucm_modifier *mod;
int i = 0;
const char *name, *sink, *source;
char *verb_cmp, *c;
dev->input_jack = ucm_get_jack(ucm, name, PA_UCM_PRE_TAG_INPUT);
}
+ /* Now find modifiers that have their own PlaybackPCM and create
+ * separate sinks for them. */
+ PA_LLIST_FOREACH(mod, verb->modifiers) {
+ name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME);
+
+ sink = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SINK);
+ source = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_SOURCE);
+
+ if (sink)
+ ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, sink, TRUE);
+ else if (source)
+ ucm_create_mapping_for_modifier(ucm, ps, p, mod, verb_name, name, source, FALSE);
+ }
+
pa_alsa_profile_dump(p);
return 0;
uint32_t idx;
PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
- if (!m->output_pcm)
- continue;
-
if (p->supported)
m->supported++;
+ if (!m->output_pcm)
+ continue;
+
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++;
+ if (!m->input_pcm)
+ continue;
+
snd_pcm_close(m->input_pcm);
m->input_pcm = NULL;
}
PA_HASHMAP_FOREACH(p, ps->profiles, state) {
/* change verb */
pa_log_info("Set ucm verb to %s", p->name);
+
if ((snd_use_case_set(ucm->ucm_mgr, "_verb", p->name)) < 0) {
pa_log("Failed to set verb %s", p->name);
p->supported = FALSE;
continue;
}
+
PA_IDXSET_FOREACH(m, p->output_mappings, idx) {
+ if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
+ /* Skip jack probing on modifier PCMs since we expect this to
+ * only be controlled on the main device/verb PCM. */
+ continue;
+ }
+
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) {
+ if (PA_UCM_IS_MODIFIER_MAPPING(m)) {
+ /* Skip jack probing on modifier PCMs since we expect this to
+ * only be controlled on the main device/verb PCM. */
+ continue;
+ }
+
m->input_pcm = mapping_open_pcm(ucm, m, SND_PCM_STREAM_CAPTURE);
if (!m->input_pcm) {
p->supported = FALSE;
pa_log_debug("Profile %s supported.", p->name);
PA_IDXSET_FOREACH(m, p->output_mappings, idx)
- ucm_mapping_jack_probe(m);
+ if (!PA_UCM_IS_MODIFIER_MAPPING(m))
+ ucm_mapping_jack_probe(m);
PA_IDXSET_FOREACH(m, p->input_mappings, idx)
- ucm_mapping_jack_probe(m);
+ if (!PA_UCM_IS_MODIFIER_MAPPING(m))
+ ucm_mapping_jack_probe(m);
profile_finalize_probing(p);
}
void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) {
pa_alsa_ucm_device *dev;
+ pa_alsa_ucm_modifier *mod;
uint32_t idx;
if (context->ucm_devices) {
}
if (context->ucm_modifiers) {
+ PA_IDXSET_FOREACH(mod, context->ucm_modifiers, idx) {
+ if (context->direction == PA_DIRECTION_OUTPUT)
+ mod->playback_mapping = NULL;
+ else
+ mod->capture_mapping = NULL;
+ }
+
pa_idxset_free(context->ucm_modifiers, NULL, NULL);
}
}