/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include <alsa/asoundlib.h> #include <alsa/use-case.h> #include <ctype.h> #include <string.h> #include <syslog.h> #include "cras_alsa_ucm.h" #include "utlist.h" static const char jack_var[] = "JackName"; static const char jack_type_var[] = "JackType"; static const char jack_switch_var[] = "JackSwitch"; static const char edid_var[] = "EDIDFile"; static const char cap_var[] = "CaptureControl"; static const char mic_positions[] = "MicPositions"; static const char override_type_name_var[] = "OverrideNodeType"; static const char output_dsp_name_var[] = "OutputDspName"; static const char input_dsp_name_var[] = "InputDspName"; static const char mixer_var[] = "MixerName"; static const char swap_mode_suffix[] = "Swap Mode"; static const char min_buffer_level_var[] = "MinBufferLevel"; static const char dma_period_var[] = "DmaPeriodMicrosecs"; static const char disable_software_volume[] = "DisableSoftwareVolume"; static const char playback_device_name_var[] = "PlaybackPCM"; static const char playback_device_rate_var[] = "PlaybackRate"; static const char capture_device_name_var[] = "CapturePCM"; static const char capture_device_rate_var[] = "CaptureRate"; static const char capture_channel_map_var[] = "CaptureChannelMap"; static const char coupled_mixers[] = "CoupledMixers"; /* Set this value in a SectionDevice to specify the maximum software gain in dBm * and enable software gain on this node. */ static const char max_software_gain[] = "MaxSoftwareGain"; /* Set this value in a SectionDevice to specify the default node gain in dBm. */ static const char default_node_gain[] = "DefaultNodeGain"; static const char hotword_model_prefix[] = "Hotword Model"; static const char fully_specified_ucm_var[] = "FullySpecifiedUCM"; static const char main_volume_names[] = "MainVolumeNames"; static const char enable_htimestamp_var[] = "EnableHtimestamp"; /* Use case verbs corresponding to CRAS_STREAM_TYPE. */ static const char *use_case_verbs[] = { "HiFi", "Multimedia", "Voice Call", "Speech", "Pro Audio", }; /* Represents a list of section names found in UCM. */ struct section_name { const char* name; struct section_name *prev, *next; }; struct cras_use_case_mgr { snd_use_case_mgr_t *mgr; const char *name; unsigned int avail_use_cases; enum CRAS_STREAM_TYPE use_case; }; static inline const char *uc_verb(struct cras_use_case_mgr *mgr) { return use_case_verbs[mgr->use_case]; } static int device_enabled(struct cras_use_case_mgr *mgr, const char *dev) { const char **list; unsigned int i; int num_devs; int enabled = 0; num_devs = snd_use_case_get_list(mgr->mgr, "_enadevs", &list); if (num_devs <= 0) return 0; for (i = 0; i < (unsigned int)num_devs; i++) if (!strcmp(dev, list[i])) { enabled = 1; break; } snd_use_case_free_list(list, num_devs); return enabled; } static int modifier_enabled(struct cras_use_case_mgr *mgr, const char *mod) { const char **list; unsigned int mod_idx; int num_mods; num_mods = snd_use_case_get_list(mgr->mgr, "_enamods", &list); if (num_mods <= 0) return 0; for (mod_idx = 0; mod_idx < (unsigned int)num_mods; mod_idx++) if (!strcmp(mod, list[mod_idx])) break; snd_use_case_free_list(list, num_mods); return (mod_idx < (unsigned int)num_mods); } static int get_var(struct cras_use_case_mgr *mgr, const char *var, const char *dev, const char *verb, const char **value) { char *id; int rc; size_t len = strlen(var) + strlen(dev) + strlen(verb) + 4; id = (char *)malloc(len); if (!id) return -ENOMEM; snprintf(id, len, "=%s/%s/%s", var, dev, verb); rc = snd_use_case_get(mgr->mgr, id, value); free((void *)id); return rc; } static int get_int(struct cras_use_case_mgr *mgr, const char *var, const char *dev, const char *verb, int *value) { const char *str_value; int rc; if (!value) return -EINVAL; rc = get_var(mgr, var, dev, verb, &str_value); if (rc != 0) return rc; *value = atoi(str_value); free((void *)str_value); return 0; } static int ucm_set_modifier_enabled(struct cras_use_case_mgr *mgr, const char *mod, int enable) { return snd_use_case_set(mgr->mgr, enable ? "_enamod" : "_dismod", mod); } static int ucm_str_ends_with_suffix(const char *str, const char *suffix) { if (!str || !suffix) return 0; size_t len_str = strlen(str); size_t len_suffix = strlen(suffix); if (len_suffix > len_str) return 0; return strncmp(str + len_str - len_suffix, suffix, len_suffix) == 0; } static int ucm_section_exists_with_name(struct cras_use_case_mgr *mgr, const char *name, const char *identifier) { const char **list; unsigned int i; int num_entries; int exist = 0; num_entries = snd_use_case_get_list(mgr->mgr, identifier, &list); if (num_entries <= 0) return 0; for (i = 0; i < (unsigned int)num_entries; i+=2) { if (!list[i]) continue; if (strcmp(list[i], name) == 0) { exist = 1; break; } } snd_use_case_free_list(list, num_entries); return exist; } static int ucm_section_exists_with_suffix(struct cras_use_case_mgr *mgr, const char *suffix, const char *identifier) { const char **list; unsigned int i; int num_entries; int exist = 0; num_entries = snd_use_case_get_list(mgr->mgr, identifier, &list); if (num_entries <= 0) return 0; for (i = 0; i < (unsigned int)num_entries; i+=2) { if (!list[i]) continue; if (ucm_str_ends_with_suffix(list[i], suffix)) { exist = 1; break; } } snd_use_case_free_list(list, num_entries); return exist; } static int ucm_mod_exists_with_suffix(struct cras_use_case_mgr *mgr, const char *suffix) { char *identifier; int rc; identifier = snd_use_case_identifier("_modifiers/%s", uc_verb(mgr)); rc = ucm_section_exists_with_suffix(mgr, suffix, identifier); free(identifier); return rc; } static int ucm_mod_exists_with_name(struct cras_use_case_mgr *mgr, const char *name) { char *identifier; int rc; identifier = snd_use_case_identifier("_modifiers/%s", uc_verb(mgr)); rc = ucm_section_exists_with_name(mgr, name, identifier); free(identifier); return rc; } /* Get a list of section names whose variable is the matched value. */ static struct section_name * ucm_get_sections_for_var( struct cras_use_case_mgr *mgr, const char *var, const char *value, const char *identifier, enum CRAS_STREAM_DIRECTION direction) { const char **list; struct section_name *section_names = NULL, *s_name; unsigned int i; int num_entries; int rc; num_entries = snd_use_case_get_list(mgr->mgr, identifier, &list); if (num_entries <= 0) return NULL; /* snd_use_case_get_list fills list with pairs of device name and * comment, so device names are in even-indexed elements. */ for (i = 0; i < (unsigned int)num_entries; i+=2) { const char *this_value; if (!list[i]) continue; rc = get_var(mgr, var, list[i], uc_verb(mgr), &this_value); if (rc) continue; if (!strcmp(value, this_value)) { s_name = (struct section_name *)malloc( sizeof(struct section_name)); if (!s_name) { syslog(LOG_ERR, "Failed to allocate memory"); free((void *)this_value); break; } s_name->name = strdup(list[i]); DL_APPEND(section_names, s_name); } free((void *)this_value); } snd_use_case_free_list(list, num_entries); return section_names; } static struct section_name *ucm_get_devices_for_var( struct cras_use_case_mgr *mgr, const char *var, const char *value, enum CRAS_STREAM_DIRECTION dir) { char *identifier; struct section_name *section_names; identifier = snd_use_case_identifier("_devices/%s", uc_verb(mgr)); section_names = ucm_get_sections_for_var(mgr, var, value, identifier, dir); free(identifier); return section_names; } static const char *ucm_get_playback_device_name_for_dev( struct cras_use_case_mgr *mgr, const char *dev) { const char *name = NULL; int rc; rc = get_var(mgr, playback_device_name_var, dev, uc_verb(mgr), &name); if (rc) return NULL; return name; } static const char *ucm_get_capture_device_name_for_dev( struct cras_use_case_mgr *mgr, const char *dev) { const char *name = NULL; int rc; rc = get_var(mgr, capture_device_name_var, dev, uc_verb(mgr), &name); if (rc) return NULL; return name; } /* Get a list of mixer names specified in a UCM variable separated by ",". * E.g. "Left Playback,Right Playback". */ static struct mixer_name *ucm_get_mixer_names(struct cras_use_case_mgr *mgr, const char *dev, const char* var, enum CRAS_STREAM_DIRECTION dir, mixer_name_type type) { const char *names_in_string = NULL; int rc; char *tokens, *name, *laststr; struct mixer_name *names = NULL; rc = get_var(mgr, var, dev, uc_verb(mgr), &names_in_string); if (rc) return NULL; tokens = strdup(names_in_string); name = strtok_r(tokens, ",", &laststr); while (name != NULL) { names = mixer_name_add(names, name, dir, type); name = strtok_r(NULL, ",", &laststr); } free((void*)names_in_string); free(tokens); return names; } /* Exported Interface */ struct cras_use_case_mgr *ucm_create(const char *name) { struct cras_use_case_mgr *mgr; int rc; const char **list; int num_verbs, i, j; if (!name) return NULL; mgr = (struct cras_use_case_mgr *)malloc(sizeof(*mgr)); if (!mgr) return NULL; rc = snd_use_case_mgr_open(&mgr->mgr, name); if (rc) { syslog(LOG_WARNING, "Can not open ucm for card %s, rc = %d", name, rc); goto cleanup; } mgr->name = name; mgr->avail_use_cases = 0; num_verbs = snd_use_case_get_list(mgr->mgr, "_verbs", &list); for (i = 0; i < num_verbs; i += 2) { for (j = 0; j < CRAS_STREAM_NUM_TYPES; ++j) { if (strcmp(list[i], use_case_verbs[j]) == 0) break; } if (j < CRAS_STREAM_NUM_TYPES) mgr->avail_use_cases |= (1 << j); } if (num_verbs > 0) snd_use_case_free_list(list, num_verbs); rc = ucm_set_use_case(mgr, CRAS_STREAM_TYPE_DEFAULT); if (rc) goto cleanup_mgr; return mgr; cleanup_mgr: snd_use_case_mgr_close(mgr->mgr); cleanup: free(mgr); return NULL; } void ucm_destroy(struct cras_use_case_mgr *mgr) { snd_use_case_mgr_close(mgr->mgr); free(mgr); } int ucm_set_use_case(struct cras_use_case_mgr *mgr, enum CRAS_STREAM_TYPE use_case) { int rc; if (mgr->avail_use_cases & (1 << use_case)) { mgr->use_case = use_case; } else { syslog(LOG_ERR, "Unavailable use case %d for card %s", use_case, mgr->name); return -1; } rc = snd_use_case_set(mgr->mgr, "_verb", uc_verb(mgr)); if (rc) { syslog(LOG_ERR, "Can not set verb %s for card %s, rc = %d", uc_verb(mgr), mgr->name, rc); return rc; } return 0; } int ucm_swap_mode_exists(struct cras_use_case_mgr *mgr) { return ucm_mod_exists_with_suffix(mgr, swap_mode_suffix); } int ucm_enable_swap_mode(struct cras_use_case_mgr *mgr, const char *node_name, int enable) { char *swap_mod = NULL; int rc; size_t len = strlen(node_name) + 1 + strlen(swap_mode_suffix) + 1; swap_mod = (char *)malloc(len); if (!swap_mod) return -ENOMEM; snprintf(swap_mod, len, "%s %s", node_name, swap_mode_suffix); if (!ucm_mod_exists_with_name(mgr, swap_mod)) { syslog(LOG_ERR, "Can not find swap mode modifier %s.", swap_mod); free((void *)swap_mod); return -EPERM; } if (modifier_enabled(mgr, swap_mod) == !!enable) { free((void *)swap_mod); return 0; } rc = ucm_set_modifier_enabled(mgr, swap_mod, enable); free((void *)swap_mod); return rc; } int ucm_set_enabled(struct cras_use_case_mgr *mgr, const char *dev, int enable) { if (device_enabled(mgr, dev) == !!enable) return 0; syslog(LOG_DEBUG, "UCM %s %s", enable ? "enable" : "disable", dev); return snd_use_case_set(mgr->mgr, enable ? "_enadev" : "_disdev", dev); } char *ucm_get_flag(struct cras_use_case_mgr *mgr, const char *flag_name) { char *flag_value = NULL; const char *value; int rc; /* Set device to empty string since flag is specified in verb section */ rc = get_var(mgr, flag_name, "", uc_verb(mgr), &value); if (!rc) { flag_value = strdup(value); free((void *)value); } return flag_value; } char *ucm_get_cap_control(struct cras_use_case_mgr *mgr, const char *ucm_dev) { char *control_name = NULL; const char *value; int rc; rc = get_var(mgr, cap_var, ucm_dev, uc_verb(mgr), &value); if (!rc) { control_name = strdup(value); free((void *)value); } return control_name; } char *ucm_get_mic_positions(struct cras_use_case_mgr *mgr) { char *control_name = NULL; const char *value; int rc; rc = get_var(mgr, mic_positions, "", uc_verb(mgr), &value); if (!rc) { control_name = strdup(value); free((void *)value); } return control_name; } const char *ucm_get_override_type_name(struct cras_use_case_mgr *mgr, const char *dev) { const char *override_type_name; int rc; rc = get_var(mgr, override_type_name_var, dev, uc_verb(mgr), &override_type_name); if (rc) return NULL; return override_type_name; } char *ucm_get_dev_for_jack(struct cras_use_case_mgr *mgr, const char *jack, enum CRAS_STREAM_DIRECTION direction) { struct section_name *section_names, *c; char *ret = NULL; section_names = ucm_get_devices_for_var(mgr, jack_var, jack, direction); DL_FOREACH(section_names, c) { if (!strcmp(c->name, "Mic")) { /* Skip mic section for output */ if (direction == CRAS_STREAM_OUTPUT) continue; } else { /* Only check mic for input. */ if (direction == CRAS_STREAM_INPUT) continue; } ret = strdup(c->name); break; } DL_FOREACH(section_names, c) { DL_DELETE(section_names, c); free((void*)c->name); free(c); } return ret; } char *ucm_get_dev_for_mixer(struct cras_use_case_mgr *mgr, const char *mixer, enum CRAS_STREAM_DIRECTION dir) { struct section_name *section_names, *c; char *ret = NULL; section_names = ucm_get_devices_for_var(mgr, mixer_var, mixer, dir); if (section_names) ret = strdup(section_names->name); DL_FOREACH(section_names, c) { DL_DELETE(section_names, c); free((void*)c->name); free(c); } return ret; } const char *ucm_get_edid_file_for_dev(struct cras_use_case_mgr *mgr, const char *dev) { const char *file_name; int rc; rc = get_var(mgr, edid_var, dev, uc_verb(mgr), &file_name); if (rc) return NULL; return file_name; } const char *ucm_get_dsp_name(struct cras_use_case_mgr *mgr, const char *ucm_dev, int direction) { const char *var = (direction == CRAS_STREAM_OUTPUT) ? output_dsp_name_var : input_dsp_name_var; const char *dsp_name = NULL; int rc; rc = get_var(mgr, var, ucm_dev, uc_verb(mgr), &dsp_name); if (rc) return NULL; return dsp_name; } const char *ucm_get_dsp_name_default(struct cras_use_case_mgr *mgr, int direction) { return ucm_get_dsp_name(mgr, "", direction); } unsigned int ucm_get_min_buffer_level(struct cras_use_case_mgr *mgr) { int value; int rc; rc = get_int(mgr, min_buffer_level_var, "", uc_verb(mgr), &value); if (rc) return 0; return value; } unsigned int ucm_get_disable_software_volume(struct cras_use_case_mgr *mgr) { int value; int rc; rc = get_int(mgr, disable_software_volume, "", uc_verb(mgr), &value); if (rc) return 0; return value; } int ucm_get_max_software_gain(struct cras_use_case_mgr *mgr, const char *dev, long *gain) { int value; int rc; rc = get_int(mgr, max_software_gain, dev, uc_verb(mgr), &value); if (rc) return rc; *gain = value; return 0; } int ucm_get_default_node_gain(struct cras_use_case_mgr *mgr, const char *dev, long *gain) { int value; int rc; rc = get_int(mgr, default_node_gain, dev, uc_verb(mgr), &value); if (rc) return rc; *gain = value; return 0; } const char *ucm_get_device_name_for_dev( struct cras_use_case_mgr *mgr, const char *dev, enum CRAS_STREAM_DIRECTION direction) { if (direction == CRAS_STREAM_OUTPUT) return ucm_get_playback_device_name_for_dev(mgr, dev); else if (direction == CRAS_STREAM_INPUT) return ucm_get_capture_device_name_for_dev(mgr, dev); return NULL; } int ucm_get_sample_rate_for_dev(struct cras_use_case_mgr *mgr, const char *dev, enum CRAS_STREAM_DIRECTION direction) { int value; int rc; const char *var_name; if (direction == CRAS_STREAM_OUTPUT) var_name = playback_device_rate_var; else if (direction == CRAS_STREAM_INPUT) var_name = capture_device_rate_var; else return -EINVAL; rc = get_int(mgr, var_name, dev, uc_verb(mgr), &value); if (rc) return rc; return value; } int ucm_get_capture_chmap_for_dev(struct cras_use_case_mgr *mgr, const char *dev, int8_t *channel_layout) { const char *var_str; char *tokens, *token; int i, rc; rc = get_var(mgr, capture_channel_map_var, dev, uc_verb(mgr), &var_str); if (rc) return rc; tokens = strdup(var_str); token = strtok(tokens, " "); for (i = 0; token && (i < CRAS_CH_MAX); i++) { channel_layout[i] = atoi(token); token = strtok(NULL, " "); } free((void *)tokens); free((void *)var_str); return (i == CRAS_CH_MAX) ? 0 : -EINVAL; } struct mixer_name *ucm_get_coupled_mixer_names( struct cras_use_case_mgr *mgr, const char *dev) { return ucm_get_mixer_names(mgr, dev, coupled_mixers, CRAS_STREAM_OUTPUT, MIXER_NAME_VOLUME); } static int get_device_index_from_target(const char *target_device_name) { /* Expects a string in the form: hw:card-name,<num> */ const char *pos = target_device_name; if (!pos) return -1; while (*pos && *pos != ',') ++pos; if (*pos == ',') { ++pos; return atoi(pos); } return -1; } struct ucm_section *ucm_get_sections(struct cras_use_case_mgr *mgr) { struct ucm_section *sections = NULL; struct ucm_section *dev_sec; const char **list; int num_devs; int i; char *identifier; /* Find the list of all mixers using the control names defined in * the header definintion for this function. */ identifier = snd_use_case_identifier("_devices/%s", uc_verb(mgr)); num_devs = snd_use_case_get_list(mgr->mgr, identifier, &list); free(identifier); /* snd_use_case_get_list fills list with pairs of device name and * comment, so device names are in even-indexed elements. */ for (i = 0; i < num_devs; i += 2) { enum CRAS_STREAM_DIRECTION dir = CRAS_STREAM_UNDEFINED; int dev_idx = -1; const char *dev_name = strdup(list[i]); const char *jack_name; const char *jack_type; const char *mixer_name; struct mixer_name *m_name; int rc; const char *target_device_name; if (!dev_name) continue; target_device_name = ucm_get_playback_device_name_for_dev(mgr, dev_name); if (target_device_name) dir = CRAS_STREAM_OUTPUT; else { target_device_name = ucm_get_capture_device_name_for_dev( mgr, dev_name); if (target_device_name) dir = CRAS_STREAM_INPUT; } if (target_device_name) { dev_idx = get_device_index_from_target( target_device_name); free((void *)target_device_name); } if (dir == CRAS_STREAM_UNDEFINED) { syslog(LOG_ERR, "UCM configuration for device '%s' missing" " PlaybackPCM or CapturePCM definition.", dev_name); goto error_cleanup; } if (dev_idx == -1) { syslog(LOG_ERR, "PlaybackPCM or CapturePCM for '%s' must be in" " the form 'hw:<card>,<number>'", dev_name); goto error_cleanup; } jack_name = ucm_get_jack_name_for_dev(mgr, dev_name); jack_type = ucm_get_jack_type_for_dev(mgr, dev_name); mixer_name = ucm_get_mixer_name_for_dev(mgr, dev_name); dev_sec = ucm_section_create(dev_name, dev_idx, dir, jack_name, jack_type); if (jack_name) free((void *)jack_name); if (jack_type) free((void *)jack_type); if (!dev_sec) { syslog(LOG_ERR, "Failed to allocate memory."); if (mixer_name) free((void *)mixer_name); goto error_cleanup; } dev_sec->jack_switch = ucm_get_jack_switch_for_dev(mgr, dev_name); if (mixer_name) { rc = ucm_section_set_mixer_name(dev_sec, mixer_name); free((void *)mixer_name); if (rc) goto error_cleanup; } m_name = ucm_get_mixer_names(mgr, dev_name, coupled_mixers, dir, MIXER_NAME_VOLUME); ucm_section_concat_coupled(dev_sec, m_name); DL_APPEND(sections, dev_sec); ucm_section_dump(dev_sec); } if (num_devs > 0) snd_use_case_free_list(list, num_devs); return sections; error_cleanup: if (num_devs > 0) snd_use_case_free_list(list, num_devs); ucm_section_free_list(sections); return NULL; } char *ucm_get_hotword_models(struct cras_use_case_mgr *mgr) { const char **list; int i, num_entries; int models_len = 0; char *models = NULL; const char *tmp; char *identifier; identifier = snd_use_case_identifier("_modifiers/%s", uc_verb(mgr)); num_entries = snd_use_case_get_list(mgr->mgr, identifier, &list); free(identifier); if (num_entries <= 0) return 0; models = (char *)malloc(num_entries * 8); for (i = 0; i < num_entries; i+=2) { if (!list[i]) continue; if (0 == strncmp(list[i], hotword_model_prefix, strlen(hotword_model_prefix))) { tmp = list[i] + strlen(hotword_model_prefix); while (isspace(*tmp)) tmp++; strcpy(models + models_len, tmp); models_len += strlen(tmp); if (i + 2 >= num_entries) models[models_len] = '\0'; else models[models_len++] = ','; } } snd_use_case_free_list(list, num_entries); return models; } int ucm_set_hotword_model(struct cras_use_case_mgr *mgr, const char *model) { const char **list; int num_enmods, mod_idx; char *model_mod = NULL; size_t model_mod_size = strlen(model) + 1 + strlen(hotword_model_prefix) + 1; model_mod = (char *)malloc(model_mod_size); if (!model_mod) return -ENOMEM; snprintf(model_mod, model_mod_size, "%s %s", hotword_model_prefix, model); if (!ucm_mod_exists_with_name(mgr, model_mod)) { free((void *)model_mod); return -EINVAL; } /* Disable all currently enabled horword model modifiers. */ num_enmods = snd_use_case_get_list(mgr->mgr, "_enamods", &list); if (num_enmods <= 0) goto enable_mod; for (mod_idx = 0; mod_idx < num_enmods; mod_idx++) { if (!strncmp(list[mod_idx], hotword_model_prefix, strlen(hotword_model_prefix))) ucm_set_modifier_enabled(mgr, list[mod_idx], 0); } snd_use_case_free_list(list, num_enmods); enable_mod: ucm_set_modifier_enabled(mgr, model_mod, 1); return 0; } int ucm_has_fully_specified_ucm_flag(struct cras_use_case_mgr *mgr) { char *flag; int ret = 0; flag = ucm_get_flag(mgr, fully_specified_ucm_var); if (!flag) return 0; ret = !strcmp(flag, "1"); free(flag); return ret; } const char *ucm_get_mixer_name_for_dev(struct cras_use_case_mgr *mgr, const char *dev) { const char *name = NULL; int rc; rc = get_var(mgr, mixer_var, dev, uc_verb(mgr), &name); if (rc) return NULL; return name; } struct mixer_name *ucm_get_main_volume_names(struct cras_use_case_mgr *mgr) { return ucm_get_mixer_names(mgr, "", main_volume_names, CRAS_STREAM_OUTPUT, MIXER_NAME_MAIN_VOLUME); } int ucm_list_section_devices_by_device_name( struct cras_use_case_mgr *mgr, enum CRAS_STREAM_DIRECTION direction, const char *device_name, ucm_list_section_devices_callback cb, void *cb_arg) { int listed= 0; struct section_name *section_names, *c; const char* var; char *identifier; if (direction == CRAS_STREAM_OUTPUT) var = playback_device_name_var; else if (direction == CRAS_STREAM_INPUT) var = capture_device_name_var; else return 0; identifier = snd_use_case_identifier("_devices/%s", uc_verb(mgr)); section_names = ucm_get_sections_for_var( mgr, var, device_name, identifier, direction); free(identifier); if (!section_names) return 0; DL_FOREACH(section_names, c) { cb(c->name, cb_arg); listed++; } DL_FOREACH(section_names, c) { DL_DELETE(section_names, c); free((void*)c->name); free(c); } return listed; } const char *ucm_get_jack_name_for_dev(struct cras_use_case_mgr *mgr, const char *dev) { const char *name = NULL; int rc; rc = get_var(mgr, jack_var, dev, uc_verb(mgr), &name); if (rc) return NULL; return name; } const char *ucm_get_jack_type_for_dev(struct cras_use_case_mgr *mgr, const char *dev) { const char *name = NULL; int rc; rc = get_var(mgr, jack_type_var, dev, uc_verb(mgr), &name); if (rc) return NULL; if (strcmp(name, "hctl") && strcmp(name, "gpio")) { syslog(LOG_ERR, "Unknown jack type: %s", name); return NULL; } return name; } int ucm_get_jack_switch_for_dev(struct cras_use_case_mgr *mgr, const char *dev) { int value; int rc = get_int(mgr, jack_switch_var, dev, uc_verb(mgr), &value); if (rc || value < 0) return -1; return value; } unsigned int ucm_get_dma_period_for_dev(struct cras_use_case_mgr *mgr, const char *dev) { int value; int rc = get_int(mgr, dma_period_var, dev, uc_verb(mgr), &value); if (rc || value < 0) return 0; return value; } unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr *mgr) { char *flag; int ret = 0; flag = ucm_get_flag(mgr, enable_htimestamp_var); if (!flag) return 0; ret = !strcmp(flag, "1"); free(flag); return ret; }