| /** | |
| * \file async.c | |
| * \brief Async notification helpers | |
| * \author Abramo Bagnara <abramo@alsa-project.org> | |
| * \date 2001 | |
| */ | |
| /* | |
| * Async notification helpers | |
| * Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org> | |
| * | |
| * 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 <signal.h> | |
| static struct sigaction previous_action; | |
| #ifndef DOC_HIDDEN | |
| #define MAX_SIG_FUNCTION_CODE 10 /* i.e. SIG_DFL SIG_IGN SIG_HOLD et al */ | |
| #endif /* DOC_HIDDEN */ | |
| #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) | |
| { | |
| #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) | |
| /* siginfo_t does not have si_fd */ | |
| struct list_head *i; | |
| list_for_each(i, &snd_async_handlers) { | |
| snd_async_handler_t *h = list_entry(i, snd_async_handler_t, glist); | |
| if (h->callback) | |
| h->callback(h); | |
| } | |
| #else | |
| 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); | |
| } | |
| #endif | |
| } | |
| /** | |
| * \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, err2 = 0; | |
| int was_empty; | |
| assert(handler); | |
| if (handler->type != SND_ASYNC_HANDLER_GENERIC) { | |
| if (!list_empty(&handler->hlist)) | |
| list_del(&handler->hlist); | |
| if (!list_empty(&handler->hlist)) | |
| goto _glist; | |
| switch (handler->type) { | |
| #ifdef BUILD_PCM | |
| case SND_ASYNC_HANDLER_PCM: | |
| err2 = snd_pcm_async(handler->u.pcm, -1, 1); | |
| break; | |
| #endif | |
| case SND_ASYNC_HANDLER_CTL: | |
| err2 = snd_ctl_async(handler->u.ctl, -1, 1); | |
| break; | |
| default: | |
| assert(0); | |
| } | |
| } | |
| _glist: | |
| was_empty = list_empty(&snd_async_handlers); | |
| 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)); | |
| } | |
| free(handler); | |
| return err ? err : err2; | |
| } | |
| /** | |
| * \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; | |
| } | |