/** * \file async.c * \brief Async notification helpers * \author Abramo Bagnara * \date 2001 */ /* * Async notification helpers * Copyright (c) 2001 by Abramo Bagnara * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "pcm/pcm_local.h" #include "control/control_local.h" #include static struct sigaction previous_action; #define MAX_SIG_FUNCTION_CODE 10 /* i.e. SIG_DFL SIG_IGN SIG_HOLD et al */ #ifdef SND_ASYNC_RT_SIGNAL /** async signal number */ static int snd_async_signo; void snd_async_init(void) __attribute__ ((constructor)); void snd_async_init(void) { snd_async_signo = __libc_allocate_rtsig(0); if (snd_async_signo < 0) { SNDERR("Unable to find a RT signal to use for snd_async"); exit(1); } } #else /** async signal number */ static const int snd_async_signo = SIGIO; #endif static LIST_HEAD(snd_async_handlers); static void snd_async_handler(int signo ATTRIBUTE_UNUSED, siginfo_t *siginfo, void *context ATTRIBUTE_UNUSED) { int fd; struct list_head *i; //assert(siginfo->si_code == SI_SIGIO); if (signo == SIGIO && (unsigned long)(previous_action.sa_sigaction) > MAX_SIG_FUNCTION_CODE) previous_action.sa_sigaction(signo, siginfo, context); fd = siginfo->si_fd; list_for_each(i, &snd_async_handlers) { snd_async_handler_t *h = list_entry(i, snd_async_handler_t, glist); if (h->fd == fd && h->callback) h->callback(h); } } /** * \brief Registers an async handler. * \param handler The function puts the pointer to the new async handler * object at the address specified by \p handler. * \param fd The file descriptor to be associated with the callback. * \param callback The async callback function. * \param private_data Private data for the async callback function. * \result Zero if successful, otherwise a negative error code. * * This function associates the callback function with the given file, * and saves this association in a \c snd_async_handler_t object. * * Whenever the \c SIGIO signal is raised for the file \p fd, the callback * function will be called with its parameter pointing to the async handler * object returned by this function. * * The ALSA \c sigaction handler for the \c SIGIO signal automatically * multiplexes the notifications to the registered async callbacks. * However, the application is responsible for instructing the device driver * to generate the \c SIGIO signal. * * The \c SIGIO signal may have been replaced with another signal, * see #snd_async_handler_get_signo. * * When the async handler isn't needed anymore, you must delete it with * #snd_async_del_handler. * * \see snd_async_add_pcm_handler, snd_async_add_ctl_handler */ int snd_async_add_handler(snd_async_handler_t **handler, int fd, snd_async_callback_t callback, void *private_data) { snd_async_handler_t *h; int was_empty; assert(handler); h = malloc(sizeof(*h)); if (!h) return -ENOMEM; h->fd = fd; h->callback = callback; h->private_data = private_data; was_empty = list_empty(&snd_async_handlers); list_add_tail(&h->glist, &snd_async_handlers); INIT_LIST_HEAD(&h->hlist); *handler = h; if (was_empty) { int err; struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_flags = SA_RESTART | SA_SIGINFO; act.sa_sigaction = snd_async_handler; sigemptyset(&act.sa_mask); assert(!previous_action.sa_sigaction); err = sigaction(snd_async_signo, &act, &previous_action); if (err < 0) { SYSERR("sigaction"); return -errno; } } return 0; } /** * \brief Deletes an async handler. * \param handler Handle of the async handler to delete. * \result Zero if successful, otherwise a negative error code. */ int snd_async_del_handler(snd_async_handler_t *handler) { int err = 0; int was_empty = list_empty(&snd_async_handlers); assert(handler); list_del(&handler->glist); if (!was_empty && list_empty(&snd_async_handlers)) { err = sigaction(snd_async_signo, &previous_action, NULL); if (err < 0) { SYSERR("sigaction"); return -errno; } memset(&previous_action, 0, sizeof(previous_action)); } if (handler->type == SND_ASYNC_HANDLER_GENERIC) goto _end; if (!list_empty(&handler->hlist)) list_del(&handler->hlist); if (!list_empty(&handler->hlist)) goto _end; switch (handler->type) { #ifdef BUILD_PCM case SND_ASYNC_HANDLER_PCM: err = snd_pcm_async(handler->u.pcm, -1, 1); break; #endif case SND_ASYNC_HANDLER_CTL: err = snd_ctl_async(handler->u.ctl, -1, 1); break; default: assert(0); } _end: free(handler); return err; } /** * \brief Returns the signal number assigned to an async handler. * \param handler Handle to an async handler. * \result The signal number if successful, otherwise a negative error code. * * The signal number for async handlers usually is \c SIGIO, * but wizards can redefine it to a realtime signal * when compiling the ALSA library. */ int snd_async_handler_get_signo(snd_async_handler_t *handler) { assert(handler); return snd_async_signo; } /** * \brief Returns the file descriptor assigned to an async handler. * \param handler Handle to an async handler. * \result The file descriptor if successful, otherwise a negative error code. */ int snd_async_handler_get_fd(snd_async_handler_t *handler) { assert(handler); return handler->fd; } /** * \brief Returns the private data assigned to an async handler. * \param handler Handle to an async handler. * \result The \c private_data value registered with the async handler. */ void *snd_async_handler_get_callback_private(snd_async_handler_t *handler) { assert(handler); return handler->private_data; }