Skip to content

Instantly share code, notes, and snippets.

@takaswie
Last active June 25, 2022 11:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takaswie/8378fe3bc04652d83428694cd7573bc0 to your computer and use it in GitHub Desktop.
Save takaswie/8378fe3bc04652d83428694cd7573bc0 to your computer and use it in GitHub Desktop.
Assertion test at mixer class implementation for removal event
#include <alsa/asoundlib.h>
#include <sys/epoll.h>
#include <stdbool.h>
static bool unsafe;
// (alsa-lib:src/mixer/mixer.c)
// snd_mixer_handle_events()
// (alsa-lib:src/control/hcontrol.c)
// ->snd_hctl_handle_events()
// ->snd_hctl_handle_event()
// ->snd_hctl_elem_remove()
// ->snd_hctl_elem_throw_event(REMOVE)
// ->snd_hctl_elem_add()
// ->snd_hctl_elem_throw_event(ADD)
// ->snd_hctl_elem_throw_event(VALUE | INFO)
// ->snd_hctl_elem_t.callback()
// (alsa-lib:src/mixer/mixer.c)
// = hctl_elem_event_handler() (src/mixer/mixer.c)
// assert(bag_emptry(bag))
static int mixer_class_event(snd_mixer_class_t *class, unsigned int mask, snd_hctl_elem_t *helem,
snd_mixer_elem_t *melem)
{
printf("Event:\n");
if (mask == SND_CTL_EVENT_MASK_REMOVE) {
printf(" remove\n");
if (!unsafe)
snd_mixer_elem_detach(melem, helem);
} else {
switch (mask) {
case SND_CTL_EVENT_MASK_ADD:
{
snd_mixer_elem_t *melem;
int err;
printf(" add\n");
printf(" %s, %d\n", snd_hctl_elem_get_name(helem), snd_hctl_elem_get_index(helem));
err = snd_mixer_elem_new(&melem, SND_MIXER_ELEM_SIMPLE + 1, 0, helem, NULL);
if (err < 0) {
printf("snd_mixer_elem_new(): %s\n", strerror(-err));
return 0;
}
err = snd_mixer_elem_attach(melem, helem);
if (err < 0) {
printf("snd_mixer_elem_attach(): %s\n", strerror(-err));
return 0;
}
err = snd_mixer_elem_add(melem, class);
if (err < 0) {
printf("snd_mixer_elem_add(): %s\n", strerror(-err));
return 0;
}
break;
}
case SND_CTL_EVENT_MASK_VALUE:
printf(" value\n");
break;
case SND_CTL_EVENT_MASK_INFO:
printf(" info\n");
break;
case SND_CTL_EVENT_MASK_TLV:
printf(" tlv\n");
break;
default:
break;
}
}
return 0;
}
static int mixer_class_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2)
{
// Momentary logic.
return c1 == c2 ? 0 : (c1 > c2 ? 1 : -1);
}
static int register_mixer_class(snd_mixer_t *handle)
{
snd_mixer_class_t *class;
int err;
if (snd_mixer_class_malloc(&class)) {
printf("snd_mixer_class_malloc(): %s\n", strerror(ENOMEM));
return -ENOMEM;
}
snd_mixer_class_set_event(class, mixer_class_event);
snd_mixer_class_set_compare(class, mixer_class_compare);
err = snd_mixer_class_register(class, handle);
if (err < 0) {
printf("snd_mixer_class_register(): %s\n", strerror(-err));
// TODO: free.
return err;
}
return 0;
}
static void dispatch_event(snd_mixer_t *handle, int epfd)
{
struct epoll_event events[10];
while (1) {
int result;
result = epoll_wait(epfd, events, 10, 100);
if (result < 0) {
printf("epoll_wait(): %s\n", strerror(errno));
break;
} else if (result > 0) {
int err;
printf("events: %d\n", result);
err = snd_mixer_handle_events(handle);
if (err < 0)
printf("snd_mixer_handle_events(): %s\n", strerror(-err));
}
}
}
static void run_dispatcher(snd_mixer_t *handle)
{
int epfd;
unsigned int count;
struct pollfd *pfds;
int i;
int err;
epfd = epoll_create(1);
if (epfd < 0) {
printf("epoll_create(): %s\n", strerror(errno));
return;
}
count = snd_mixer_poll_descriptors_count(handle);
pfds = calloc(count, sizeof(*pfds));
if (pfds == NULL) {
printf("calloc(): %s\n", strerror(ENOMEM));
goto err_epfd;
}
err = snd_mixer_poll_descriptors(handle, pfds, count);
if (err < 0) {
printf("snd_mixer_poll_descriptors(): %s\n", strerror(-err));
goto err_pfds;
}
for (i = 0; i < count; ++i) {
struct epoll_event ev = {
.data.fd = pfds[i].fd,
.events = pfds[i].events,
};
if (epoll_ctl(epfd, EPOLL_CTL_ADD, pfds[i].fd, &ev) < 0) {
printf("epoll_ctl(EPOLL_CTL_ADD): %s\n", strerror(errno));
goto err_pfds;
}
}
dispatch_event(handle, epfd);
for (i = 0; i < count; ++i) {
if (epoll_ctl(epfd, EPOLL_CTL_DEL, pfds[i].fd, NULL) < 0)
printf("epoll_ctl(EPOLL_CTL_DEL): %s\n", strerror(errno));
}
err_pfds:
free(pfds);
err_epfd:
close(epfd);
}
int main(int argc, const char **argv)
{
snd_mixer_t *handle;
int err;
unsafe = argc > 1;
err = snd_mixer_open(&handle, 0);
if (err < 0) {
printf("snd_mixer_open(): %s\n", strerror(-err));
return EXIT_FAILURE;
}
err = snd_mixer_attach(handle, "hw:0");
if (err < 0) {
printf("snd_mixer_attach(): %s\n", strerror(-err));
goto err_handle;
}
err = register_mixer_class(handle);
if (err < 0)
goto err_attach;
err = snd_mixer_selem_register(handle, NULL, NULL);
if (err < 0) {
printf("snd_mixer_selem_register(): %s\n", strerror(-err));
goto err_attach;
}
err = snd_mixer_load(handle);
if (err < 0) {
printf("snd_mixer_load(): %s\n", strerror(-err));
goto err_attach;
}
run_dispatcher(handle);
snd_mixer_free(handle);
err_attach:
snd_mixer_detach(handle, "hw:0");
err_handle:
snd_mixer_close(handle);
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment