mirror of
				https://github.com/alsa-project/alsa-lib.git
				synced 2025-10-29 05:40:25 -04:00 
			
		
		
		
	ucm: add LibraryConfig support
This commit allows to define private alsa-lib's configuration. When
the configuration is present, the device values ("PlaybackCTL",
"CaptureCTL", "PlaybackMixer", "CaptureMixer", "CapturePCM")
are prefixed with '_ucmHEXA.' string where HEXA is replaced by the
unique hexadecimal number identifying the opened ucm manager handle.
    Syntax 4
    LibraryConfig.a_label.SubstiConfig {
            # substituted library configuration like:
            usr_share_dir "${ConfLibDir}"
    }
    LibraryConfig.b_label.Config {
            # non-substituted library configuration like:
            usr_share_dir "/usr/share/alsa"
    }
    The File counterparts:
    LibraryConfig.c_label.SubstiFile "/some/path"
    LibraryConfig.d_label.File "/some/path"
Note that for files the contents is substituted on the request,
but the file name is always substituted (useful for ${ConfDir} etc.).
The private configuration is not saved or preserved. It's life time
belongs to the opened ucm manager handle.
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
			
			
This commit is contained in:
		
							parent
							
								
									3e0140088c
								
							
						
					
					
						commit
						8f5779eb3f
					
				
					 11 changed files with 400 additions and 36 deletions
				
			
		|  | @ -374,4 +374,11 @@ int _snd_config_load_with_include(snd_config_t *config, snd_input_t *in, | |||
| void *INTERNAL(snd_dlopen)(const char *name, int mode, char *errbuf, size_t errbuflen); | ||||
| #endif | ||||
| 
 | ||||
| const char *uc_mgr_alibcfg_by_device(snd_config_t **config, const char *name); | ||||
| 
 | ||||
| static inline int _snd_is_ucm_device(const char *name) | ||||
| { | ||||
| 	return name && name[0] == '_' && name[1] == 'u' && name[2] == 'c' && name[3] == 'm'; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -257,6 +257,7 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, | |||
|  *   - NULL 		- return current card | ||||
|  *   - _verb		- return current verb | ||||
|  *   - _file		- return configuration file loaded for current card | ||||
|  *   - _alibcfg		- return private alsa-lib's configuration for current card | ||||
|  * | ||||
|  *   - [=]{NAME}[/[{modifier}|{/device}][/{verb}]] | ||||
|  *                      - value identifier {NAME} | ||||
|  |  | |||
|  | @ -1520,9 +1520,15 @@ int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode) | |||
| 	int err; | ||||
| 
 | ||||
| 	assert(ctlp && name); | ||||
| 	err = snd_config_update_ref(&top); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	if (_snd_is_ucm_device(name)) { | ||||
| 		name = uc_mgr_alibcfg_by_device(&top, name); | ||||
| 		if (name == NULL) | ||||
| 			return -ENODEV; | ||||
| 	} else { | ||||
| 		err = snd_config_update_ref(&top); | ||||
| 		if (err < 0) | ||||
| 			return err; | ||||
| 	} | ||||
| 	err = snd_ctl_open_noupdate(ctlp, top, name, mode, 0); | ||||
| 	snd_config_unref(top); | ||||
| 	return err; | ||||
|  |  | |||
|  | @ -2686,9 +2686,15 @@ int snd_pcm_open(snd_pcm_t **pcmp, const char *name, | |||
| 	int err; | ||||
| 
 | ||||
| 	assert(pcmp && name); | ||||
| 	err = snd_config_update_ref(&top); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	if (_snd_is_ucm_device(name)) { | ||||
| 		name = uc_mgr_alibcfg_by_device(&top, name); | ||||
| 		if (name == NULL) | ||||
| 			return -ENODEV; | ||||
| 	} else { | ||||
| 		err = snd_config_update_ref(&top); | ||||
| 		if (err < 0) | ||||
| 			return err; | ||||
| 	} | ||||
| 	err = snd_pcm_open_noupdate(pcmp, top, name, stream, mode, 0); | ||||
| 	snd_config_unref(top); | ||||
| 	return err; | ||||
|  |  | |||
|  | @ -304,9 +304,15 @@ int snd_rawmidi_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, | |||
| 	int err; | ||||
| 
 | ||||
| 	assert((inputp || outputp) && name); | ||||
| 	err = snd_config_update_ref(&top); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	if (_snd_is_ucm_device(name)) { | ||||
| 		name = uc_mgr_alibcfg_by_device(&top, name); | ||||
| 		if (name == NULL) | ||||
| 			return -ENODEV; | ||||
| 	} else { | ||||
| 		err = snd_config_update_ref(&top); | ||||
| 		if (err < 0) | ||||
| 			return err; | ||||
| 	} | ||||
| 	err = snd_rawmidi_open_noupdate(inputp, outputp, top, name, mode); | ||||
| 	snd_config_unref(top); | ||||
| 	return err; | ||||
|  |  | |||
|  | @ -978,9 +978,15 @@ int snd_seq_open(snd_seq_t **seqp, const char *name, | |||
| 	int err; | ||||
| 
 | ||||
| 	assert(seqp && name); | ||||
| 	err = snd_config_update_ref(&top); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	if (_snd_is_ucm_device(name)) { | ||||
| 		name = uc_mgr_alibcfg_by_device(&top, name); | ||||
| 		if (name == NULL) | ||||
| 			return -ENODEV; | ||||
| 	} else { | ||||
| 		err = snd_config_update_ref(&top); | ||||
| 		if (err < 0) | ||||
| 			return err; | ||||
| 	} | ||||
| 	err = snd_seq_open_noupdate(seqp, top, name, streams, mode, 0); | ||||
| 	snd_config_unref(top); | ||||
| 	return err; | ||||
|  |  | |||
|  | @ -205,9 +205,15 @@ int snd_timer_open(snd_timer_t **timer, const char *name, int mode) | |||
| 	int err; | ||||
| 
 | ||||
| 	assert(timer && name); | ||||
| 	err = snd_config_update_ref(&top); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	if (_snd_is_ucm_device(name)) { | ||||
| 		name = uc_mgr_alibcfg_by_device(&top, name); | ||||
| 		if (name == NULL) | ||||
| 			return -ENODEV; | ||||
| 	} else { | ||||
| 		err = snd_config_update_ref(&top); | ||||
| 		if (err < 0) | ||||
| 			return err; | ||||
| 	} | ||||
| 	err = snd_timer_open_noupdate(timer, top, name, mode); | ||||
| 	snd_config_unref(top); | ||||
| 	return err; | ||||
|  |  | |||
|  | @ -570,6 +570,38 @@ static int execute_sysw(const char *sysw) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int rewrite_device_value(snd_use_case_mgr_t *uc_mgr, const char *name, char **value) | ||||
| { | ||||
| 	char *sval; | ||||
| 	size_t l; | ||||
| 	static const char **s, *_prefix[] = { | ||||
| 		"PlaybackCTL", | ||||
| 		"CaptureCTL", | ||||
| 		"PlaybackMixer", | ||||
| 		"CaptureMixer", | ||||
| 		"PlaybackPCM", | ||||
| 		"CapturePCM", | ||||
| 		NULL | ||||
| 	}; | ||||
| 
 | ||||
| 	for (s = _prefix; *s && *value; s++) { | ||||
| 		if (strcmp(*s, name) != 0) | ||||
| 			continue; | ||||
| 		l = strlen(*value) + 9 + 1; | ||||
| 		sval = malloc(l); | ||||
| 		if (sval == NULL) { | ||||
| 			free(*value); | ||||
| 			*value = NULL; | ||||
| 			return -ENOMEM; | ||||
| 		} | ||||
| 		snprintf(sval, l, "_ucm%04X.%s", uc_mgr->ucm_card_number, *value); | ||||
| 		free(*value); | ||||
| 		*value = sval; | ||||
| 		break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * \brief Execute the sequence | ||||
|  * \param uc_mgr Use case manager | ||||
|  | @ -596,6 +628,8 @@ static int execute_sequence(snd_use_case_mgr_t *uc_mgr, | |||
| 			cdev = strdup(s->data.cdev); | ||||
| 			if (cdev == NULL) | ||||
| 				goto __fail_nomem; | ||||
| 			if (rewrite_device_value(uc_mgr, "PlaybackCTL", &cdev)) | ||||
| 				goto __fail_nomem; | ||||
| 			break; | ||||
| 		case SEQUENCE_ELEMENT_TYPE_CSET: | ||||
| 		case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE: | ||||
|  | @ -1259,10 +1293,18 @@ int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr, | |||
| 	INIT_LIST_HEAD(&mgr->variable_list); | ||||
| 	pthread_mutex_init(&mgr->mutex, NULL); | ||||
| 
 | ||||
| 	err = uc_mgr_card_open(mgr); | ||||
| 	if (err < 0) | ||||
| 		goto _err; | ||||
| 
 | ||||
| 	err = snd_config_top(&mgr->local_config); | ||||
| 	if (err < 0) | ||||
| 		goto _err; | ||||
| 
 | ||||
| 	mgr->card_name = strdup(card_name); | ||||
| 	if (mgr->card_name == NULL) { | ||||
| 		free(mgr); | ||||
| 		return -ENOMEM; | ||||
| 		err = -ENOMEM; | ||||
| 		goto _err; | ||||
| 	} | ||||
| 
 | ||||
| 	/* get info on use_cases and verify against card */ | ||||
|  | @ -1321,6 +1363,7 @@ int snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr) | |||
|  */ | ||||
| int snd_use_case_mgr_close(snd_use_case_mgr_t *uc_mgr) | ||||
| { | ||||
| 	uc_mgr_card_close(uc_mgr); | ||||
| 	uc_mgr_free(uc_mgr); | ||||
| 
 | ||||
| 	return 0; | ||||
|  | @ -1868,6 +1911,7 @@ static int get_value1(snd_use_case_mgr_t *uc_mgr, char **value, | |||
| { | ||||
| 	struct ucm_value *val; | ||||
| 	struct list_head *pos; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!value_list) | ||||
| 		return -ENOENT; | ||||
|  | @ -1881,7 +1925,10 @@ static int get_value1(snd_use_case_mgr_t *uc_mgr, char **value, | |||
| 					return -ENOMEM; | ||||
| 				return 0; | ||||
| 			} | ||||
| 			return uc_mgr_get_substituted_value(uc_mgr, value, val->data); | ||||
| 			err = uc_mgr_get_substituted_value(uc_mgr, value, val->data); | ||||
| 			if (err < 0) | ||||
| 				return err; | ||||
| 			return rewrite_device_value(uc_mgr, val->name, value); | ||||
| 		} | ||||
| 	} | ||||
| 	return -ENOENT; | ||||
|  | @ -1976,6 +2023,31 @@ static int get_value(snd_use_case_mgr_t *uc_mgr, | |||
| 	return -ENOENT; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * \brief Get private alsa-lib configuration (ASCII) | ||||
|  * \param uc_mgr Use case manager | ||||
|  * \param str Returned value string | ||||
|  * \return Zero on success (value is filled), otherwise a negative error code | ||||
|  */ | ||||
| static int get_alibcfg(snd_use_case_mgr_t *uc_mgr, char **str) | ||||
| { | ||||
| 	snd_output_t *out; | ||||
| 	size_t size; | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = snd_output_buffer_open(&out); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	err = snd_config_save(uc_mgr->local_config, out); | ||||
| 	if (err >= 0) { | ||||
| 		size = snd_output_buffer_steal(out, str); | ||||
| 		if (*str) | ||||
| 			(*str)[size] = '\0'; | ||||
| 	} | ||||
| 	snd_output_close(out); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * \brief Get current - string | ||||
|  * \param uc_mgr Use case manager | ||||
|  | @ -2029,9 +2101,10 @@ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, | |||
| 		} | ||||
| 		err = 0; | ||||
| 
 | ||||
| 	} else if (strcmp(identifier, "_alibcfg") == 0) { | ||||
| 		err = get_alibcfg(uc_mgr, (char **)value); | ||||
| 	} else if (identifier[0] == '_') { | ||||
| 		err = -ENOENT; | ||||
| 		goto __end; | ||||
| 	} else { | ||||
| 		if (identifier[0] == '=') { | ||||
| 			exact = 1; | ||||
|  |  | |||
							
								
								
									
										145
									
								
								src/ucm/parser.c
									
										
									
									
									
								
							
							
						
						
									
										145
									
								
								src/ucm/parser.c
									
										
									
									
									
								
							|  | @ -31,6 +31,7 @@ | |||
|  */ | ||||
| 
 | ||||
| #include "ucm_local.h" | ||||
| #include <stdbool.h> | ||||
| #include <dirent.h> | ||||
| #include <limits.h> | ||||
| 
 | ||||
|  | @ -420,6 +421,128 @@ int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr, | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Parse one item for alsa-lib config | ||||
|  */ | ||||
| static int parse_libconfig1(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) | ||||
| { | ||||
| 	snd_config_iterator_t i, next; | ||||
| 	snd_config_t *n, *config = NULL; | ||||
| 	const char *id, *file = NULL; | ||||
| 	bool substfile = false, substconfig = false; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (snd_config_get_id(cfg, &id) < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { | ||||
| 		uc_error("compound type expected for %s", id); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	snd_config_for_each(i, next, cfg) { | ||||
| 		n = snd_config_iterator_entry(i); | ||||
| 
 | ||||
| 		if (snd_config_get_id(n, &id) < 0) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		if (strcmp(id, "File") == 0 || | ||||
| 		    strcmp(id, "SubstiFile") == 0) { | ||||
| 			substfile = id[0] == 'S'; | ||||
| 			err = snd_config_get_string(n, &file); | ||||
| 			if (err < 0) | ||||
| 				return err; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (strcmp(id, "Config") == 0 || | ||||
| 		    strcmp(id, "SubstiConfig") == 0) { | ||||
| 			substconfig = id[0] == 'S'; | ||||
| 			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) | ||||
| 				return -EINVAL; | ||||
| 			config = n; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		uc_error("unknown field %s", id); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (file) { | ||||
| 		if (substfile) { | ||||
| 			snd_config_t *cfg; | ||||
| 			err = uc_mgr_config_load(uc_mgr->conf_format, file, &cfg); | ||||
| 			if (err < 0) | ||||
| 				return err; | ||||
| 			err = uc_mgr_substitute_tree(uc_mgr, cfg); | ||||
| 			if (err < 0) { | ||||
| 				snd_config_delete(config); | ||||
| 				return err; | ||||
| 			} | ||||
| 			err = snd_config_merge(uc_mgr->local_config, cfg, 1); | ||||
| 			if (err < 0) { | ||||
| 				snd_config_delete(cfg); | ||||
| 				return err; | ||||
| 			} | ||||
| 		} else { | ||||
| 			char filename[PATH_MAX]; | ||||
| 
 | ||||
| 			ucm_filename(filename, sizeof(filename), uc_mgr->conf_format, | ||||
| 				     file[0] == '/' ? NULL : uc_mgr->conf_dir_name, | ||||
| 				     file); | ||||
| 			err = uc_mgr_config_load_into(uc_mgr->conf_format, filename, uc_mgr->local_config); | ||||
| 			if (err < 0) | ||||
| 				return err; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (config) { | ||||
| 		if (substconfig) { | ||||
| 			err = uc_mgr_substitute_tree(uc_mgr, config); | ||||
| 			if (err < 0) { | ||||
| 				snd_config_delete(config); | ||||
| 				return err; | ||||
| 			} | ||||
| 		} | ||||
| 		err = snd_config_merge(uc_mgr->local_config, config, 1); | ||||
| 		if (err < 0) { | ||||
| 			snd_config_delete(config); | ||||
| 			return err; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Parse alsa-lib config | ||||
|  */ | ||||
| static int parse_libconfig(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) | ||||
| { | ||||
| 	snd_config_iterator_t i, next; | ||||
| 	snd_config_t *n; | ||||
| 	const char *id; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (snd_config_get_id(cfg, &id) < 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { | ||||
| 		uc_error("compound type expected for %s", id); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	snd_config_for_each(i, next, cfg) { | ||||
| 		n = snd_config_iterator_entry(i); | ||||
| 
 | ||||
| 		err = parse_libconfig1(uc_mgr, n); | ||||
| 		if (err < 0) | ||||
| 			return err; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Parse transition | ||||
|  */ | ||||
|  | @ -1644,6 +1767,7 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, | |||
| 						file); | ||||
| 				goto _err; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		/* device remove */ | ||||
|  | @ -1654,6 +1778,17 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, | |||
| 						file); | ||||
| 				goto _err; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		/* alsa-lib configuration */ | ||||
| 		if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) { | ||||
| 			err = parse_libconfig(uc_mgr, n); | ||||
| 			if (err < 0) { | ||||
| 				uc_error("error: failed to parse LibConfig"); | ||||
| 				return err; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1962,6 +2097,16 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) | |||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		/* alsa-lib configuration */ | ||||
| 		if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) { | ||||
| 			err = parse_libconfig(uc_mgr, n); | ||||
| 			if (err < 0) { | ||||
| 				uc_error("error: failed to parse LibraryConfig"); | ||||
| 				return err; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		/* error */ | ||||
| 		if (strcmp(id, "Error") == 0) | ||||
| 			return error_node(uc_mgr, n); | ||||
|  |  | |||
|  | @ -222,6 +222,10 @@ struct snd_use_case_mgr { | |||
| 	char *conf_dir_name; | ||||
| 	char *comment; | ||||
| 	int conf_format; | ||||
| 	unsigned int ucm_card_number; | ||||
| 
 | ||||
| 	/* UCM cards list */ | ||||
| 	struct list_head cards_list; | ||||
| 
 | ||||
| 	/* use case verb, devices and modifier configs parsed from files */ | ||||
| 	struct list_head verb_list; | ||||
|  | @ -253,6 +257,9 @@ struct snd_use_case_mgr { | |||
| 	/* list of opened control devices */ | ||||
| 	struct list_head ctl_list; | ||||
| 
 | ||||
| 	/* local library configuration */ | ||||
| 	snd_config_t *local_config; | ||||
| 
 | ||||
| 	/* Components don't define cdev, the card device. When executing
 | ||||
| 	 * a sequence of a component device, ucm manager enters component | ||||
| 	 * domain and needs to provide cdev to the component. This cdev | ||||
|  | @ -275,6 +282,7 @@ void uc_mgr_stdout(const char *fmt, ...); | |||
| 
 | ||||
| const char *uc_mgr_sysfs_root(void); | ||||
| const char *uc_mgr_config_dir(int format); | ||||
| int uc_mgr_config_load_into(int format, const char *file, snd_config_t *cfg); | ||||
| int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg); | ||||
| int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr,  const char *file, snd_config_t **cfg); | ||||
| int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr); | ||||
|  | @ -291,6 +299,14 @@ void uc_mgr_free_transition_element(struct transition_sequence *seq); | |||
| void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr); | ||||
| void uc_mgr_free(snd_use_case_mgr_t *uc_mgr); | ||||
| 
 | ||||
| static inline int uc_mgr_has_local_config(snd_use_case_mgr_t *uc_mgr) | ||||
| { | ||||
| 	return uc_mgr && snd_config_iterator_first(uc_mgr->local_config); | ||||
| } | ||||
| 
 | ||||
| int uc_mgr_card_open(snd_use_case_mgr_t *uc_mgr); | ||||
| void uc_mgr_card_close(snd_use_case_mgr_t *uc_mgr); | ||||
| 
 | ||||
| int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr, | ||||
| 		    struct ctl_list **ctl_list, | ||||
| 		    const char *device, | ||||
|  |  | |||
							
								
								
									
										126
									
								
								src/ucm/utils.c
									
										
									
									
									
								
							
							
						
						
									
										126
									
								
								src/ucm/utils.c
									
										
									
									
									
								
							|  | @ -341,49 +341,54 @@ const char *uc_mgr_config_dir(int format) | |||
| 	return path; | ||||
| } | ||||
| 
 | ||||
| int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg) | ||||
| int uc_mgr_config_load_into(int format, const char *file, snd_config_t *top) | ||||
| { | ||||
| 	FILE *fp; | ||||
| 	snd_input_t *in; | ||||
| 	snd_config_t *top; | ||||
| 	const char *default_paths[2]; | ||||
| 	int err; | ||||
| 
 | ||||
| 	fp = fopen(file, "r"); | ||||
| 	if (!fp) { | ||||
| 		err = -errno; | ||||
|   __err0: | ||||
|   __err_open: | ||||
| 		uc_error("could not open configuration file %s", file); | ||||
| 		return err; | ||||
| 	} | ||||
| 	err = snd_input_stdio_attach(&in, fp, 1); | ||||
| 	if (err < 0) | ||||
| 		goto __err0; | ||||
| 	err = snd_config_top(&top); | ||||
| 	if (err < 0) | ||||
| 		goto __err1; | ||||
| 		goto __err_open; | ||||
| 
 | ||||
| 	default_paths[0] = uc_mgr_config_dir(format); | ||||
| 	default_paths[1] = NULL; | ||||
| 	err = _snd_config_load_with_include(top, in, 0, default_paths); | ||||
| 	if (err < 0) { | ||||
| 		uc_error("could not load configuration file %s", file); | ||||
| 		goto __err2; | ||||
| 		if (in) | ||||
| 			snd_input_close(in); | ||||
| 		return err; | ||||
| 	} | ||||
| 	err = snd_input_close(in); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg) | ||||
| { | ||||
| 	snd_config_t *top; | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = snd_config_top(&top); | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	err = uc_mgr_config_load_into(format, file, top); | ||||
| 	if (err < 0) { | ||||
| 		in = NULL; | ||||
| 		goto __err2; | ||||
| 		snd_config_delete(top); | ||||
| 		return err; | ||||
| 	} | ||||
| 	*cfg = top; | ||||
| 	return 0; | ||||
| 
 | ||||
|  __err2: | ||||
| 	snd_config_delete(top); | ||||
|  __err1: | ||||
| 	if (in) | ||||
| 		snd_input_close(in); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| void uc_mgr_free_value(struct list_head *base) | ||||
|  | @ -725,8 +730,95 @@ void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr) | |||
| 
 | ||||
| void uc_mgr_free(snd_use_case_mgr_t *uc_mgr) | ||||
| { | ||||
| 	snd_config_delete(uc_mgr->local_config); | ||||
| 	uc_mgr_free_verb(uc_mgr); | ||||
| 	uc_mgr_free_ctl_list(uc_mgr); | ||||
| 	free(uc_mgr->card_name); | ||||
| 	free(uc_mgr); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * UCM card list stuff | ||||
|  */ | ||||
| 
 | ||||
| static pthread_mutex_t ucm_cards_mutex = PTHREAD_MUTEX_INITIALIZER; | ||||
| static LIST_HEAD(ucm_cards); | ||||
| static unsigned int ucm_card_assign; | ||||
| 
 | ||||
| static snd_use_case_mgr_t *uc_mgr_card_find(unsigned int card_number) | ||||
| { | ||||
| 	struct list_head *pos; | ||||
| 	snd_use_case_mgr_t *uc_mgr; | ||||
| 
 | ||||
| 	list_for_each(pos, &ucm_cards) { | ||||
| 		uc_mgr = list_entry(pos, snd_use_case_mgr_t, cards_list); | ||||
| 		if (uc_mgr->ucm_card_number == card_number) | ||||
| 			return uc_mgr; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| int uc_mgr_card_open(snd_use_case_mgr_t *uc_mgr) | ||||
| { | ||||
| 	unsigned int prev; | ||||
| 
 | ||||
| 	pthread_mutex_lock(&ucm_cards_mutex); | ||||
| 	prev = ucm_card_assign++; | ||||
| 	while (uc_mgr_card_find(ucm_card_assign)) { | ||||
| 		ucm_card_assign++; | ||||
| 		ucm_card_assign &= 0xffff; | ||||
| 		if (ucm_card_assign == prev) { | ||||
| 			pthread_mutex_unlock(&ucm_cards_mutex); | ||||
| 			return -ENOMEM; | ||||
| 		} | ||||
| 	} | ||||
| 	uc_mgr->ucm_card_number = ucm_card_assign; | ||||
| 	list_add(&uc_mgr->cards_list, &ucm_cards); | ||||
| 	pthread_mutex_unlock(&ucm_cards_mutex); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void uc_mgr_card_close(snd_use_case_mgr_t *uc_mgr) | ||||
| { | ||||
| 	pthread_mutex_lock(&ucm_cards_mutex); | ||||
| 	list_del(&uc_mgr->cards_list); | ||||
| 	pthread_mutex_unlock(&ucm_cards_mutex); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * \brief Get library configuration based on the private ALSA device name | ||||
|  * \param name[in] ALSA device name | ||||
|  * \retval config A configuration tree or NULL | ||||
|  * | ||||
|  * The returned configuration (non-NULL) should be unreferenced using | ||||
|  * snd_config_unref() call. | ||||
|  */ | ||||
| const char *uc_mgr_alibcfg_by_device(snd_config_t **top, const char *name) | ||||
| { | ||||
| 	char buf[5]; | ||||
| 	long card_num; | ||||
| 	snd_config_t *config; | ||||
| 	snd_use_case_mgr_t *uc_mgr; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (strncmp(name, "_ucm", 4) || strlen(name) < 12 || name[8] != '.') | ||||
| 		return NULL; | ||||
| 	strncpy(buf, name + 4, 4); | ||||
| 	buf[4] = '\0'; | ||||
| 	err = safe_strtol(buf, &card_num); | ||||
| 	if (err < 0 || card_num < 0 || card_num > 0xffff) | ||||
| 		return NULL; | ||||
| 	config = NULL; | ||||
| 	pthread_mutex_lock(&ucm_cards_mutex); | ||||
| 	uc_mgr = uc_mgr_card_find(card_num); | ||||
| 	/* non-empty configs are accepted only */ | ||||
| 	if (uc_mgr_has_local_config(uc_mgr)) { | ||||
| 		config = uc_mgr->local_config; | ||||
| 		snd_config_ref(config); | ||||
| 	} | ||||
| 	pthread_mutex_unlock(&ucm_cards_mutex); | ||||
| 	if (!config) | ||||
| 		return NULL; | ||||
| 	*top = config; | ||||
| 	return name + 9; | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jaroslav Kysela
						Jaroslav Kysela