From 25c20e5534f6671a27b0218d6c0882e1fbb46e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 21 Aug 2021 10:52:12 +0200 Subject: [PATCH] module/alsa: use inotify on /dev/snd instead of a poll timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While waiting for the configured ALSA card to become available, use inotify and watch for CREATE events on /dev/snd instead of polling (using a timeout in the poll(3) call). Note that we don’t know the actual names of the files that (will) be created. This means: * Every time we see a CREATE event on /dev/snd, we *try* to connect to ALSA. If we fail, we go back to watching /dev/snd again. * ALSA (not yambar) will log an error message each time we fail. --- modules/alsa.c | 118 +++++++++++++++++++++++++++++-------------------- 1 file changed, 70 insertions(+), 48 deletions(-) diff --git a/modules/alsa.c b/modules/alsa.c index 54b9124..aa985b0 100644 --- a/modules/alsa.c +++ b/modules/alsa.c @@ -2,6 +2,7 @@ #include #include #include +#include #include @@ -315,79 +316,100 @@ err: static int run(struct module *mod) { - static const int min_timeout_ms = 500; - static const int max_timeout_ms = 30000; - int timeout_ms = min_timeout_ms; + int ret = 1; + + int wd = -1; + int ifd = inotify_init(); + if (ifd < 0) { + LOG_ERRNO("failed to inotify"); + return 1; + } while (true) { enum run_state state = run_while_online(mod); switch (state) { case RUN_DONE: - return 0; - - case RUN_ERROR: - return 1; - - case RUN_FAILED_CONNECT: - timeout_ms *= 2; + ret = 0; break; + case RUN_ERROR: + ret = 1; + break; + + case RUN_FAILED_CONNECT: case RUN_DISCONNECTED: - timeout_ms = min_timeout_ms; break; } - if (timeout_ms > max_timeout_ms) - timeout_ms = max_timeout_ms; - - struct timeval now; - gettimeofday(&now, NULL); - - struct timeval timeout = { - .tv_sec = timeout_ms / 1000, - .tv_usec = (timeout_ms % 1000) * 1000, - }; - - struct timeval deadline; - timeradd(&now, &timeout, &deadline); - - LOG_DBG("timeout is now %dms", timeout_ms); + wd = inotify_add_watch(ifd, "/dev/snd", IN_CREATE); + if (wd < 0) { + LOG_ERRNO("failed to create inotify watcher for /dev/snd"); + ret = 1; + break; + } while (true) { - - struct timeval n; - gettimeofday(&n, NULL); - - struct timeval left; - timersub(&deadline, &n, &left); - - int poll_timeout = timercmp(&left, &(struct timeval){0}, <) - ? 0 - : left.tv_sec * 1000 + left.tv_usec / 1000; - - LOG_DBG( - "polling for alsa device to become available (timeout=%dms)", - poll_timeout); - - struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}}; - int r = poll(fds, 1, poll_timeout); + struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}, + {.fd = ifd, .events = POLLIN}}; + int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); if (r < 0) { if (errno == EINTR) continue; LOG_ERRNO("failed to poll"); - return 1; + ret = 1; + goto out; } - if (fds[0].revents & POLLIN) - return 0; + if (fds[0].revents & POLLIN) { + ret = 0; + goto out; + } - assert(r == 0); - break; + if (fds[1].revents & POLLIN) { + char buf[1024]; + ssize_t len = read(ifd, buf, sizeof(buf)); + + if (len < 0) { + LOG_ERRNO("failed to read inotify events"); + ret = 1; + goto out; + } + + if (len == 0) { + LOG_ERR("inotify FD closed"); + ret = 1; + goto out; + } + + /* Consume inotify data */ + bool have_create_event = false; + for (const char *ptr = buf; ptr < buf + len; ) { + const struct inotify_event *e = (const struct inotify_event *)ptr; + if (e->mask & IN_CREATE) { + LOG_DBG("inotify: CREATED: /dev/snd/%.*s", e->len, e->name); + have_create_event = true; + } + ptr += sizeof(*e) + e->len; + } + + if (have_create_event) { + inotify_rm_watch(ifd, wd); + wd = -1; + break; + } + } } } + +out: + if (wd >= 0) + inotify_rm_watch(ifd, wd); + if (ifd >= 0) + close (ifd); + return ret; } static struct module *