mirror of
				https://github.com/alsa-project/alsa-lib.git
				synced 2025-10-29 05:40:25 -04:00 
			
		
		
		
	ucm: add sys-card substitution
It may be useful to check additional sysfs parameters like USB descriptors
to determine the exact hardware capabilities.
Introduce 'sys-card' substitution and 'sys' substitution to allow data
fetching from given range. Also, add conversion to hexadecimal format
when the source file has binary contents.
Example - fetch bytes from positions 0x10..0x15 (6 bytes):
  Define.Bytes1 "${sys-card:[type=hex,pos=0x10,size=6]device/../descriptors}"
Example - fetch one byte from position 0x22:
  Define.Bytes2 "${sys-card:[type=hex,pos=0x22]device/../descriptors}"
Replace type=hex or omit this variable settings to work with ASCII
characters.
Link: https://github.com/alsa-project/alsa-ucm-conf/issues/444
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
			
			
This commit is contained in:
		
							parent
							
								
									0c7086777a
								
							
						
					
					
						commit
						7fbd47ce79
					
				
					 2 changed files with 153 additions and 7 deletions
				
			
		|  | @ -40,7 +40,7 @@ | ||||||
| #include <pthread.h> | #include <pthread.h> | ||||||
| #include "use-case.h" | #include "use-case.h" | ||||||
| 
 | 
 | ||||||
| #define SYNTAX_VERSION_MAX	7 | #define SYNTAX_VERSION_MAX	8 | ||||||
| 
 | 
 | ||||||
| #define MAX_CARD_SHORT_NAME	32 | #define MAX_CARD_SHORT_NAME	32 | ||||||
| #define MAX_CARD_LONG_NAME	80 | #define MAX_CARD_LONG_NAME	80 | ||||||
|  |  | ||||||
|  | @ -30,6 +30,8 @@ | ||||||
| #include <limits.h> | #include <limits.h> | ||||||
| #include <regex.h> | #include <regex.h> | ||||||
| 
 | 
 | ||||||
