/* Common operations. */ static int add_elem_set(struct elem_set_trial *trial) { snd_ctl_elem_info_t *info; char name[64] = {0}; int err; snprintf(name, 64, "userspace-control-element-%s", snd_ctl_elem_type_name(trial->type)); snd_ctl_elem_info_alloca(&info); snd_ctl_elem_info_set_interface(info, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_info_set_name(info, name); snd_ctl_elem_info_set_dimension(info, trial->dimension); err = trial->add_elem_set(trial, info); if (err >= 0) snd_ctl_elem_info_get_id(info, trial->id); return err; }
static const char *control_type(snd_ctl_elem_info_t *info) { return snd_ctl_elem_type_name(snd_ctl_elem_info_get_type(info)); }
static int get_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id, snd_config_t *top) { snd_ctl_elem_value_t *ctl; snd_ctl_elem_info_t *info; snd_config_t *control, *comment, *item, *value; const char *s; char buf[256]; unsigned int idx; int err; unsigned int device, subdevice, index; const char *name; snd_ctl_elem_type_t type; unsigned int count; snd_ctl_elem_value_alloca(&ctl); snd_ctl_elem_info_alloca(&info); snd_ctl_elem_info_set_id(info, id); err = snd_ctl_elem_info(handle, info); if (err < 0) { error("Cannot read control info '%s': %s", id_str(id), snd_strerror(err)); return err; } if (snd_ctl_elem_info_is_inactive(info) || !snd_ctl_elem_info_is_readable(info)) return 0; snd_ctl_elem_value_set_id(ctl, id); err = snd_ctl_elem_read(handle, ctl); if (err < 0) { error("Cannot read control '%s': %s", id_str(id), snd_strerror(err)); return err; } err = snd_config_compound_add(top, num_str(snd_ctl_elem_info_get_numid(info)), 0, &control); if (err < 0) { error("snd_config_compound_add: %s", snd_strerror(err)); return err; } err = snd_config_compound_add(control, "comment", 1, &comment); if (err < 0) { error("snd_config_compound_add: %s", snd_strerror(err)); return err; } buf[0] = '\0'; buf[1] = '\0'; if (snd_ctl_elem_info_is_readable(info)) strcat(buf, " read"); if (snd_ctl_elem_info_is_writable(info)) strcat(buf, " write"); if (snd_ctl_elem_info_is_inactive(info)) strcat(buf, " inactive"); if (snd_ctl_elem_info_is_volatile(info)) strcat(buf, " volatile"); if (snd_ctl_elem_info_is_locked(info)) strcat(buf, " locked"); if (snd_ctl_elem_info_is_user(info)) strcat(buf, " user"); err = snd_config_string_add(comment, "access", buf + 1); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } type = snd_ctl_elem_info_get_type(info); device = snd_ctl_elem_info_get_device(info); subdevice = snd_ctl_elem_info_get_subdevice(info); index = snd_ctl_elem_info_get_index(info); name = snd_ctl_elem_info_get_name(info); count = snd_ctl_elem_info_get_count(info); s = snd_ctl_elem_type_name(type); err = snd_config_string_add(comment, "type", s); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } err = snd_config_integer_add(comment, "count", count); if (err < 0) { error("snd_config_integer_add: %s", snd_strerror(err)); return err; } switch (type) { case SND_CTL_ELEM_TYPE_BOOLEAN: break; case SND_CTL_ELEM_TYPE_INTEGER: { long min = snd_ctl_elem_info_get_min(info); long max = snd_ctl_elem_info_get_max(info); long step = snd_ctl_elem_info_get_step(info); if (step) sprintf(buf, "%li - %li (step %li)", min, max, step); else sprintf(buf, "%li - %li", min, max); err = snd_config_string_add(comment, "range", buf); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } if (snd_ctl_elem_info_is_tlv_readable(info)) { err = add_tlv_comments(handle, id, info, comment); if (err < 0) return err; } break; } case SND_CTL_ELEM_TYPE_INTEGER64: { long long min = snd_ctl_elem_info_get_min64(info); long long max = snd_ctl_elem_info_get_max64(info); long long step = snd_ctl_elem_info_get_step64(info); if (step) sprintf(buf, "%Li - %Li (step %Li)", min, max, step); else sprintf(buf, "%Li - %Li", min, max); err = snd_config_string_add(comment, "range", buf); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } break; } case SND_CTL_ELEM_TYPE_ENUMERATED: { unsigned int items; err = snd_config_compound_add(comment, "item", 1, &item); if (err < 0) { error("snd_config_compound_add: %s", snd_strerror(err)); return err; } items = snd_ctl_elem_info_get_items(info); for (idx = 0; idx < items; idx++) { snd_ctl_elem_info_set_item(info, idx); err = snd_ctl_elem_info(handle, info); if (err < 0) { error("snd_ctl_card_info: %s", snd_strerror(err)); return err; } err = snd_config_string_add(item, num_str(idx), snd_ctl_elem_info_get_item_name(info)); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } } break; } default: break; } s = snd_ctl_elem_iface_name(snd_ctl_elem_info_get_interface(info)); err = snd_config_string_add(control, "iface", s); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } if (device != 0) { err = snd_config_integer_add(control, "device", device); if (err < 0) { error("snd_config_integer_add: %s", snd_strerror(err)); return err; } } if (subdevice != 0) { err = snd_config_integer_add(control, "subdevice", subdevice); if (err < 0) { error("snd_config_integer_add: %s", snd_strerror(err)); return err; } } err = snd_config_string_add(control, "name", name); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } if (index != 0) { err = snd_config_integer_add(control, "index", index); if (err < 0) { error("snd_config_integer_add: %s", snd_strerror(err)); return err; } } switch (type) { case SND_CTL_ELEM_TYPE_BYTES: case SND_CTL_ELEM_TYPE_IEC958: { size_t size = type == SND_CTL_ELEM_TYPE_BYTES ? count : sizeof(snd_aes_iec958_t); char buf[size * 2 + 1]; char *p = buf; char *hex = "0123456789abcdef"; const unsigned char *bytes = (const unsigned char *)snd_ctl_elem_value_get_bytes(ctl); for (idx = 0; idx < size; idx++) { int v = bytes[idx]; *p++ = hex[v >> 4]; *p++ = hex[v & 0x0f]; } *p = '\0'; err = snd_config_string_add(control, "value", buf); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } return 0; } default: break; } if (count == 1) { switch (type) { case SND_CTL_ELEM_TYPE_BOOLEAN: err = snd_config_string_add(control, "value", snd_ctl_elem_value_get_boolean(ctl, 0) ? "true" : "false"); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } return 0; case SND_CTL_ELEM_TYPE_INTEGER: err = snd_config_integer_add(control, "value", snd_ctl_elem_value_get_integer(ctl, 0)); if (err < 0) { error("snd_config_integer_add: %s", snd_strerror(err)); return err; } return 0; case SND_CTL_ELEM_TYPE_INTEGER64: err = snd_config_integer64_add(control, "value", snd_ctl_elem_value_get_integer64(ctl, 0)); if (err < 0) { error("snd_config_integer64_add: %s", snd_strerror(err)); return err; } return 0; case SND_CTL_ELEM_TYPE_ENUMERATED: { unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, 0); snd_config_t *c; err = snd_config_search(item, num_str(v), &c); if (err == 0) { err = snd_config_get_string(c, &s); assert(err == 0); err = snd_config_string_add(control, "value", s); } else { err = snd_config_integer_add(control, "value", v); } if (err < 0) error("snd_config add: %s", snd_strerror(err)); return 0; } default: error("Unknown control type: %d\n", type); return -EINVAL; } } err = snd_config_compound_add(control, "value", 1, &value); if (err < 0) { error("snd_config_compound_add: %s", snd_strerror(err)); return err; } switch (type) { case SND_CTL_ELEM_TYPE_BOOLEAN: for (idx = 0; idx < count; idx++) { err = snd_config_string_add(value, num_str(idx), snd_ctl_elem_value_get_boolean(ctl, idx) ? "true" : "false"); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } } break; case SND_CTL_ELEM_TYPE_INTEGER: for (idx = 0; idx < count; idx++) { err = snd_config_integer_add(value, num_str(idx), snd_ctl_elem_value_get_integer(ctl, idx)); if (err < 0) { error("snd_config_integer_add: %s", snd_strerror(err)); return err; } } break; case SND_CTL_ELEM_TYPE_INTEGER64: for (idx = 0; idx < count; idx++) { err = snd_config_integer64_add(value, num_str(idx), snd_ctl_elem_value_get_integer64(ctl, idx)); if (err < 0) { error("snd_config_integer64_add: %s", snd_strerror(err)); return err; } } break; case SND_CTL_ELEM_TYPE_ENUMERATED: for (idx = 0; idx < count; idx++) { unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, idx); snd_config_t *c; err = snd_config_search(item, num_str(v), &c); if (err == 0) { err = snd_config_get_string(c, &s); assert(err == 0); err = snd_config_string_add(value, num_str(idx), s); } else { err = snd_config_integer_add(value, num_str(idx), v); } if (err < 0) { error("snd_config add: %s", snd_strerror(err)); return err; } } break; default: error("Unknown control type: %d\n", type); return -EINVAL; } return 0; }
int main(void) { struct elem_set_trial trial = {0}; unsigned int i; int err; snd_ctl_elem_id_alloca(&trial.id); err = snd_ctl_open(&trial.handle, "hw:0", 0); if (err < 0) return EXIT_FAILURE; err = snd_ctl_subscribe_events(trial.handle, 1); if (err < 0) return EXIT_FAILURE; /* Test all of types. */ for (i = 0; i < SND_CTL_ELEM_TYPE_LAST; ++i) { trial.type = i + 1; /* Assign type-dependent operations. */ switch (trial.type) { case SND_CTL_ELEM_TYPE_BOOLEAN: trial.element_count = 900; trial.member_count = 128; trial.dimension[0] = 4; trial.dimension[1] = 4; trial.dimension[2] = 8; trial.dimension[3] = 0; trial.add_elem_set = add_bool_elem_set; trial.check_elem_props = NULL; trial.change_elem_members = change_bool_elem_members; break; case SND_CTL_ELEM_TYPE_INTEGER: trial.element_count = 900; trial.member_count = 128; trial.dimension[0] = 128; trial.dimension[1] = 0; trial.dimension[2] = 0; trial.dimension[3] = 0; trial.add_elem_set = add_int_elem_set; trial.check_elem_props = check_int_elem_props; trial.change_elem_members = change_int_elem_members; break; case SND_CTL_ELEM_TYPE_ENUMERATED: trial.element_count = 900; trial.member_count = 128; trial.dimension[0] = 16; trial.dimension[1] = 8; trial.dimension[2] = 0; trial.dimension[3] = 0; trial.add_elem_set = add_enum_elem_set; trial.check_elem_props = check_enum_elem_props; trial.change_elem_members = change_enum_elem_members; break; case SND_CTL_ELEM_TYPE_BYTES: trial.element_count = 900; trial.member_count = 512; trial.dimension[0] = 8; trial.dimension[1] = 4; trial.dimension[2] = 8; trial.dimension[3] = 2; trial.add_elem_set = add_bytes_elem_set; trial.check_elem_props = NULL; trial.change_elem_members = change_bytes_elem_members; break; case SND_CTL_ELEM_TYPE_IEC958: trial.element_count = 1; trial.member_count = 1; trial.dimension[0] = 0; trial.dimension[1] = 0; trial.dimension[2] = 0; trial.dimension[3] = 0; trial.add_elem_set = add_iec958_elem_set; trial.check_elem_props = NULL; trial.change_elem_members = change_iec958_elem_members; break; case SND_CTL_ELEM_TYPE_INTEGER64: default: trial.element_count = 900; trial.member_count = 64; trial.dimension[0] = 0; trial.dimension[1] = 0; trial.dimension[2] = 0; trial.dimension[3] = 0; trial.add_elem_set = add_int64_elem_set; trial.check_elem_props = check_int64_elem_props; trial.change_elem_members = change_int64_elem_members; break; } /* Test an operation to add an element set. */ err = add_elem_set(&trial); if (err < 0) { printf("Fail to add an element set with %s type.\n", snd_ctl_elem_type_name(trial.type)); break; } err = check_event(&trial, SND_CTL_EVENT_MASK_ADD, trial.element_count); if (err < 0) { printf("Fail to check some events to add elements with " "%s type.\n", snd_ctl_elem_type_name(trial.type)); break; } /* Check properties of each element in this element set. */ err = check_elem_set_props(&trial); if (err < 0) { printf("Fail to check propetries of each element with " "%s type.\n", snd_ctl_elem_type_name(trial.type)); break; } /* * Test operations to change the state of members in each * element in the element set. */ err = check_elems(&trial); if (err < 0) { printf("Fail to change status of each element with %s " "type.\n", snd_ctl_elem_type_name(trial.type)); break; } err = check_event(&trial, SND_CTL_EVENT_MASK_VALUE, trial.element_count); if (err < 0) { printf("Fail to check some events to change status of " "each elements with %s type.\n", snd_ctl_elem_type_name(trial.type)); break; } /* * Test an operation to change threshold data of this element set, * except for IEC958 type. */ if (trial.type != SND_CTL_ELEM_TYPE_IEC958) { err = check_tlv(&trial); if (err < 0) { printf("Fail to change threshold level of an " "element set with %s type.\n", snd_ctl_elem_type_name(trial.type)); break; } err = check_event(&trial, SND_CTL_EVENT_MASK_TLV, 1); if (err < 0) { printf("Fail to check an event to change " "threshold level of an an element set " "with %s type.\n", snd_ctl_elem_type_name(trial.type)); break; } } /* Test an operation to remove elements in this element set. */ err = snd_ctl_elem_remove(trial.handle, trial.id); if (err < 0) { printf("Fail to remove elements with %s type.\n", snd_ctl_elem_type_name(trial.type)); break; } err = check_event(&trial, SND_CTL_EVENT_MASK_REMOVE, trial.element_count); if (err < 0) { printf("Fail to check some events to remove each " "element with %s type.\n", snd_ctl_elem_type_name(trial.type)); break; } } if (err < 0) { printf("%s\n", snd_strerror(err)); /* To ensure. */ snd_ctl_elem_remove(trial.handle, trial.id); return EXIT_FAILURE; } return EXIT_SUCCESS; }