@@ -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;
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(-)