xref: /third_party/alsa-utils/alsactl/daemon.c (revision c72fcc34)
1c72fcc34Sopenharmony_ci/*
2c72fcc34Sopenharmony_ci *  Advanced Linux Sound Architecture Control Program
3c72fcc34Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
4c72fcc34Sopenharmony_ci *
5c72fcc34Sopenharmony_ci *
6c72fcc34Sopenharmony_ci *   This program is free software; you can redistribute it and/or modify
7c72fcc34Sopenharmony_ci *   it under the terms of the GNU General Public License as published by
8c72fcc34Sopenharmony_ci *   the Free Software Foundation; either version 2 of the License, or
9c72fcc34Sopenharmony_ci *   (at your option) any later version.
10c72fcc34Sopenharmony_ci *
11c72fcc34Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
12c72fcc34Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13c72fcc34Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14c72fcc34Sopenharmony_ci *   GNU General Public License for more details.
15c72fcc34Sopenharmony_ci *
16c72fcc34Sopenharmony_ci *   You should have received a copy of the GNU General Public License
17c72fcc34Sopenharmony_ci *   along with this program; if not, write to the Free Software
18c72fcc34Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19c72fcc34Sopenharmony_ci *
20c72fcc34Sopenharmony_ci */
21c72fcc34Sopenharmony_ci
22c72fcc34Sopenharmony_ci#include "aconfig.h"
23c72fcc34Sopenharmony_ci#include "version.h"
24c72fcc34Sopenharmony_ci#include <getopt.h>
25c72fcc34Sopenharmony_ci#include <stdarg.h>
26c72fcc34Sopenharmony_ci#include <stdio.h>
27c72fcc34Sopenharmony_ci#include <assert.h>
28c72fcc34Sopenharmony_ci#include <errno.h>
29c72fcc34Sopenharmony_ci#include <signal.h>
30c72fcc34Sopenharmony_ci#include <time.h>
31c72fcc34Sopenharmony_ci#include <poll.h>
32c72fcc34Sopenharmony_ci#include "alsactl.h"
33c72fcc34Sopenharmony_ci
34c72fcc34Sopenharmony_cistruct id_list {
35c72fcc34Sopenharmony_ci	snd_ctl_elem_id_t **list;
36c72fcc34Sopenharmony_ci	int size;
37c72fcc34Sopenharmony_ci};
38c72fcc34Sopenharmony_ci
39c72fcc34Sopenharmony_cistruct card {
40c72fcc34Sopenharmony_ci	int index;
41c72fcc34Sopenharmony_ci	int pfds;
42c72fcc34Sopenharmony_ci	snd_ctl_t *handle;
43c72fcc34Sopenharmony_ci	struct id_list whitelist;
44c72fcc34Sopenharmony_ci	struct id_list blacklist;
45c72fcc34Sopenharmony_ci};
46c72fcc34Sopenharmony_ci
47c72fcc34Sopenharmony_cistatic int quit = 0;
48c72fcc34Sopenharmony_cistatic int rescan = 0;
49c72fcc34Sopenharmony_cistatic int save_now = 0;
50c72fcc34Sopenharmony_ci
51c72fcc34Sopenharmony_cistatic void signal_handler_quit(int sig)
52c72fcc34Sopenharmony_ci{
53c72fcc34Sopenharmony_ci	quit = 1;
54c72fcc34Sopenharmony_ci	signal(sig, signal_handler_quit);
55c72fcc34Sopenharmony_ci}
56c72fcc34Sopenharmony_ci
57c72fcc34Sopenharmony_cistatic void signal_handler_save_and_quit(int sig)
58c72fcc34Sopenharmony_ci{
59c72fcc34Sopenharmony_ci	quit = save_now = 1;
60c72fcc34Sopenharmony_ci	signal(sig, signal_handler_quit);
61c72fcc34Sopenharmony_ci}
62c72fcc34Sopenharmony_ci
63c72fcc34Sopenharmony_cistatic void signal_handler_rescan(int sig)
64c72fcc34Sopenharmony_ci{
65c72fcc34Sopenharmony_ci	rescan = 1;
66c72fcc34Sopenharmony_ci	signal(sig, signal_handler_rescan);
67c72fcc34Sopenharmony_ci}
68c72fcc34Sopenharmony_ci
69c72fcc34Sopenharmony_cistatic void free_list(struct id_list *list)
70c72fcc34Sopenharmony_ci{
71c72fcc34Sopenharmony_ci	int i;
72c72fcc34Sopenharmony_ci
73c72fcc34Sopenharmony_ci	for (i = 0; i < list->size; i++)
74c72fcc34Sopenharmony_ci		free(list->list[i]);
75c72fcc34Sopenharmony_ci	free(list->list);
76c72fcc34Sopenharmony_ci}
77c72fcc34Sopenharmony_ci
78c72fcc34Sopenharmony_cistatic void card_free(struct card **card)
79c72fcc34Sopenharmony_ci{
80c72fcc34Sopenharmony_ci	struct card *c = *card;
81c72fcc34Sopenharmony_ci
82c72fcc34Sopenharmony_ci	free_list(&c->blacklist);
83c72fcc34Sopenharmony_ci	free_list(&c->whitelist);
84c72fcc34Sopenharmony_ci	if (c->handle)
85c72fcc34Sopenharmony_ci		snd_ctl_close(c->handle);
86c72fcc34Sopenharmony_ci	free(c);
87c72fcc34Sopenharmony_ci	*card = NULL;
88c72fcc34Sopenharmony_ci}
89c72fcc34Sopenharmony_ci
90c72fcc34Sopenharmony_cistatic void add_card(struct card ***cards, int *count, const char *cardname)
91c72fcc34Sopenharmony_ci{
92c72fcc34Sopenharmony_ci	struct card *card, **cc;
93c72fcc34Sopenharmony_ci	int i, index, findex;
94c72fcc34Sopenharmony_ci	char device[16];
95c72fcc34Sopenharmony_ci
96c72fcc34Sopenharmony_ci	index = snd_card_get_index(cardname);
97c72fcc34Sopenharmony_ci	if (index < 0)
98c72fcc34Sopenharmony_ci		return;
99c72fcc34Sopenharmony_ci	for (i = 0, findex = -1; i < *count; i++) {
100c72fcc34Sopenharmony_ci		if ((*cards)[i] == NULL) {
101c72fcc34Sopenharmony_ci			findex = i;
102c72fcc34Sopenharmony_ci		} else {
103c72fcc34Sopenharmony_ci			if ((*cards)[i]->index == index)
104c72fcc34Sopenharmony_ci				return;
105c72fcc34Sopenharmony_ci		}
106c72fcc34Sopenharmony_ci	}
107c72fcc34Sopenharmony_ci	card = calloc(1, sizeof(*card));
108c72fcc34Sopenharmony_ci	if (card == NULL)
109c72fcc34Sopenharmony_ci		return;
110c72fcc34Sopenharmony_ci	card->index = index;
111c72fcc34Sopenharmony_ci	sprintf(device, "hw:%i", index);
112c72fcc34Sopenharmony_ci	if (snd_ctl_open(&card->handle, device, SND_CTL_READONLY|SND_CTL_NONBLOCK) < 0) {
113c72fcc34Sopenharmony_ci		card_free(&card);
114c72fcc34Sopenharmony_ci		return;
115c72fcc34Sopenharmony_ci	}
116c72fcc34Sopenharmony_ci	card->pfds = snd_ctl_poll_descriptors_count(card->handle);
117c72fcc34Sopenharmony_ci	if (card->pfds < 0) {
118c72fcc34Sopenharmony_ci		card_free(&card);
119c72fcc34Sopenharmony_ci		return;
120c72fcc34Sopenharmony_ci	}
121c72fcc34Sopenharmony_ci	if (snd_ctl_subscribe_events(card->handle, 1) < 0) {
122c72fcc34Sopenharmony_ci		card_free(&card);
123c72fcc34Sopenharmony_ci		return;
124c72fcc34Sopenharmony_ci	}
125c72fcc34Sopenharmony_ci	if (findex >= 0) {
126c72fcc34Sopenharmony_ci		(*cards)[findex] = card;
127c72fcc34Sopenharmony_ci	} else {
128c72fcc34Sopenharmony_ci		cc = realloc(*cards, sizeof(void *) * (*count + 1));
129c72fcc34Sopenharmony_ci		if (cc == NULL) {
130c72fcc34Sopenharmony_ci			card_free(&card);
131c72fcc34Sopenharmony_ci			return;
132c72fcc34Sopenharmony_ci		}
133c72fcc34Sopenharmony_ci		cc[*count] = card;
134c72fcc34Sopenharmony_ci		*count = *count + 1;
135c72fcc34Sopenharmony_ci		*cards = cc;
136c72fcc34Sopenharmony_ci	}
137c72fcc34Sopenharmony_ci}
138c72fcc34Sopenharmony_ci
139c72fcc34Sopenharmony_cistatic void add_cards(struct card ***cards, int *count)
140c72fcc34Sopenharmony_ci{
141c72fcc34Sopenharmony_ci	int card = -1;
142c72fcc34Sopenharmony_ci	char cardname[16];
143c72fcc34Sopenharmony_ci
144c72fcc34Sopenharmony_ci	while (1) {
145c72fcc34Sopenharmony_ci		if (snd_card_next(&card) < 0)
146c72fcc34Sopenharmony_ci			break;
147c72fcc34Sopenharmony_ci		if (card < 0)
148c72fcc34Sopenharmony_ci			break;
149c72fcc34Sopenharmony_ci		if (card >= 0) {
150c72fcc34Sopenharmony_ci			sprintf(cardname, "%i", card);
151c72fcc34Sopenharmony_ci			add_card(cards, count, cardname);
152c72fcc34Sopenharmony_ci		}
153c72fcc34Sopenharmony_ci	}
154c72fcc34Sopenharmony_ci}
155c72fcc34Sopenharmony_ci
156c72fcc34Sopenharmony_cistatic int compare_ids(snd_ctl_elem_id_t *id1, snd_ctl_elem_id_t *id2)
157c72fcc34Sopenharmony_ci{
158c72fcc34Sopenharmony_ci	if (id1 == NULL || id2 == NULL)
159c72fcc34Sopenharmony_ci		return 0;
160c72fcc34Sopenharmony_ci	return snd_ctl_elem_id_get_interface(id1) == snd_ctl_elem_id_get_interface(id2) &&
161c72fcc34Sopenharmony_ci	       snd_ctl_elem_id_get_index(id1) == snd_ctl_elem_id_get_index(id2) &&
162c72fcc34Sopenharmony_ci	       strcmp(snd_ctl_elem_id_get_name(id1), snd_ctl_elem_id_get_name(id2)) == 0 &&
163c72fcc34Sopenharmony_ci	       snd_ctl_elem_id_get_device(id1) == snd_ctl_elem_id_get_device(id2) &&
164c72fcc34Sopenharmony_ci	       snd_ctl_elem_id_get_subdevice(id1) == snd_ctl_elem_id_get_subdevice(id2);
165c72fcc34Sopenharmony_ci}
166c72fcc34Sopenharmony_ci
167c72fcc34Sopenharmony_cistatic int in_list(struct id_list *list, snd_ctl_elem_id_t *id)
168c72fcc34Sopenharmony_ci{
169c72fcc34Sopenharmony_ci	int i;
170c72fcc34Sopenharmony_ci	snd_ctl_elem_id_t *id1;
171c72fcc34Sopenharmony_ci
172c72fcc34Sopenharmony_ci	for (i = 0; i < list->size; i++) {
173c72fcc34Sopenharmony_ci		id1 = list->list[i];
174c72fcc34Sopenharmony_ci		if (id1 == NULL)
175c72fcc34Sopenharmony_ci			continue;
176c72fcc34Sopenharmony_ci		if (compare_ids(id, id1))
177c72fcc34Sopenharmony_ci			return 1;
178c72fcc34Sopenharmony_ci	}
179c72fcc34Sopenharmony_ci	return 0;
180c72fcc34Sopenharmony_ci}
181c72fcc34Sopenharmony_ci
182c72fcc34Sopenharmony_cistatic void remove_from_list(struct id_list *list, snd_ctl_elem_id_t *id)
183c72fcc34Sopenharmony_ci{
184c72fcc34Sopenharmony_ci	int i;
185c72fcc34Sopenharmony_ci
186c72fcc34Sopenharmony_ci	for (i = 0; i < list->size; i++) {
187c72fcc34Sopenharmony_ci		if (compare_ids(id, list->list[i])) {
188c72fcc34Sopenharmony_ci			free(list->list[i]);
189c72fcc34Sopenharmony_ci			list->list[i] = NULL;
190c72fcc34Sopenharmony_ci		}
191c72fcc34Sopenharmony_ci	}
192c72fcc34Sopenharmony_ci}
193c72fcc34Sopenharmony_ci
194c72fcc34Sopenharmony_cistatic void add_to_list(struct id_list *list, snd_ctl_elem_id_t *id)
195c72fcc34Sopenharmony_ci{
196c72fcc34Sopenharmony_ci	snd_ctl_elem_id_t *id1;
197c72fcc34Sopenharmony_ci	snd_ctl_elem_id_t **n;
198c72fcc34Sopenharmony_ci	int i;
199c72fcc34Sopenharmony_ci
200c72fcc34Sopenharmony_ci	if (snd_ctl_elem_id_malloc(&id1))
201c72fcc34Sopenharmony_ci		return;
202c72fcc34Sopenharmony_ci	snd_ctl_elem_id_copy(id1, id);
203c72fcc34Sopenharmony_ci	for (i = 0; i < list->size; i++) {
204c72fcc34Sopenharmony_ci		if (list->list[i] == NULL) {
205c72fcc34Sopenharmony_ci			list->list[i] = id1;
206c72fcc34Sopenharmony_ci			return;
207c72fcc34Sopenharmony_ci		}
208c72fcc34Sopenharmony_ci	}
209c72fcc34Sopenharmony_ci	n = realloc(list->list, sizeof(void *) * (list->size + 1));
210c72fcc34Sopenharmony_ci	if (n == NULL)
211c72fcc34Sopenharmony_ci		return;
212c72fcc34Sopenharmony_ci	n[list->size] = id1;
213c72fcc34Sopenharmony_ci	list->size++;
214c72fcc34Sopenharmony_ci	list->list = n;
215c72fcc34Sopenharmony_ci}
216c72fcc34Sopenharmony_ci
217c72fcc34Sopenharmony_cistatic int check_lists(struct card *card, snd_ctl_elem_id_t *id)
218c72fcc34Sopenharmony_ci{
219c72fcc34Sopenharmony_ci	snd_ctl_elem_info_t *info;
220c72fcc34Sopenharmony_ci	snd_ctl_elem_info_alloca(&info);
221c72fcc34Sopenharmony_ci
222c72fcc34Sopenharmony_ci	if (in_list(&card->blacklist, id))
223c72fcc34Sopenharmony_ci		return 0;
224c72fcc34Sopenharmony_ci	if (in_list(&card->whitelist, id))
225c72fcc34Sopenharmony_ci		return 1;
226c72fcc34Sopenharmony_ci	snd_ctl_elem_info_set_id(info, id);
227c72fcc34Sopenharmony_ci	if (snd_ctl_elem_info(card->handle, info) < 0)
228c72fcc34Sopenharmony_ci		return 0;
229c72fcc34Sopenharmony_ci	if (snd_ctl_elem_info_is_writable(info) ||
230c72fcc34Sopenharmony_ci	    snd_ctl_elem_info_is_tlv_writable(info)) {
231c72fcc34Sopenharmony_ci		add_to_list(&card->whitelist, id);
232c72fcc34Sopenharmony_ci		return 1;
233c72fcc34Sopenharmony_ci	} else {
234c72fcc34Sopenharmony_ci		add_to_list(&card->blacklist, id);
235c72fcc34Sopenharmony_ci		return 0;
236c72fcc34Sopenharmony_ci	}
237c72fcc34Sopenharmony_ci}
238c72fcc34Sopenharmony_ci
239c72fcc34Sopenharmony_cistatic int card_events(struct card *card)
240c72fcc34Sopenharmony_ci{
241c72fcc34Sopenharmony_ci	int res = 0;
242c72fcc34Sopenharmony_ci	snd_ctl_event_t *ev;
243c72fcc34Sopenharmony_ci	snd_ctl_event_type_t type;
244c72fcc34Sopenharmony_ci	unsigned int mask;
245c72fcc34Sopenharmony_ci	snd_ctl_elem_id_t *id;
246c72fcc34Sopenharmony_ci	snd_ctl_event_alloca(&ev);
247c72fcc34Sopenharmony_ci	snd_ctl_elem_id_alloca(&id);
248c72fcc34Sopenharmony_ci
249c72fcc34Sopenharmony_ci	while (snd_ctl_read(card->handle, ev) == 1) {
250c72fcc34Sopenharmony_ci		type = snd_ctl_event_get_type(ev);
251c72fcc34Sopenharmony_ci		if (type != SND_CTL_EVENT_ELEM)
252c72fcc34Sopenharmony_ci			continue;
253c72fcc34Sopenharmony_ci		mask = snd_ctl_event_elem_get_mask(ev);
254c72fcc34Sopenharmony_ci		snd_ctl_event_elem_get_id(ev, id);
255c72fcc34Sopenharmony_ci		if (mask == SND_CTL_EVENT_MASK_REMOVE) {
256c72fcc34Sopenharmony_ci			remove_from_list(&card->whitelist, id);
257c72fcc34Sopenharmony_ci			remove_from_list(&card->blacklist, id);
258c72fcc34Sopenharmony_ci			continue;
259c72fcc34Sopenharmony_ci		}
260c72fcc34Sopenharmony_ci		if (mask & SND_CTL_EVENT_MASK_INFO) {
261c72fcc34Sopenharmony_ci			remove_from_list(&card->whitelist, id);
262c72fcc34Sopenharmony_ci			remove_from_list(&card->blacklist, id);
263c72fcc34Sopenharmony_ci		}
264c72fcc34Sopenharmony_ci		if (mask & (SND_CTL_EVENT_MASK_VALUE|
265c72fcc34Sopenharmony_ci			    SND_CTL_EVENT_MASK_ADD|
266c72fcc34Sopenharmony_ci			    SND_CTL_EVENT_MASK_TLV)) {
267c72fcc34Sopenharmony_ci			if (check_lists(card, id))
268c72fcc34Sopenharmony_ci				res = 1;
269c72fcc34Sopenharmony_ci		}
270c72fcc34Sopenharmony_ci	}
271c72fcc34Sopenharmony_ci	return res;
272c72fcc34Sopenharmony_ci}
273c72fcc34Sopenharmony_ci
274c72fcc34Sopenharmony_cistatic long read_pid_file(const char *pidfile)
275c72fcc34Sopenharmony_ci{
276c72fcc34Sopenharmony_ci	int fd, err;
277c72fcc34Sopenharmony_ci	char pid_txt[12];
278c72fcc34Sopenharmony_ci
279c72fcc34Sopenharmony_ci	fd = open(pidfile, O_RDONLY);
280c72fcc34Sopenharmony_ci	if (fd >= 0) {
281c72fcc34Sopenharmony_ci		err = read(fd, pid_txt, 11);
282c72fcc34Sopenharmony_ci		if (err != 11)
283c72fcc34Sopenharmony_ci			err = err < 0 ? -errno : -EIO;
284c72fcc34Sopenharmony_ci		close(fd);
285c72fcc34Sopenharmony_ci		pid_txt[11] = '\0';
286c72fcc34Sopenharmony_ci		return err < 0 ? err : atol(pid_txt);
287c72fcc34Sopenharmony_ci	} else {
288c72fcc34Sopenharmony_ci		return -errno;
289c72fcc34Sopenharmony_ci	}
290c72fcc34Sopenharmony_ci}
291c72fcc34Sopenharmony_ci
292c72fcc34Sopenharmony_cistatic int write_pid_file(const char *pidfile)
293c72fcc34Sopenharmony_ci{
294c72fcc34Sopenharmony_ci	int fd, err;
295c72fcc34Sopenharmony_ci	char pid_txt[14];
296c72fcc34Sopenharmony_ci
297c72fcc34Sopenharmony_ci	sprintf(pid_txt, "%10li\n", (long)getpid());
298c72fcc34Sopenharmony_ci	fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
299c72fcc34Sopenharmony_ci	if (fd >= 0) {
300c72fcc34Sopenharmony_ci		err = write(fd, pid_txt, 11);
301c72fcc34Sopenharmony_ci		if (err != 11) {
302c72fcc34Sopenharmony_ci			err = err < 0 ? -errno : -EIO;
303c72fcc34Sopenharmony_ci			unlink(pidfile);
304c72fcc34Sopenharmony_ci		} else {
305c72fcc34Sopenharmony_ci			err = 0;
306c72fcc34Sopenharmony_ci		}
307c72fcc34Sopenharmony_ci		close(fd);
308c72fcc34Sopenharmony_ci	} else {
309c72fcc34Sopenharmony_ci		err = -errno;
310c72fcc34Sopenharmony_ci	}
311c72fcc34Sopenharmony_ci	return err;
312c72fcc34Sopenharmony_ci}
313c72fcc34Sopenharmony_ci
314c72fcc34Sopenharmony_ciint state_daemon_kill(const char *pidfile, const char *cmd)
315c72fcc34Sopenharmony_ci{
316c72fcc34Sopenharmony_ci	long pid;
317c72fcc34Sopenharmony_ci	int sig = SIGHUP;
318c72fcc34Sopenharmony_ci
319c72fcc34Sopenharmony_ci	if (cmd == NULL) {
320c72fcc34Sopenharmony_ci		error("Specify kill command (quit, rescan or save_and_quit)");
321c72fcc34Sopenharmony_ci		return -EINVAL;
322c72fcc34Sopenharmony_ci	}
323c72fcc34Sopenharmony_ci	if (strcmp(cmd, "rescan") == 0)
324c72fcc34Sopenharmony_ci		sig = SIGUSR1;
325c72fcc34Sopenharmony_ci	else if (strcmp(cmd, "save_and_quit") == 0)
326c72fcc34Sopenharmony_ci		sig = SIGUSR2;
327c72fcc34Sopenharmony_ci	else if (strcmp(cmd, "quit") == 0)
328c72fcc34Sopenharmony_ci		sig = SIGTERM;
329c72fcc34Sopenharmony_ci	if (sig == SIGHUP) {
330c72fcc34Sopenharmony_ci		error("Unknown kill command '%s'", cmd);
331c72fcc34Sopenharmony_ci		return -EINVAL;
332c72fcc34Sopenharmony_ci	}
333c72fcc34Sopenharmony_ci	pid = read_pid_file(pidfile);
334c72fcc34Sopenharmony_ci	if (pid > 0) {
335c72fcc34Sopenharmony_ci		if (kill(pid, sig) >= 0)
336c72fcc34Sopenharmony_ci			return 0;
337c72fcc34Sopenharmony_ci		return -errno;
338c72fcc34Sopenharmony_ci	}
339c72fcc34Sopenharmony_ci	return 0;
340c72fcc34Sopenharmony_ci}
341c72fcc34Sopenharmony_ci
342c72fcc34Sopenharmony_cistatic int check_another_instance(const char *pidfile)
343c72fcc34Sopenharmony_ci{
344c72fcc34Sopenharmony_ci	long pid;
345c72fcc34Sopenharmony_ci
346c72fcc34Sopenharmony_ci	pid = read_pid_file(pidfile);
347c72fcc34Sopenharmony_ci	if (pid >= 0) {
348c72fcc34Sopenharmony_ci		/* invoke new card rescan */
349c72fcc34Sopenharmony_ci		if (kill(pid, SIGUSR1) >= 0) {
350c72fcc34Sopenharmony_ci			usleep(1000);
351c72fcc34Sopenharmony_ci			pid = read_pid_file(pidfile);
352c72fcc34Sopenharmony_ci			if (pid >= 0)
353c72fcc34Sopenharmony_ci				return 1;
354c72fcc34Sopenharmony_ci		}
355c72fcc34Sopenharmony_ci	}
356c72fcc34Sopenharmony_ci	return 0;
357c72fcc34Sopenharmony_ci}
358c72fcc34Sopenharmony_ci
359c72fcc34Sopenharmony_ciint state_daemon(const char *file, const char *cardname, int period,
360c72fcc34Sopenharmony_ci		 const char *pidfile)
361c72fcc34Sopenharmony_ci{
362c72fcc34Sopenharmony_ci	int count = 0, pcount, psize = 0, i, j, k, changed = 0;
363c72fcc34Sopenharmony_ci	time_t last_write, now;
364c72fcc34Sopenharmony_ci	unsigned short revents;
365c72fcc34Sopenharmony_ci	struct card **cards = NULL;
366c72fcc34Sopenharmony_ci	struct pollfd *pfd = NULL, *pfdn;
367c72fcc34Sopenharmony_ci
368c72fcc34Sopenharmony_ci	if (check_another_instance(pidfile))
369c72fcc34Sopenharmony_ci		return 0;
370c72fcc34Sopenharmony_ci	rescan = 1;
371c72fcc34Sopenharmony_ci	signal(SIGABRT, signal_handler_quit);
372c72fcc34Sopenharmony_ci	signal(SIGTERM, signal_handler_quit);
373c72fcc34Sopenharmony_ci	signal(SIGINT, signal_handler_quit);
374c72fcc34Sopenharmony_ci	signal(SIGUSR1, signal_handler_rescan);
375c72fcc34Sopenharmony_ci	signal(SIGUSR2, signal_handler_save_and_quit);
376c72fcc34Sopenharmony_ci	write_pid_file(pidfile);
377c72fcc34Sopenharmony_ci	time(&last_write);
378c72fcc34Sopenharmony_ci	while (!quit || save_now) {
379c72fcc34Sopenharmony_ci		if (save_now)
380c72fcc34Sopenharmony_ci			goto save;
381c72fcc34Sopenharmony_ci		if (rescan) {
382c72fcc34Sopenharmony_ci			if (cardname) {
383c72fcc34Sopenharmony_ci				add_card(&cards, &count, cardname);
384c72fcc34Sopenharmony_ci			} else {
385c72fcc34Sopenharmony_ci				add_cards(&cards, &count);
386c72fcc34Sopenharmony_ci			}
387c72fcc34Sopenharmony_ci			snd_config_update_free_global();
388c72fcc34Sopenharmony_ci			rescan = 0;
389c72fcc34Sopenharmony_ci		}
390c72fcc34Sopenharmony_ci		for (i = pcount = 0; i < count; i++) {
391c72fcc34Sopenharmony_ci			if (cards[i] == NULL)
392c72fcc34Sopenharmony_ci				continue;
393c72fcc34Sopenharmony_ci			pcount += cards[i]->pfds;
394c72fcc34Sopenharmony_ci		}
395c72fcc34Sopenharmony_ci		if (pcount > psize) {
396c72fcc34Sopenharmony_ci			pfdn = realloc(pfd, sizeof(struct pollfd) * pcount);
397c72fcc34Sopenharmony_ci			if (pfdn) {
398c72fcc34Sopenharmony_ci				psize = pcount;
399c72fcc34Sopenharmony_ci				pfd = pfdn;
400c72fcc34Sopenharmony_ci			} else {
401c72fcc34Sopenharmony_ci				error("No enough memory...");
402c72fcc34Sopenharmony_ci				goto out;
403c72fcc34Sopenharmony_ci			}
404c72fcc34Sopenharmony_ci		}
405c72fcc34Sopenharmony_ci		for (i = j = 0; i < count; i++) {
406c72fcc34Sopenharmony_ci			if (cards[i] == NULL)
407c72fcc34Sopenharmony_ci				continue;
408c72fcc34Sopenharmony_ci			k = snd_ctl_poll_descriptors(cards[i]->handle, pfd + j, pcount - j);
409c72fcc34Sopenharmony_ci			if (k != cards[i]->pfds) {
410c72fcc34Sopenharmony_ci				error("poll prepare failed: %i", k);
411c72fcc34Sopenharmony_ci				goto out;
412c72fcc34Sopenharmony_ci			}
413c72fcc34Sopenharmony_ci			j += k;
414c72fcc34Sopenharmony_ci		}
415c72fcc34Sopenharmony_ci		i = poll(pfd, j, (period / 2) * 1000);
416c72fcc34Sopenharmony_ci		if (i < 0 && errno == EINTR)
417c72fcc34Sopenharmony_ci			continue;
418c72fcc34Sopenharmony_ci		if (i < 0) {
419c72fcc34Sopenharmony_ci			error("poll failed: %s", strerror(errno));
420c72fcc34Sopenharmony_ci			break;
421c72fcc34Sopenharmony_ci		}
422c72fcc34Sopenharmony_ci		time(&now);
423c72fcc34Sopenharmony_ci		for (i = j = 0; i < count; i++) {
424c72fcc34Sopenharmony_ci			if (cards[i] == NULL)
425c72fcc34Sopenharmony_ci				continue;
426c72fcc34Sopenharmony_ci			k = snd_ctl_poll_descriptors_revents(cards[i]->handle,
427c72fcc34Sopenharmony_ci					pfd + j, cards[i]->pfds, &revents);
428c72fcc34Sopenharmony_ci			if (k < 0) {
429c72fcc34Sopenharmony_ci				error("poll post failed: %i\n", k);
430c72fcc34Sopenharmony_ci				goto out;
431c72fcc34Sopenharmony_ci			}
432c72fcc34Sopenharmony_ci			j += cards[i]->pfds;
433c72fcc34Sopenharmony_ci			if (revents & (POLLERR|POLLNVAL)) {
434c72fcc34Sopenharmony_ci				card_free(&cards[i]);
435c72fcc34Sopenharmony_ci			} else if (revents & POLLIN) {
436c72fcc34Sopenharmony_ci				if (card_events(cards[i])) {
437c72fcc34Sopenharmony_ci					/* delay the write */
438c72fcc34Sopenharmony_ci					if (!changed)
439c72fcc34Sopenharmony_ci						last_write = now;
440c72fcc34Sopenharmony_ci					changed = 1;
441c72fcc34Sopenharmony_ci				}
442c72fcc34Sopenharmony_ci			}
443c72fcc34Sopenharmony_ci		}
444c72fcc34Sopenharmony_ci		if ((now - last_write >= period && changed) || save_now) {
445c72fcc34Sopenharmony_cisave:
446c72fcc34Sopenharmony_ci			changed = save_now = 0;
447c72fcc34Sopenharmony_ci			save_state(file, cardname);
448c72fcc34Sopenharmony_ci		}
449c72fcc34Sopenharmony_ci	}
450c72fcc34Sopenharmony_ciout:
451c72fcc34Sopenharmony_ci	free(pfd);
452c72fcc34Sopenharmony_ci	remove(pidfile);
453c72fcc34Sopenharmony_ci	if (cards) {
454c72fcc34Sopenharmony_ci		for (i = 0; i < count; i++)
455c72fcc34Sopenharmony_ci			card_free(&cards[i]);
456c72fcc34Sopenharmony_ci		free(cards);
457c72fcc34Sopenharmony_ci	}
458c72fcc34Sopenharmony_ci	return 0;
459c72fcc34Sopenharmony_ci}
460