mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-31 22:25:35 -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,19 +506,116 @@ 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++;
|
||||||
|
if (top_path)
|
||||||
|
snprintf(path, sizeof(path), "%s/%s/%s", e, top_path, id);
|
||||||
|
else
|
||||||
snprintf(path, sizeof(path), "%s/%s", e, id);
|
snprintf(path, sizeof(path), "%s/%s", e, id);
|
||||||
if (lstat64(path, &sb) != 0)
|
if (lstat64(path, &sb) != 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -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