mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-28 05:40:23 -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 "use-case.h"
|
||||
|
||||
#define SYNTAX_VERSION_MAX 7
|
||||
#define SYNTAX_VERSION_MAX 8
|
||||
|
||||
#define MAX_CARD_SHORT_NAME 32
|
||||
#define MAX_CARD_LONG_NAME 80
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
#include <limits.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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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];
|
||||
struct stat64 sb;
|
||||
ssize_t len;
|
||||
const char *e;
|
||||
int fd;
|
||||
ssize_t len, range_start = -1, range_size = -1;
|
||||
const char *e, *s;
|
||||
int fd, type = RANGE_TYPE_ASCII;
|
||||
|
||||
e = uc_mgr_sysfs_root();
|
||||
if (e == 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] == '/')
|
||||
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)
|
||||
return NULL;
|
||||
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);
|
||||
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);
|
||||
if (len < 0) {
|
||||
uc_error("sysfs unable to read value '%s' (%d)", path, errno);
|
||||
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')
|
||||
len--;
|
||||
path[len] = '\0';
|
||||
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)
|
||||
{
|
||||
const char *v;
|
||||
|
|
@ -751,6 +896,7 @@ __std:
|
|||
MATCH_VARIABLE(value, "${CardComponents}", rval_card_components, true);
|
||||
MATCH_VARIABLE2(value, "${env:", rval_env, 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, "${eval:", rval_eval, false);
|
||||
MATCH_VARIABLE2(value, "${find-card:", rval_card_lookup, false);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue