forked from external/yambar
Merge branch 'alsa-inotify'
This commit is contained in:
commit
36de95cc2a
2 changed files with 100 additions and 46 deletions
2
log.c
2
log.c
|
@ -84,7 +84,7 @@ _log(enum log_class log_class, const char *module, const char *file, int lineno,
|
||||||
vfprintf(stderr, fmt, va);
|
vfprintf(stderr, fmt, va);
|
||||||
|
|
||||||
if (sys_errno != 0)
|
if (sys_errno != 0)
|
||||||
fprintf(stderr, ": %s", strerror(sys_errno));
|
fprintf(stderr, ": %s (%d)", strerror(sys_errno), sys_errno);
|
||||||
|
|
||||||
fputc('\n', stderr);
|
fputc('\n', stderr);
|
||||||
}
|
}
|
||||||
|
|
144
modules/alsa.c
144
modules/alsa.c
|
@ -2,6 +2,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
|
|
||||||
|
@ -187,6 +188,9 @@ run_while_online(struct module *mod)
|
||||||
struct private *m = mod->private;
|
struct private *m = mod->private;
|
||||||
enum run_state ret = RUN_ERROR;
|
enum run_state ret = RUN_ERROR;
|
||||||
|
|
||||||
|
/* Make sure we aren’t still tracking channels from previous connects */
|
||||||
|
tll_free(m->channels);
|
||||||
|
|
||||||
snd_mixer_t *handle;
|
snd_mixer_t *handle;
|
||||||
if (snd_mixer_open(&handle, 0) != 0) {
|
if (snd_mixer_open(&handle, 0) != 0) {
|
||||||
LOG_ERR("failed to open handle");
|
LOG_ERR("failed to open handle");
|
||||||
|
@ -315,79 +319,129 @@ err:
|
||||||
static int
|
static int
|
||||||
run(struct module *mod)
|
run(struct module *mod)
|
||||||
{
|
{
|
||||||
static const int min_timeout_ms = 500;
|
int ret = 1;
|
||||||
static const int max_timeout_ms = 30000;
|
|
||||||
int timeout_ms = min_timeout_ms;
|
int ifd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||||
|
if (ifd < 0) {
|
||||||
|
LOG_ERRNO("failed to inotify");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wd = inotify_add_watch(ifd, "/dev/snd", IN_CREATE);
|
||||||
|
if (wd < 0) {
|
||||||
|
LOG_ERRNO("failed to create inotify watcher for /dev/snd");
|
||||||
|
close(ifd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
enum run_state state = run_while_online(mod);
|
enum run_state state = run_while_online(mod);
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case RUN_DONE:
|
case RUN_DONE:
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto out;
|
||||||
|
|
||||||
case RUN_ERROR:
|
case RUN_ERROR:
|
||||||
return 1;
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
|
||||||
case RUN_FAILED_CONNECT:
|
case RUN_FAILED_CONNECT:
|
||||||
timeout_ms *= 2;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RUN_DISCONNECTED:
|
case RUN_DISCONNECTED:
|
||||||
timeout_ms = min_timeout_ms;
|
/*
|
||||||
|
* We’ve been connected - drain the watcher
|
||||||
|
*
|
||||||
|
* We don’t want old, un-releated events (for other
|
||||||
|
* soundcards, for example) to trigger a storm of
|
||||||
|
* re-connect attempts.
|
||||||
|
*/
|
||||||
|
while (true) {
|
||||||
|
uint8_t buf[1024];
|
||||||
|
ssize_t amount = read(ifd, buf, sizeof(buf));
|
||||||
|
if (amount < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
break;
|
||||||
|
|
||||||
|
LOG_ERRNO("failed to drain inotify watcher");
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amount == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeout_ms > max_timeout_ms)
|
bool have_create_event = false;
|
||||||
timeout_ms = max_timeout_ms;
|
|
||||||
|
|
||||||
struct timeval now;
|
while (!have_create_event) {
|
||||||
gettimeofday(&now, NULL);
|
struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN},
|
||||||
|
{.fd = ifd, .events = POLLIN}};
|
||||||
struct timeval timeout = {
|
int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1);
|
||||||
.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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
LOG_ERRNO("failed to poll");
|
LOG_ERRNO("failed to poll");
|
||||||
return 1;
|
ret = 1;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fds[0].revents & POLLIN)
|
if (fds[0].revents & (POLLIN | POLLHUP)) {
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
assert(r == 0);
|
if (fds[1].revents & POLLHUP) {
|
||||||
break;
|
LOG_ERR("inotify socket closed");
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(fds[1].revents & POLLIN);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
char buf[1024];
|
||||||
|
ssize_t len = read(ifd, buf, sizeof(buf));
|
||||||
|
|
||||||
|
if (len < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
break;
|
||||||
|
|
||||||
|
LOG_ERRNO("failed to read inotify events");
|
||||||
|
ret = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Consume inotify data */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (wd >= 0)
|
||||||
|
inotify_rm_watch(ifd, wd);
|
||||||
|
if (ifd >= 0)
|
||||||
|
close (ifd);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct module *
|
static struct module *
|
||||||
|
|
Loading…
Add table
Reference in a new issue