[alsa-devel] [PATCH][RFC][alsa-utils 9/9] alsactl: handle detection of new sound card
Takashi Sakamoto
o-takashi at sakamocchi.jp
Fri Oct 5 16:47:29 CEST 2018
At present, plug-and-play is not supported in a mode of 'monitor',
thus new sound card is not handled during runtime. This is not happy.
This commit uses Linux-specific inotify(7) to monitor '/dev/snd'
directory. When some files are newly added to the directory,
event dispatcher is suspended. Event sources are scanned again and the
dispatcher continue to run.
Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
alsactl/monitor.c | 93 ++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 80 insertions(+), 13 deletions(-)
diff --git a/alsactl/monitor.c b/alsactl/monitor.c
index 4eb1ad1..6b5cfd4 100644
--- a/alsactl/monitor.c
+++ b/alsactl/monitor.c
@@ -24,6 +24,7 @@
#include <string.h>
#include <signal.h>
#include <sys/epoll.h>
+#include <sys/inotify.h>
#include <alsa/asoundlib.h>
static int signal_type;
@@ -94,6 +95,12 @@ static int insert_source_entry(struct src_entry **head, snd_ctl_t *handle,
int count;
int err;
+ // Avoid duplication at retry case.
+ for (src = *head; src; src = src->list.next) {
+ if (!strcmp(src->name, name))
+ return 0;
+ }
+
src = calloc(1, sizeof(*src));
if (!src)
return -ENOMEM;
@@ -181,6 +188,28 @@ static int prepare_source_entry(struct src_entry **srcs, const char *name)
return 0;
}
+static int dump_notification(int infd)
+{
+ char buf[1024];
+ ssize_t len;
+ int err = 0;
+
+ while (1) {
+ len = read(infd, buf, 1024);
+ if (len < 0) {
+ if (errno = EAGAIN)
+ break;
+ else
+ err = errno;
+ break;
+ }
+ if (len == 0)
+ break;
+ }
+
+ return err;
+}
+
static int print_event(snd_ctl_t *ctl, const char *name)
{
snd_ctl_event_t *event;
@@ -254,16 +283,20 @@ end:
return err;
}
-static int prepare_dispatcher(int epfd, struct src_entry *srcs)
+static int prepare_dispatcher(int epfd, int infd, struct src_entry *srcs)
{
+ struct epoll_event ev = {0};
struct src_entry *src;
int err = 0;
+ ev.events = EPOLLIN;
+ ev.data.fd = infd;
+ if (epoll_ctl(epfd, EPOLL_CTL_ADD, infd, &ev) < 0)
+ return -errno;
+
for (src = srcs; src; src = src->list.next) {
- struct epoll_event ev = {
- .events = EPOLLIN,
- .data.ptr = (void *)src,
- };
+ ev.events = EPOLLIN;
+ ev.data.ptr = (void *)src;
err = operate_dispatcher(epfd, EPOLL_CTL_ADD, &ev, src);
if (err < 0)
break;
@@ -272,7 +305,8 @@ static int prepare_dispatcher(int epfd, struct src_entry *srcs)
return err;
}
-static int run_dispatcher(int epfd, struct src_entry *srcs)
+static int run_dispatcher(int epfd, int infd, struct src_entry *srcs,
+ bool *retry)
{
struct src_entry *src;
unsigned int max_ev_count;
@@ -309,7 +343,16 @@ static int run_dispatcher(int epfd, struct src_entry *srcs)
for (i = 0; i < count; ++i) {
struct epoll_event *ev = epev + i;
- struct src_entry *src = (struct src_entry *)ev->data.ptr;
+ struct src_entry *src;
+
+ if (ev->data.fd == infd) {
+ err = dump_notification(infd);
+ if (err == 0)
+ *retry = true;
+ goto end;
+ }
+
+ src = ev->data.ptr;
if (ev->events & EPOLLIN)
print_event(src->handle, src->name);
if (ev->events & EPOLLERR) {
@@ -317,19 +360,22 @@ static int run_dispatcher(int epfd, struct src_entry *srcs)
remove_source_entry(src);
}
}
+ if (err < 0)
+ break;
}
-
+end:
free(epev);
-
return err;
}
-static void clear_dispatcher(int epfd, struct src_entry *srcs)
+static void clear_dispatcher(int epfd, int infd, struct src_entry *srcs)
{
struct src_entry *src;
for (src = srcs; src; src = src->list.next)
operate_dispatcher(epfd, EPOLL_CTL_DEL, NULL, src);
+
+ epoll_ctl(epfd, EPOLL_CTL_DEL, infd, NULL);
}
static void handle_unix_signal_for_finish(int sig)
@@ -360,6 +406,9 @@ int monitor(const char *name)
{
struct src_entry *srcs = NULL;
int epfd;
+ int infd;
+ int wd = 0;
+ bool retry;
int err = 0;
err = prepare_signal_handler();
@@ -370,17 +419,35 @@ int monitor(const char *name)
if (epfd < 0)
return -errno;
+ infd = inotify_init1(IN_NONBLOCK);
+ if (infd < 0) {
+ err = -errno;
+ goto error;
+ }
+ wd = inotify_add_watch(infd, "/dev/snd/", IN_CREATE);
+ if (wd < 0) {
+ err = -errno;
+ goto error;
+ }
+retry:
+ retry = false;
err = prepare_source_entry(&srcs, name);
if (err < 0)
goto error;
- err = prepare_dispatcher(epfd, srcs);
+ err = prepare_dispatcher(epfd, infd, srcs);
if (err >= 0)
- err = run_dispatcher(epfd, srcs);
- clear_dispatcher(epfd, srcs);
+ err = run_dispatcher(epfd, infd, srcs, &retry);
+ clear_dispatcher(epfd, infd, srcs);
+
+ if (retry)
+ goto retry;
error:
+ if (wd > 0)
+ inotify_rm_watch(infd, wd);
clear_source_list(&srcs);
+ close(infd);
close(epfd);
return err;
--
2.19.0
More information about the Alsa-devel
mailing list