[PATCH alsa-utils] amixer: implement handling of TLV bytes kcontrols

SND_SOC_BYTES_EXT kcontrols are deprecated and SND_SOC_BYTES_TLV should be used instead. However, there is no support of these kcontrols in amixer.
This commit introduces a mechanism to show and write the content of SND_SOC_BYTES_TLV-based kcontrols.
Example:
$ amixer cset name="Test" 0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0xa0 $ amixer cget name="Test" numid=42,iface=MIXER,name='Test' ; type=BYTES,access=-----RW-,values=10 : values=0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0xa0
Signed-off-by: Jan Dakinevich jan.dakinevich@salutedevices.com --- amixer/amixer.c | 180 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 141 insertions(+), 39 deletions(-)
diff --git a/amixer/amixer.c b/amixer/amixer.c index 07e9819e5cdb..09f774fe2885 100644 --- a/amixer/amixer.c +++ b/amixer/amixer.c @@ -609,6 +609,8 @@ static int show_control(const char *space, snd_hctl_elem_t *elem, { int err; unsigned int item, idx, count, *tlv; + uint8_t *tlv_bytes = NULL; + unsigned int tlv_bytes_count = 0; snd_ctl_elem_type_t type; snd_ctl_elem_id_t *id; snd_ctl_elem_info_t *info; @@ -628,6 +630,18 @@ static int show_control(const char *space, snd_hctl_elem_t *elem, } count = snd_ctl_elem_info_get_count(info); type = snd_ctl_elem_info_get_type(info); + if (type == SND_CTL_ELEM_TYPE_BYTES && + !snd_ctl_elem_info_is_readable(info) && + !snd_ctl_elem_info_is_writable(info)) { + if (count <= 2 * sizeof(unsigned int)) + return -EINVAL; + tlv_bytes_count = count; + count -= 2 * sizeof(unsigned int); + + tlv_bytes = malloc(tlv_bytes_count); + if (!tlv_bytes) + return -ENOMEM; + } printf("%s; type=%s,access=%s,values=%u", space, control_type(info), control_access(info), count); switch (type) { case SND_CTL_ELEM_TYPE_INTEGER: @@ -661,16 +675,30 @@ static int show_control(const char *space, snd_hctl_elem_t *elem, break; } if (level & LEVEL_BASIC) { - if (!snd_ctl_elem_info_is_readable(info)) + if (snd_ctl_elem_info_is_readable(info)) { + err = snd_hctl_elem_read(elem, control); + if (err < 0) { + error("Control %s element read error: %s\n", card, snd_strerror(err)); + return err; + } + } else if (tlv_bytes) { + err = snd_hctl_elem_tlv_read(elem, (void *)tlv_bytes, tlv_bytes_count); + if (err < 0) { + error("Control %s element TLV read error: %s\n", card, snd_strerror(err)); + free(tlv_bytes); + return err; + } + } else goto __skip_read; - if ((err = snd_hctl_elem_read(elem, control)) < 0) { - error("Control %s element read error: %s\n", card, snd_strerror(err)); - return err; - } + printf("%s: values=", space); for (idx = 0; idx < count; idx++) { if (idx > 0) printf(","); + if (tlv_bytes) { + printf("0x%02x", tlv_bytes[idx + 2 * sizeof(unsigned int)]); + continue; + } switch (type) { case SND_CTL_ELEM_TYPE_BOOLEAN: printf("%s", snd_ctl_elem_value_get_boolean(control, idx) ? "on" : "off"); @@ -699,17 +727,11 @@ static int show_control(const char *space, snd_hctl_elem_t *elem, } } printf("\n"); + if (tlv_bytes) + goto __skip_tlv; __skip_read: if (!snd_ctl_elem_info_is_tlv_readable(info)) goto __skip_tlv; - /* skip ASoC ext bytes controls that may have huge binary TLV data */ - if (type == SND_CTL_ELEM_TYPE_BYTES && - !snd_ctl_elem_info_is_readable(info) && - !snd_ctl_elem_info_is_writable(info)) { - printf("%s; ASoC TLV Byte control, skipping bytes dump\n", space); - goto __skip_tlv; - } - tlv = malloc(4096); if ((err = snd_hctl_elem_tlv_read(elem, tlv, 4096)) < 0) { error("Control %s element TLV read error: %s\n", card, snd_strerror(err)); @@ -720,6 +742,7 @@ static int show_control(const char *space, snd_hctl_elem_t *elem, free(tlv); } __skip_tlv: + free(tlv_bytes); return 0; }
@@ -1133,16 +1156,110 @@ static int parse_simple_id(const char *str, snd_mixer_selem_id_t *sid) return 0; }
+static int parse_bytes(const char *str, void *ptr, unsigned int len) +{ + char *p; + unsigned long tmp; + unsigned int idx = 0; + unsigned char *data = ptr; + + for (;;) { + if (!*str) + break; + if (idx == len) + return -EINVAL; + + tmp = strtoul(str, &p, 0); + if (p - str < 1) + return -EINVAL; + if (*p == ',') + str = p + 1; + else if (*p == '\0') + str = p; + else + return -EINVAL; + + if (tmp > 255) + return -EINVAL; + + data[idx] = tmp; + idx++; + } + + if (!idx) + return -EINVAL; + + return idx; +} + +static int cset_tlv(int argc, char *argv[], snd_ctl_t *handle, + snd_ctl_elem_info_t *info, snd_ctl_elem_id_t *id) +{ + int err; + unsigned int count; + unsigned int *tlv = NULL; + + count = snd_ctl_elem_info_get_count(info); + if (count <= 2 * sizeof(unsigned int)) { + err = -EINVAL; + goto out; + } + tlv = malloc(count); + if (!tlv) { + err = -ENOMEM; + goto out; + } + err = parse_bytes(argv[1], tlv + 2, + count - 2 * sizeof(unsigned int)); + if (err < 0) + goto out; + tlv[0] = 0; + tlv[1] = err; + + err = snd_ctl_elem_tlv_write(handle, id, tlv); + +out: + free(tlv); + return err; +} + +static int cset_elem(int argc, char *argv[], snd_ctl_t *handle, + snd_ctl_elem_info_t *info, snd_ctl_elem_id_t *id) +{ + int err; + snd_ctl_elem_value_t *control; + snd_ctl_elem_value_alloca(&control); + + snd_ctl_elem_value_set_id(control, id); + if ((err = snd_ctl_elem_read(handle, control)) < 0) { + if (!ignore_error) + error("Cannot read the given element from control %s\n", card); + return err; + } + err = snd_ctl_ascii_value_parse(handle, control, info, argv[1]); + if (err < 0) { + if (!ignore_error) + error("Control %s parse error: %s\n", card, snd_strerror(err)); + return err; + } + if ((err = snd_ctl_elem_write(handle, control)) < 0) { + if (!ignore_error) + error("Control %s element write error: %s\n", card, snd_strerror(err)); + return err; + } + + return 0; +} + static int cset(int argc, char *argv[], int roflag, int keep_handle) { int err; static snd_ctl_t *handle = NULL; snd_ctl_elem_info_t *info; snd_ctl_elem_id_t *id; - snd_ctl_elem_value_t *control; + snd_ctl_elem_type_t type; snd_ctl_elem_info_alloca(&info); snd_ctl_elem_id_alloca(&id); - snd_ctl_elem_value_alloca(&control);
if (argc < 1) { fprintf(stderr, "Specify a full control identifier: [[iface=<iface>,][name='name',][index=<index>,][device=<device>,][subdevice=<subdevice>]]|[numid=<numid>]\n"); @@ -1175,30 +1292,15 @@ static int cset(int argc, char *argv[], int roflag, int keep_handle) } snd_ctl_elem_info_get_id(info, id); /* FIXME: Remove it when hctl find works ok !!! */ if (!roflag) { - snd_ctl_elem_value_set_id(control, id); - if ((err = snd_ctl_elem_read(handle, control)) < 0) { - if (ignore_error) - return 0; - error("Cannot read the given element from control %s\n", card); - if (! keep_handle) { - snd_ctl_close(handle); - handle = NULL; - } - return err; - } - err = snd_ctl_ascii_value_parse(handle, control, info, argv[1]); - if (err < 0) { - if (!ignore_error) - error("Control %s parse error: %s\n", card, snd_strerror(err)); - if (!keep_handle) { - snd_ctl_close(handle); - handle = NULL; - } - return ignore_error ? 0 : err; - } - if ((err = snd_ctl_elem_write(handle, control)) < 0) { - if (!ignore_error) - error("Control %s element write error: %s\n", card, snd_strerror(err)); + type = snd_ctl_elem_info_get_type(info); + if (type == SND_CTL_ELEM_TYPE_BYTES && + !snd_ctl_elem_info_is_readable(info) && + !snd_ctl_elem_info_is_writable(info)) { + err = cset_tlv(argc, argv, handle, info, id); + } else + err = cset_elem(argc, argv, handle, info, id); + + if (err) { if (!keep_handle) { snd_ctl_close(handle); handle = NULL;
participants (1)
-
Jan Dakinevich