|  | static unsigned char _hex_table[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; | ||||||
|  | 
 | ||||||
| static char *rval_open_name(snd_use_case_mgr_t *uc_mgr) | static char *rval_open_name(snd_use_case_mgr_t *uc_mgr) | ||||||
| { | { | ||||||
| 	const char *name; | 	const char *name; | ||||||
|  | @ -504,20 +506,117 @@ static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *i | ||||||
| 	return NULL; | 	return NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id) | #define RANGE_TYPE_ASCII 0 | ||||||
|  | #define RANGE_TYPE_HEX   1 | ||||||
|  | 
 | ||||||
|  | static int parse_position(snd_config_t *config, const char *name, ssize_t *pos, bool optional) | ||||||
|  | { | ||||||
|  | 	snd_config_t *d; | ||||||
|  | 	const char *s; | ||||||
|  | 	long v; | ||||||
|  | 
 | ||||||
|  | 	if (snd_config_search(config, name, &d)) { | ||||||
|  | 		if (optional) { | ||||||
|  | 			*pos = -1; | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 		uc_error("Unable to find field '%s'", name); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	if (!snd_config_get_integer(d, &v)) | ||||||
|  | 		goto fin; | ||||||
|  | 	if (snd_config_get_string(d, &s)) | ||||||
|  | 		return -1; | ||||||
|  | 	if (safe_strtol(s, &v)) { | ||||||
|  | 		uc_error("Unable to parse position '%s'", s); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | fin: | ||||||
|  | 	*pos = v; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int parse_range(const char *cfg, int *type, ssize_t *pos, ssize_t *size) | ||||||
|  | { | ||||||
|  | 	snd_config_t *config, *d; | ||||||
|  | 	int err, retval = 0; | ||||||
|  | 	const char *s; | ||||||
|  | 
 | ||||||
|  | 	err = snd_config_load_string(&config, cfg, 0); | ||||||
|  | 	if (err < 0) { | ||||||
|  | 		uc_error("The range arguments '%s' are invalid", cfg); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	if (snd_config_search(config, "type", &d)) { | ||||||
|  | 		*type = RANGE_TYPE_ASCII; | ||||||
|  | 	} else { | ||||||
|  | 		if (snd_config_get_string(d, &s)) | ||||||
|  | 			goto null; | ||||||
|  | 		if (strcasecmp(s, "ascii") == 0) { | ||||||
|  | 			*type = RANGE_TYPE_ASCII; | ||||||
|  | 		} else if (strcasecmp(s, "hex") == 0) { | ||||||
|  | 			*type = RANGE_TYPE_HEX; | ||||||
|  | 		} else { | ||||||
|  | 			uc_error("Unknown range type '%s'", s); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	*pos = 0; | ||||||
|  | 	*size = -1; | ||||||
|  | 	if (parse_position(config, "pos", pos, false) || | ||||||
|  | 	    parse_position(config, "size", size, true)) { | ||||||
|  | 		retval = -1; | ||||||
|  | 		goto null; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (*size <= 0) | ||||||
|  | 		*size = 1; | ||||||
|  | 	if (*pos < 0) { | ||||||
|  | 		uc_error("Invalid start position"); | ||||||
|  | 		retval = -1; | ||||||
|  | 		goto null; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | null: | ||||||
|  | 	snd_config_delete(config); | ||||||
|  | 	return retval; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static char *rval_sysfs_main(snd_use_case_mgr_t *uc_mgr, const char *top_path, const char *id) | ||||||
| { | { | ||||||
| 	char path[PATH_MAX], link[PATH_MAX + 1]; | 	char path[PATH_MAX], link[PATH_MAX + 1]; | ||||||
| 	struct stat64 sb; | 	struct stat64 sb; | ||||||
| 	ssize_t len; | 	ssize_t len, range_start = -1, range_size = -1; | ||||||
| 	const char *e; | 	const char *e, *s; | ||||||
| 	int fd; | 	int fd, type = RANGE_TYPE_ASCII; | ||||||
| 
 | 
 | ||||||
| 	e = uc_mgr_sysfs_root(); | 	e = uc_mgr_sysfs_root(); | ||||||
| 	if (e == NULL) | 	if (e == NULL) | ||||||
| 		return NULL; | 		return NULL; | ||||||
|  | 	if (id[0] == '[') { | ||||||
|  | 		if (uc_mgr->conf_format < 8) { | ||||||
|  | 			uc_error("Sysfs ranges are supported in v8+ syntax"); | ||||||
|  | 			return NULL; | ||||||
|  | 		} | ||||||
|  | 		s = strchr(id, ']'); | ||||||
|  | 		if (s == NULL) | ||||||
|  | 			return NULL; | ||||||
|  | 		len = s - id - 1; | ||||||
|  | 		if ((size_t)(len - 1) > sizeof(link) - 1) | ||||||
|  | 			return NULL; | ||||||
|  | 		strncpy(link, id + 1, len); | ||||||
|  | 		link[len] = '\0'; | ||||||
|  | 		if (parse_range(link, &type, &range_start, &range_size)) { | ||||||
|  | 			uc_error("sysfs: cannot parse hex range '%s'", link); | ||||||
|  | 			return NULL; | ||||||
|  | 		} | ||||||
|  | 		id = s + 1; | ||||||
|  | 	} | ||||||
| 	if (id[0] == '/') | 	if (id[0] == '/') | ||||||
| 		id++; | 		id++; | ||||||
| 	snprintf(path, sizeof(path), "%s/%s", e, id); | 	if (top_path) | ||||||
|  | 		snprintf(path, sizeof(path), "%s/%s/%s", e, top_path, id); | ||||||
|  | 	else | ||||||
|  | 		snprintf(path, sizeof(path), "%s/%s", e, id); | ||||||
| 	if (lstat64(path, &sb) != 0) | 	if (lstat64(path, &sb) != 0) | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	if (S_ISLNK(sb.st_mode)) { | 	if (S_ISLNK(sb.st_mode)) { | ||||||
|  | @ -542,18 +641,64 @@ static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char | ||||||
| 		uc_error("sysfs open failed for '%s' (%d)", path, errno); | 		uc_error("sysfs open failed for '%s' (%d)", path, errno); | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
| 	len = read(fd, path, sizeof(path)-1); | 	len = sizeof(path) - 1; | ||||||
|  | 	if (range_start > 0 && lseek(fd, range_start, SEEK_SET) != range_start) { | ||||||
|  | 		uc_error("sysfs seek failed (%d)", errno); | ||||||
|  | 		close(fd); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	if (range_size > 0) { | ||||||
|  | 		if (range_size > len) { | ||||||
|  | 			uc_error("sysfs EOB for '%s'", path); | ||||||
|  | 			close(fd); | ||||||
|  | 			return NULL; | ||||||
|  | 		} else { | ||||||
|  | 			len = range_size; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	len = read(fd, path, len); | ||||||
| 	close(fd); | 	close(fd); | ||||||
| 	if (len < 0) { | 	if (len < 0) { | ||||||
| 		uc_error("sysfs unable to read value '%s' (%d)", path, errno); | 		uc_error("sysfs unable to read value '%s' (%d)", path, errno); | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
|  | 	if (type == RANGE_TYPE_HEX && range_start >= 0) { | ||||||
|  | 		char *m = malloc(len * 2 + 1); | ||||||
|  | 		ssize_t idx; | ||||||
|  | 		if (m == NULL) | ||||||
|  | 			return NULL; | ||||||
|  | 		for (idx = 0; idx < len; idx++) { | ||||||
|  | 			m[(idx * 2) + 0] = _hex_table[((unsigned char)path[idx]) >> 4]; | ||||||
|  | 			m[(idx * 2) + 1] = _hex_table[((unsigned char)path[idx]) & 0x0f]; | ||||||
|  | 		} | ||||||
|  | 		m[len * 2] = '\0'; | ||||||
|  | 		return m; | ||||||
|  | 	} | ||||||
| 	while (len > 0 && path[len-1] == '\n') | 	while (len > 0 && path[len-1] == '\n') | ||||||
| 		len--; | 		len--; | ||||||
| 	path[len] = '\0'; | 	path[len] = '\0'; | ||||||
| 	return strdup(path); | 	return strdup(path); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr, const char *id) | ||||||
|  | { | ||||||
|  | 	return rval_sysfs_main(uc_mgr, NULL, id); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static char *rval_sysfs_card(snd_use_case_mgr_t *uc_mgr, const char *id) | ||||||
|  | { | ||||||
|  | 	char top_path[32], *s; | ||||||
|  | 
 | ||||||
|  | 	if (uc_mgr->conf_format < 8) { | ||||||
|  | 		uc_error("sys-card is supported in v8+ syntax"); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	s = get_card_number(uc_mgr_get_master_ctl(uc_mgr)); | ||||||
|  | 	snprintf(top_path, sizeof(top_path), "class/sound/card%s", s); | ||||||
|  | 	free(s); | ||||||
|  | 	return rval_sysfs_main(uc_mgr, top_path, id); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static char *rval_var(snd_use_case_mgr_t *uc_mgr, const char *id) | static char *rval_var(snd_use_case_mgr_t *uc_mgr, const char *id) | ||||||
| { | { | ||||||
| 	const char *v; | 	const char *v; | ||||||
|  | @ -751,6 +896,7 @@ __std: | ||||||
| 		MATCH_VARIABLE(value, "${CardComponents}", rval_card_components, true); | 		MATCH_VARIABLE(value, "${CardComponents}", rval_card_components, true); | ||||||
| 		MATCH_VARIABLE2(value, "${env:", rval_env, false); | 		MATCH_VARIABLE2(value, "${env:", rval_env, false); | ||||||
| 		MATCH_VARIABLE2(value, "${sys:", rval_sysfs, false); | 		MATCH_VARIABLE2(value, "${sys:", rval_sysfs, false); | ||||||
|  | 		MATCH_VARIABLE2(value, "${sys-card:", rval_sysfs_card, false); | ||||||
| 		MATCH_VARIABLE2(value, "${var:", rval_var, true); | 		MATCH_VARIABLE2(value, "${var:", rval_var, true); | ||||||
| 		MATCH_VARIABLE2(value, "${eval:", rval_eval, false); | 		MATCH_VARIABLE2(value, "${eval:", rval_eval, false); | ||||||
| 		MATCH_VARIABLE2(value, "${find-card:", rval_card_lookup, false); | 		MATCH_VARIABLE2(value, "${find-card:", rval_card_lookup, false); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jaroslav Kysela
						Jaroslav Kysela