1 /*
2 * Advanced Linux Sound Architecture Control Program
3 * Copyright (c) by Takashi Iwai <tiwai@suse.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "aconfig.h"
21 #include "version.h"
22 #include <stdio.h>
23 #include <stdbool.h>
24 #include <string.h>
25 #include <sys/epoll.h>
26 #include <sys/inotify.h>
27 #include <limits.h>
28 #include <time.h>
29 #include <signal.h>
30 #include <sys/signalfd.h>
31
32 #include <stddef.h>
33 #include "list.h"
34
35 #include "alsactl.h"
36
37 struct src_entry {
38 snd_ctl_t *handle;
39 char *name;
40 unsigned int pfd_count;
41 struct list_head list;
42 };
43
remove_source_entry(struct src_entry *entry)44 static void remove_source_entry(struct src_entry *entry)
45 {
46 list_del(&entry->list);
47 if (entry->handle)
48 snd_ctl_close(entry->handle);
49 free(entry->name);
50 free(entry);
51 }
52
clear_source_list(struct list_head *srcs)53 static void clear_source_list(struct list_head *srcs)
54 {
55 struct src_entry *entry, *tmp;
56
57 list_for_each_entry_safe(entry, tmp, srcs, list)
58 remove_source_entry(entry);
59 }
60
insert_source_entry(struct list_head *srcs, snd_ctl_t *handle, const char *name)61 static int insert_source_entry(struct list_head *srcs, snd_ctl_t *handle,
62 const char *name)
63 {
64 struct src_entry *entry;
65 int count;
66 int err;
67
68 entry = calloc(1, sizeof(*entry));
69 if (!entry)
70 return -ENOMEM;
71 INIT_LIST_HEAD(&entry->list);
72 entry->handle = handle;
73
74 entry->name = strdup(name);
75 if (!entry->name) {
76 err = -ENOMEM;
77 goto error;
78 }
79
80 count = snd_ctl_poll_descriptors_count(handle);
81 if (count < 0) {
82 err = count;
83 goto error;
84 }
85 if (count == 0) {
86 err = -ENXIO;
87 goto error;
88 }
89 entry->pfd_count = count;
90
91 list_add_tail(&entry->list, srcs);
92
93 return 0;
94 error:
95 remove_source_entry(entry);
96 return err;
97 }
98
open_ctl(const char *name, snd_ctl_t **ctlp)99 static int open_ctl(const char *name, snd_ctl_t **ctlp)
100 {
101 snd_ctl_t *ctl;
102 int err;
103
104 err = snd_ctl_open(&ctl, name, SND_CTL_READONLY);
105 if (err < 0) {
106 fprintf(stderr, "Cannot open ctl %s\n", name);
107 return err;
108 }
109 err = snd_ctl_subscribe_events(ctl, 1);
110 if (err < 0) {
111 fprintf(stderr, "Cannot open subscribe events to ctl %s\n", name);
112 snd_ctl_close(ctl);
113 return err;
114 }
115 *ctlp = ctl;
116 return 0;
117 }
118
seek_entry_by_name(struct list_head *srcs, const char *name)119 static inline bool seek_entry_by_name(struct list_head *srcs, const char *name)
120 {
121 struct src_entry *entry;
122
123 list_for_each_entry(entry, srcs, list) {
124 if (!strcmp(entry->name, name))
125 return true;
126 }
127
128 return false;
129 }
130
prepare_source_entry(struct list_head *srcs, const char *name)131 static int prepare_source_entry(struct list_head *srcs, const char *name)
132 {
133 snd_ctl_t *handle;
134 int err;
135
136 if (!name) {
137 struct snd_card_iterator iter;
138 const char *cardname;
139
140 snd_card_iterator_init(&iter, -1);
141 while ((cardname = snd_card_iterator_next(&iter))) {
142 if (seek_entry_by_name(srcs, cardname))
143 continue;
144 err = open_ctl(cardname, &handle);
145 if (err < 0)
146 return err;
147 err = insert_source_entry(srcs, handle, cardname);
148 if (err < 0)
149 return err;
150 }
151 } else {
152 if (seek_entry_by_name(srcs, name))
153 return 0;
154 err = open_ctl(name, &handle);
155 if (err < 0)
156 return err;
157 err = insert_source_entry(srcs, handle, name);
158 if (err < 0)
159 return err;
160 }
161
162 return 0;
163 }
164
check_control_cdev(int infd, bool *retry)165 static int check_control_cdev(int infd, bool *retry)
166 {
167 struct inotify_event *ev;
168 char *buf;
169 int err = 0;
170
171 buf = calloc(1, sizeof(*ev) + NAME_MAX);
172 if (!buf)
173 return -ENOMEM;
174
175 while (1) {
176 ssize_t len = read(infd, buf, sizeof(*ev) + NAME_MAX);
177 if (len < 0) {
178 if (errno != EAGAIN)
179 err = errno;
180 break;
181 } else if (len == 0) {
182 break;
183 }
184
185 size_t pos = 0;
186 while (pos < (size_t)len) {
187 ev = (struct inotify_event *)(buf + pos);
188 if ((ev->mask & IN_CREATE) &&
189 strstr(ev->name, "controlC") == ev->name)
190 *retry = true;
191 pos += sizeof(*ev) + ev->len;
192 }
193 }
194
195 free(buf);
196
197 return err;
198 }
199
print_event(snd_ctl_t *ctl, const char *name)200 static int print_event(snd_ctl_t *ctl, const char *name)
201 {
202 snd_ctl_event_t *event;
203 unsigned int mask;
204 int err;
205
206 snd_ctl_event_alloca(&event);
207 err = snd_ctl_read(ctl, event);
208 if (err < 0)
209 return err;
210
211 if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM)
212 return 0;
213
214 printf("node %s, #%d (%i,%i,%i,%s,%i)",
215 name,
216 snd_ctl_event_elem_get_numid(event),
217 snd_ctl_event_elem_get_interface(event),
218 snd_ctl_event_elem_get_device(event),
219 snd_ctl_event_elem_get_subdevice(event),
220 snd_ctl_event_elem_get_name(event),
221 snd_ctl_event_elem_get_index(event));
222
223 mask = snd_ctl_event_elem_get_mask(event);
224 if (mask == SND_CTL_EVENT_MASK_REMOVE) {
225 printf(" REMOVE\n");
226 return 0;
227 }
228
229 if (mask & SND_CTL_EVENT_MASK_VALUE)
230 printf(" VALUE");
231 if (mask & SND_CTL_EVENT_MASK_INFO)
232 printf(" INFO");
233 if (mask & SND_CTL_EVENT_MASK_ADD)
234 printf(" ADD");
235 if (mask & SND_CTL_EVENT_MASK_TLV)
236 printf(" TLV");
237 printf("\n");
238 fflush(stdout);
239 return 0;
240 }
241
operate_dispatcher(int epfd, uint32_t op, struct epoll_event *epev, struct src_entry *entry)242 static int operate_dispatcher(int epfd, uint32_t op, struct epoll_event *epev,
243 struct src_entry *entry)
244 {
245 struct pollfd *pfds;
246 int count;
247 int i;
248 int err = 0;
249
250 pfds = calloc(entry->pfd_count, sizeof(*pfds));
251 if (!pfds)
252 return -ENOMEM;
253
254 count = snd_ctl_poll_descriptors(entry->handle, pfds, entry->pfd_count);
255 if (count < 0) {
256 err = count;
257 goto end;
258 }
259 if (count != (int)entry->pfd_count) {
260 err = -EIO;
261 goto end;
262 }
263
264 for (i = 0; i < (int)entry->pfd_count; ++i) {
265 err = epoll_ctl(epfd, op, pfds[i].fd, epev);
266 if (err < 0)
267 break;
268 }
269 end:
270 free(pfds);
271 return err;
272 }
273
prepare_dispatcher(int epfd, int sigfd, int infd, struct list_head *srcs)274 static int prepare_dispatcher(int epfd, int sigfd, int infd,
275 struct list_head *srcs)
276 {
277 struct epoll_event ev = {0};
278 struct src_entry *entry;
279 int err = 0;
280
281 ev.events = EPOLLIN;
282 ev.data.fd = sigfd;
283 if (epoll_ctl(epfd, EPOLL_CTL_ADD, sigfd, &ev) < 0)
284 return -errno;
285
286 ev.events = EPOLLIN;
287 ev.data.fd = infd;
288 if (epoll_ctl(epfd, EPOLL_CTL_ADD, infd, &ev) < 0)
289 return -errno;
290
291 list_for_each_entry(entry, srcs, list) {
292 ev.events = EPOLLIN;
293 ev.data.ptr = (void *)entry;
294 err = operate_dispatcher(epfd, EPOLL_CTL_ADD, &ev, entry);
295 if (err < 0)
296 break;
297 }
298
299 return err;
300 }
301
run_dispatcher(int epfd, int sigfd, int infd, struct list_head *srcs, bool *retry)302 static int run_dispatcher(int epfd, int sigfd, int infd, struct list_head *srcs,
303 bool *retry)
304 {
305 struct src_entry *entry;
306 unsigned int max_ev_count;
307 struct epoll_event *epev;
308 int err = 0;
309
310 max_ev_count = 0;
311 list_for_each_entry(entry, srcs, list)
312 max_ev_count += entry->pfd_count;
313
314 epev = calloc(max_ev_count, sizeof(*epev));
315 if (!epev)
316 return -ENOMEM;
317
318 while (true) {
319 int count;
320 int i;
321
322 count = epoll_wait(epfd, epev, max_ev_count, -1);
323 if (count < 0) {
324 if (errno == EINTR)
325 continue;
326 err = count;
327 break;
328 }
329 if (count == 0)
330 continue;
331
332 for (i = 0; i < count; ++i) {
333 struct epoll_event *ev = epev + i;
334
335 if (ev->data.fd == sigfd)
336 goto end;
337
338 if (ev->data.fd == infd) {
339 err = check_control_cdev(infd, retry);
340 if (err < 0 || *retry)
341 goto end;
342 continue;
343 }
344
345 entry = ev->data.ptr;
346 if (ev->events & EPOLLIN)
347 print_event(entry->handle, entry->name);
348 if (ev->events & EPOLLERR) {
349 operate_dispatcher(epfd, EPOLL_CTL_DEL, NULL, entry);
350 remove_source_entry(entry);
351 }
352 }
353 }
354 end:
355 free(epev);
356 return err;
357 }
358
clear_dispatcher(int epfd, int sigfd, int infd, struct list_head *srcs)359 static void clear_dispatcher(int epfd, int sigfd, int infd,
360 struct list_head *srcs)
361 {
362 struct src_entry *entry;
363
364 list_for_each_entry(entry, srcs, list)
365 operate_dispatcher(epfd, EPOLL_CTL_DEL, NULL, entry);
366
367 epoll_ctl(epfd, EPOLL_CTL_DEL, infd, NULL);
368
369 epoll_ctl(epfd, EPOLL_CTL_DEL, sigfd, NULL);
370 }
371
prepare_signalfd(int *sigfd)372 static int prepare_signalfd(int *sigfd)
373 {
374 sigset_t mask;
375 int fd;
376
377 sigemptyset(&mask);
378 sigaddset(&mask, SIGINT);
379 sigaddset(&mask, SIGTERM);
380
381 if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0)
382 return -errno;
383
384 fd = signalfd(-1, &mask, 0);
385 if (fd < 0)
386 return -errno;
387 *sigfd = fd;
388
389 return 0;
390 }
391
monitor(const char *name)392 int monitor(const char *name)
393 {
394 LIST_HEAD(srcs);
395 int sigfd = 0;
396 int epfd;
397 int infd;
398 int wd = 0;
399 bool retry;
400 int err = 0;
401
402 err = prepare_signalfd(&sigfd);
403 if (err < 0)
404 return err;
405
406 epfd = epoll_create(1);
407 if (epfd < 0) {
408 close(sigfd);
409 return -errno;
410 }
411
412 infd = inotify_init1(IN_NONBLOCK);
413 if (infd < 0) {
414 err = -errno;
415 goto error;
416 }
417 wd = inotify_add_watch(infd, "/dev/snd/", IN_CREATE);
418 if (wd < 0) {
419 err = -errno;
420 goto error;
421 }
422 retry:
423 retry = false;
424 err = prepare_source_entry(&srcs, name);
425 if (err < 0)
426 goto error;
427
428 err = prepare_dispatcher(epfd, sigfd, infd, &srcs);
429 if (err >= 0)
430 err = run_dispatcher(epfd, sigfd, infd, &srcs, &retry);
431 clear_dispatcher(epfd, sigfd, infd, &srcs);
432
433 if (retry) {
434 // A simple makeshift for timing gap between creation of nodes
435 // by devtmpfs and chmod() by udevd.
436 struct timespec req = { .tv_sec = 1 };
437 nanosleep(&req, NULL);
438 goto retry;
439 }
440 error:
441 clear_source_list(&srcs);
442
443 if (wd > 0)
444 inotify_rm_watch(infd, wd);
445 close(infd);
446
447 close(epfd);
448
449 close(sigfd);
450
451 return err;
452 }
453