2 * Advanced Linux Sound Architecture Control Program
3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include <alsa/asoundlib.h>
36 snd_ctl_elem_id_t **list;
44 struct id_list whitelist;
45 struct id_list blacklist;
49 static int rescan = 0;
50 static int save_now = 0;
52 static void signal_handler_quit(int sig)
55 signal(sig, signal_handler_quit);
58 static void signal_handler_save_and_quit(int sig)
61 signal(sig, signal_handler_quit);
64 static void signal_handler_rescan(int sig)
67 signal(sig, signal_handler_rescan);
70 static void free_list(struct id_list *list)
74 for (i = 0; i < list->size; i++)
79 static void card_free(struct card **card)
81 struct card *c = *card;
83 free_list(&c->blacklist);
84 free_list(&c->whitelist);
86 snd_ctl_close(c->handle);
91 static void add_card(struct card ***cards, int *count, const char *cardname)
93 struct card *card, **cc;
97 index = snd_card_get_index(cardname);
100 for (i = 0, findex = -1; i < *count; i++) {
101 if ((*cards)[i] == NULL) {
104 if ((*cards)[i]->index == index)
108 card = calloc(1, sizeof(*card));
112 sprintf(device, "hw:%i", index);
113 if (snd_ctl_open(&card->handle, device, SND_CTL_READONLY|SND_CTL_NONBLOCK) < 0) {
117 card->pfds = snd_ctl_poll_descriptors_count(card->handle);
118 if (card->pfds < 0) {
122 if (snd_ctl_subscribe_events(card->handle, 1) < 0) {
127 (*cards)[findex] = card;
129 cc = realloc(*cards, sizeof(void *) * (*count + 1));
140 static void add_cards(struct card ***cards, int *count)
146 if (snd_card_next(&card) < 0)
151 sprintf(cardname, "%i", card);
152 add_card(cards, count, cardname);
157 static int compare_ids(snd_ctl_elem_id_t *id1, snd_ctl_elem_id_t *id2)
159 if (id1 == NULL || id2 == NULL)
161 return snd_ctl_elem_id_get_interface(id1) == snd_ctl_elem_id_get_interface(id2) &&
162 snd_ctl_elem_id_get_index(id1) == snd_ctl_elem_id_get_index(id2) &&
163 strcmp(snd_ctl_elem_id_get_name(id1), snd_ctl_elem_id_get_name(id2)) == 0 &&
164 snd_ctl_elem_id_get_device(id1) == snd_ctl_elem_id_get_device(id2) &&
165 snd_ctl_elem_id_get_subdevice(id1) == snd_ctl_elem_id_get_subdevice(id2);
168 static int in_list(struct id_list *list, snd_ctl_elem_id_t *id)
171 snd_ctl_elem_id_t *id1;
173 for (i = 0; i < list->size; i++) {
177 if (compare_ids(id, id1))
183 static void remove_from_list(struct id_list *list, snd_ctl_elem_id_t *id)
187 for (i = 0; i < list->size; i++) {
188 if (compare_ids(id, list->list[i])) {
190 list->list[i] = NULL;
195 static void add_to_list(struct id_list *list, snd_ctl_elem_id_t *id)
197 snd_ctl_elem_id_t *id1;
198 snd_ctl_elem_id_t **n;
201 if (snd_ctl_elem_id_malloc(&id1))
203 snd_ctl_elem_id_copy(id1, id);
204 for (i = 0; i < list->size; i++) {
205 if (list->list[i] == NULL) {
210 n = realloc(list->list, sizeof(void *) * (list->size + 1));
218 static int check_lists(struct card *card, snd_ctl_elem_id_t *id)
220 snd_ctl_elem_info_t *info;
221 snd_ctl_elem_info_alloca(&info);
223 if (in_list(&card->blacklist, id))
225 if (in_list(&card->whitelist, id))
227 snd_ctl_elem_info_set_id(info, id);
228 if (snd_ctl_elem_info(card->handle, info) < 0)
230 if (snd_ctl_elem_info_is_writable(info) ||
231 snd_ctl_elem_info_is_tlv_writable(info)) {
232 add_to_list(&card->whitelist, id);
235 add_to_list(&card->blacklist, id);
240 static int card_events(struct card *card)
244 snd_ctl_event_type_t type;
246 snd_ctl_elem_id_t *id;
247 snd_ctl_event_alloca(&ev);
248 snd_ctl_elem_id_alloca(&id);
250 while (snd_ctl_read(card->handle, ev) == 1) {
251 type = snd_ctl_event_get_type(ev);
252 if (type != SND_CTL_EVENT_ELEM)
254 mask = snd_ctl_event_elem_get_mask(ev);
255 snd_ctl_event_elem_get_id(ev, id);
256 if (mask == SND_CTL_EVENT_MASK_REMOVE) {
257 remove_from_list(&card->whitelist, id);
258 remove_from_list(&card->blacklist, id);
261 if (mask & SND_CTL_EVENT_MASK_INFO) {
262 remove_from_list(&card->whitelist, id);
263 remove_from_list(&card->blacklist, id);
265 if (mask & (SND_CTL_EVENT_MASK_VALUE|
266 SND_CTL_EVENT_MASK_ADD|
267 SND_CTL_EVENT_MASK_TLV)) {
268 if (check_lists(card, id))
275 static long read_pid_file(const char *pidfile)
280 fd = open(pidfile, O_RDONLY);
282 err = read(fd, pid_txt, 11);
284 err = err < 0 ? -errno : -EIO;
287 return atol(pid_txt);
293 static int write_pid_file(const char *pidfile)
298 sprintf(pid_txt, "%10li\n", (long)getpid());
299 fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
301 err = write(fd, pid_txt, 11);
303 err = err < 0 ? -errno : -EIO;
315 int state_daemon_kill(const char *pidfile, const char *cmd)
321 error("Specify kill command (quit, rescan or save_and_quit)");
324 if (strcmp(cmd, "rescan") == 0)
326 else if (strcmp(cmd, "save_and_quit") == 0)
328 else if (strcmp(cmd, "quit") == 0)
331 error("Unknown kill command '%s'", cmd);
334 pid = read_pid_file(pidfile);
336 if (kill(pid, sig) >= 0)
343 static int check_another_instance(const char *pidfile)
347 pid = read_pid_file(pidfile);
349 /* invoke new card rescan */
350 if (kill(pid, SIGUSR1) >= 0) {
352 pid = read_pid_file(pidfile);
360 int state_daemon(const char *file, const char *cardname, int period,
363 int count = 0, pcount, psize = 0, i, j, k, changed = 0;
364 time_t last_write, now;
365 unsigned short revents;
366 struct card **cards = NULL;
367 struct pollfd *pfd = NULL, *pfdn;
369 if (check_another_instance(pidfile))
372 signal(SIGABRT, signal_handler_quit);
373 signal(SIGTERM, signal_handler_quit);
374 signal(SIGINT, signal_handler_quit);
375 signal(SIGUSR1, signal_handler_rescan);
376 signal(SIGUSR2, signal_handler_save_and_quit);
377 write_pid_file(pidfile);
379 while (!quit || save_now) {
384 add_card(&cards, &count, cardname);
386 add_cards(&cards, &count);
388 snd_config_update_free_global();
391 for (i = pcount = 0; i < count; i++) {
392 if (cards[i] == NULL)
394 pcount += cards[i]->pfds;
396 if (pcount > psize) {
397 pfdn = realloc(pfd, sizeof(struct pollfd) * pcount);
402 error("No enough memory...");
406 for (i = j = 0; i < count; i++) {
407 if (cards[i] == NULL)
409 k = snd_ctl_poll_descriptors(cards[i]->handle, pfd + j, pcount - j);
410 if (k != cards[i]->pfds) {
411 error("poll prepare failed: %i", k);
416 i = poll(pfd, j, (period / 2) * 1000);
417 if (i < 0 && errno == EINTR)
420 error("poll failed: %s", strerror(errno));
424 for (i = j = 0; i < count; i++) {
425 if (cards[i] == NULL)
427 k = snd_ctl_poll_descriptors_revents(cards[i]->handle,
428 pfd + j, cards[i]->pfds, &revents);
430 error("poll post failed: %i\n", k);
434 if (revents & (POLLERR|POLLNVAL)) {
435 card_free(&cards[i]);
436 } else if (revents & POLLIN) {
437 if (card_events(cards[i])) {
438 /* delay the write */
445 if ((now - last_write >= period && changed) || save_now) {
447 changed = save_now = 0;
448 save_state(file, cardname);
454 for (i = 0; i < count; i++)
455 card_free(&cards[i]);