From bf858b73120b05d46ec932cb426ebf7b8b2a850b Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 16 Jul 2001 11:15:28 +0000 Subject: [PATCH] Updated timer interface to follow new uniform configuration style. --- include/timer.h | 61 ++++-- src/conf/alsa.conf | 86 ++++++++ src/timer/Makefile.am | 2 +- src/timer/timer.c | 398 +++++++++++++++++++++++++------------ src/timer/timer_hw.c | 261 ++++++++++++++++++++++++ src/timer/timer_local.h | 64 ++++++ src/timer/timer_query.c | 193 ++++++++++++++++++ src/timer/timer_query_hw.c | 107 ++++++++++ 8 files changed, 1029 insertions(+), 143 deletions(-) create mode 100644 src/timer/timer_hw.c create mode 100644 src/timer/timer_local.h create mode 100644 src/timer/timer_query.c create mode 100644 src/timer/timer_query_hw.c diff --git a/include/timer.h b/include/timer.h index 2e830b7f..06b1842d 100644 --- a/include/timer.h +++ b/include/timer.h @@ -11,51 +11,78 @@ * \{ */ +/** timer identification */ typedef struct sndrv_timer_id snd_timer_id_t; +/** timer select structure */ typedef struct sndrv_timer_select snd_timer_select_t; +/** timer info structure */ typedef struct sndrv_timer_info snd_timer_info_t; +/** timer parameters structure */ typedef struct sndrv_timer_params snd_timer_params_t; +/** timer status structure */ typedef struct sndrv_timer_status snd_timer_status_t; +/** timer read structure */ typedef struct sndrv_timer_read snd_timer_read_t; +/** timer master class */ typedef enum _snd_timer_class { - SND_TIMER_CLASS_NONE = SNDRV_TIMER_CLASS_NONE, - SND_TIMER_CLASS_SLAVE = SNDRV_TIMER_CLASS_SLAVE, - SND_TIMER_CLASS_GLOBAL = SNDRV_TIMER_CLASS_GLOBAL, - SND_TIMER_CLASS_CARD = SNDRV_TIMER_CLASS_CARD, - SND_TIMER_CLASS_PCM = SNDRV_TIMER_CLASS_PCM, - SND_TIMER_CLASS_LAST = SNDRV_TIMER_CLASS_LAST, + SND_TIMER_CLASS_NONE = SNDRV_TIMER_CLASS_NONE, /**< invalid */ + SND_TIMER_CLASS_SLAVE = SNDRV_TIMER_CLASS_SLAVE, /**< slave timer */ + SND_TIMER_CLASS_GLOBAL = SNDRV_TIMER_CLASS_GLOBAL, /**< global timer */ + SND_TIMER_CLASS_CARD = SNDRV_TIMER_CLASS_CARD, /**< card timer */ + SND_TIMER_CLASS_PCM = SNDRV_TIMER_CLASS_PCM, /**< PCM timer */ + SND_TIMER_CLASS_LAST = SNDRV_TIMER_CLASS_LAST, /**< last timer */ } snd_timer_class_t; +/** timer slave class */ typedef enum _snd_timer_slave_class { - SND_TIMER_SCLASS_NONE = SNDRV_TIMER_SCLASS_NONE, - SND_TIMER_SCLASS_APPLICATION = SNDRV_TIMER_SCLASS_APPLICATION, - SND_TIMER_SCLASS_SEQUENCER = SNDRV_TIMER_SCLASS_SEQUENCER, - SND_TIMER_SCLASS_OSS_SEQUENCER = SNDRV_TIMER_SCLASS_OSS_SEQUENCER, - SND_TIMER_SCLASS_LAST = SNDRV_TIMER_SCLASS_LAST, + SND_TIMER_SCLASS_NONE = SNDRV_TIMER_SCLASS_NONE, /**< none */ + SND_TIMER_SCLASS_APPLICATION = SNDRV_TIMER_SCLASS_APPLICATION, /**< for internal use */ + SND_TIMER_SCLASS_SEQUENCER = SNDRV_TIMER_SCLASS_SEQUENCER, /**< sequencer timer */ + SND_TIMER_SCLASS_OSS_SEQUENCER = SNDRV_TIMER_SCLASS_OSS_SEQUENCER, /**< OSS sequencer timer */ + SND_TIMER_SCLASS_LAST = SNDRV_TIMER_SCLASS_LAST, /**< last slave timer */ } snd_timer_slave_class_t; +/** global timer - system */ #define SND_TIMER_GLOBAL_SYSTEM SNDRV_TIMER_GLOBAL_SYSTEM +/** global timer - RTC */ #define SND_TIMER_GLOBAL_RTC SNDRV_TIMER_GLOBAL_RTC +/** timer cannot be controlled */ #define SND_TIMER_FLG_SLAVE SNDRV_TIMER_FLG_SLAVE -#define SND_TIMER_PARBIT_FLAGS SNDRV_TIMER_PARBIT_FLAGS -#define SND_TIMER_PARBIT_TICKS SNDRV_TIMER_PARBIT_TICKS -#define SND_TIMER_PARBIT_QUEUE_SIZE SNDRV_TIMER_PARBIT_QUEUE_SIZE +/** timer supports auto-start */ #define SND_TIMER_PSFLG_AUTO SNDRV_TIMER_PSFLG_AUTO +/** timer open mode flag - nonblock */ +#define SND_TIMER_OPEN_NONBLOCK 1 + +/** timer handle type */ +typedef enum _snd_timer_type { + /** Kernel level HwDep */ + SND_TIMER_TYPE_HW, + /** Shared memory client timer (not yet implemented) */ + SND_TIMER_TYPE_SHM, + /** INET client timer (not yet implemented) */ + SND_TIMER_TYPE_INET, +} snd_timer_type_t; + +/** timer query handle */ +typedef struct _snd_timer_query snd_timer_query_t; +/** timer handle */ typedef struct _snd_timer snd_timer_t; #ifdef __cplusplus extern "C" { #endif -int snd_timer_open(snd_timer_t **handle, int mode); +int snd_timer_query_open(snd_timer_query_t **handle, const char *name, int mode); +int snd_timer_query_close(snd_timer_query_t *handle); +int snd_timer_query_next_device(snd_timer_query_t *handle, snd_timer_id_t *tid); + +int snd_timer_open(snd_timer_t **handle, const char *name, int mode); int snd_timer_close(snd_timer_t *handle); int snd_timer_poll_descriptors_count(snd_timer_t *handle); int snd_timer_poll_descriptors(snd_timer_t *handle, struct pollfd *pfds, unsigned int space); -int snd_timer_next_device(snd_timer_t *handle, snd_timer_id_t *tid); -int snd_timer_select(snd_timer_t *handle, snd_timer_select_t *tselect); int snd_timer_info(snd_timer_t *handle, snd_timer_info_t *timer); int snd_timer_params(snd_timer_t *handle, snd_timer_params_t *params); int snd_timer_status(snd_timer_t *handle, snd_timer_status_t *status); diff --git a/src/conf/alsa.conf b/src/conf/alsa.conf index 82150c67..91bee5ba 100644 --- a/src/conf/alsa.conf +++ b/src/conf/alsa.conf @@ -68,6 +68,11 @@ defaults.rawmidi.device 0 defaults.rawmidi.subdevice -1 defaults.hwdep.card 0 defaults.hwdep.device 0 +defaults.timer.class 2 +defaults.timer.sclass 0 +defaults.timer.card 0 +defaults.timer.device 0 +defaults.timer.subdevice 0 # # PCM interface @@ -456,3 +461,84 @@ hwdep.default { } } } + +# +# Timer interface +# + +timer_query.hw { + type hw +} + +timer_query.default { + type hw +} + +timer.hw { + @args [ CLASS SCLASS CARD DEV SUBDEV ] + @args.CLASS { + type integer + default { + @func refer + name defaults.timer.class + } + } + @args.SCLASS { + type integer + default { + @func refer + name defaults.timer.sclass + } + } + @args.CARD { + type string + default { + @func refer + name defaults.timer.card + } + } + @args.DEV { + type integer + default { + @func refer + name defaults.timer.device + } + } + @args.SUBDEV { + type integer + default { + @func refer + name defaults.timer.subdevice + } + } + type hw + class $CLASS + sclass $SCLASS + card $CARD + device $DEV + subdevice $SUBDEV +} + +timer.default { + type hw + class { + @func refer + name defaults.timer.class + } + sclass { + @func refer + name defaults.timer.sclass + } + card { + @func refer + name defaults.timer.card + } + device { + @func refer + name defaults.timer.device + } + subdevice { + @func refer + name defaults.timer.subdevice + } +} diff --git a/src/timer/Makefile.am b/src/timer/Makefile.am index f188cbfd..d44831a7 100644 --- a/src/timer/Makefile.am +++ b/src/timer/Makefile.am @@ -1,6 +1,6 @@ EXTRA_LTLIBRARIES=libtimer.la -libtimer_la_SOURCES = timer.c +libtimer_la_SOURCES = timer.c timer_hw.c timer_query.c timer_query_hw.c all: libtimer.la diff --git a/src/timer/timer.c b/src/timer/timer.c index f9552914..6c14b341 100644 --- a/src/timer/timer.c +++ b/src/timer/timer.c @@ -1,6 +1,13 @@ +/* + * \file timer/timer.c + * \author Jaroslav Kysela + * \date 1998-2001 + * + * Timer Interface is designed to access timers. + */ /* * Timer Interface - main file - * Copyright (c) 1998 by Jaroslav Kysela + * Copyright (c) 1998-2001 by Jaroslav Kysela * * * This library is free software; you can redistribute it and/or modify @@ -24,179 +31,320 @@ #include #include #include +#include #include -#include "local.h" - -#define SNDRV_FILE_TIMER "/dev/snd/timer" -#define SNDRV_TIMER_VERSION_MAX SNDRV_PROTOCOL_VERSION(2, 0, 0) - -struct _snd_timer { - int fd; -}; +#include "timer_local.h" -int snd_timer_open(snd_timer_t **handle, int mode ATTRIBUTE_UNUSED) +static int snd_timer_open_conf(snd_timer_t **timer, + const char *name, snd_config_t *timer_root, + snd_config_t *timer_conf, int mode) { - int fd, ver; - snd_timer_t *tmr; - - *handle = NULL; - - if ((fd = open(SNDRV_FILE_TIMER, O_RDONLY)) < 0) - return -errno; - if (ioctl(fd, SNDRV_TIMER_IOCTL_PVERSION, &ver) < 0) { - close(fd); - return -errno; + const char *str; + char buf[256]; + int err; + snd_config_t *conf, *type_conf; + snd_config_iterator_t i, next; + const char *lib = NULL, *open_name = NULL; + int (*open_func)(snd_timer_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL; + void *h; + if (snd_config_get_type(timer_conf) != SND_CONFIG_TYPE_COMPOUND) { + if (name) + SNDERR("Invalid type for TIMER %s definition", name); + else + SNDERR("Invalid type for TIMER definition"); + return -EINVAL; + } + err = snd_config_search(timer_conf, "type", &conf); + if (err < 0) { + SNDERR("type is not defined"); + return err; + } + err = snd_config_get_string(conf, &str); + if (err < 0) { + SNDERR("Invalid type for %s", snd_config_get_id(conf)); + return err; + } + err = snd_config_search_definition(timer_root, "timer_type", str, &type_conf); + if (err >= 0) { + if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("Invalid type for TIMER type %s definition", str); + goto _err; + } + snd_config_for_each(i, next, type_conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + if (strcmp(id, "comment") == 0) + continue; + if (strcmp(id, "lib") == 0) { + err = snd_config_get_string(n, &lib); + if (err < 0) { + SNDERR("Invalid type for %s", id); + goto _err; + } + continue; + } + if (strcmp(id, "open") == 0) { + err = snd_config_get_string(n, &open_name); + if (err < 0) { + SNDERR("Invalid type for %s", id); + goto _err; + } + continue; + } + SNDERR("Unknown field %s", id); + err = -EINVAL; + goto _err; + } } - if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_TIMER_VERSION_MAX)) { - close(fd); - return -SND_ERROR_INCOMPATIBLE_VERSION; + if (!open_name) { + open_name = buf; + snprintf(buf, sizeof(buf), "_snd_timer_%s_open", str); } - tmr = (snd_timer_t *) calloc(1, sizeof(snd_timer_t)); - if (tmr == NULL) { - close(fd); - return -ENOMEM; + if (!lib) + lib = ALSA_LIB; + h = dlopen(lib, RTLD_NOW); + open_func = h ? dlsym(h, open_name) : NULL; + if (!h) { + SNDERR("Cannot open shared library %s", lib); + err = -ENOENT; + } else if (!open_func) { + SNDERR("symbol %s is not defined inside %s", open_name, lib); + dlclose(h); + err = -ENXIO; } - tmr->fd = fd; - *handle = tmr; + _err: + if (type_conf) + snd_config_delete(type_conf); + if (err >= 0) + err = open_func(timer, name, timer_root, timer_conf, mode); + if (err < 0) + return err; return 0; } -int snd_timer_close(snd_timer_t *handle) +static int snd_timer_open_noupdate(snd_timer_t **timer, snd_config_t *root, const char *name, int mode) { - snd_timer_t *tmr; - int res; + int err; + snd_config_t *timer_conf; + err = snd_config_search_definition(root, "timer", name, &timer_conf); + if (err < 0) { + SNDERR("Unknown timer %s", name); + return err; + } + err = snd_timer_open_conf(timer, name, root, timer_conf, mode); + snd_config_delete(timer_conf); + return err; +} - tmr = handle; - if (!tmr) - return -EINVAL; - res = close(tmr->fd) < 0 ? -errno : 0; - free(tmr); - return res; +/** + * \brief Opens a new connection to the timer interface. + * \param timer Returned handle (NULL if not wanted) + * \param name ASCII identifier of the timer handle + * \param mode Open mode + * \return 0 on success otherwise a negative error code + * + * Opens a new connection to the timer interface specified with + * an ASCII identifier and mode. + */ +int snd_timer_open(snd_timer_t **timer, const char *name, int mode) +{ + int err; + assert(timer && name); + err = snd_config_update(); + if (err < 0) + return err; + return snd_timer_open_noupdate(timer, snd_config, name, mode); +} + +/** + * \brief close timer handle + * \param timer timer handle + * \return 0 on success otherwise a negative error code + * + * Closes the specified timer handle and frees all associated + * resources. + */ +int snd_timer_close(snd_timer_t *timer) +{ + int err; + assert(timer); + if ((err = timer->ops->close(timer)) < 0) + return err; + if (timer->name) + free(timer->name); + free(timer); + return 0; +} + +/** + * \brief get identifier of timer handle + * \param timer a timer handle + * \return ascii identifier of timer handle + * + * Returns the ASCII identifier of given timer handle. It's the same + * identifier specified in snd_timer_open(). + */ +const char *snd_timer_name(snd_timer_t *timer) +{ + assert(timer); + return timer->name; +} + +/** + * \brief get type of timer handle + * \param timer a timer handle + * \return type of timer handle + * + * Returns the type #snd_timer_type_t of given timer handle. + */ +snd_timer_type_t snd_timer_type(snd_timer_t *timer) +{ + assert(timer); + return timer->type; } +/** + * \brief get count of poll descriptors for timer handle + * \param timer timer handle + * \return count of poll descriptors + */ int snd_timer_poll_descriptors_count(snd_timer_t *timer) { assert(timer); return 1; } +/** + * \brief get poll descriptors + * \param timer timer handle + * \param pfds array of poll descriptors + * \param space space in the poll descriptor array + * \return count of filled descriptors + */ int snd_timer_poll_descriptors(snd_timer_t *timer, struct pollfd *pfds, unsigned int space) { assert(timer); if (space >= 1) { - pfds->fd = timer->fd; - pfds->events = POLLIN; + pfds->fd = timer->poll_fd; + switch (timer->mode & O_ACCMODE) { + case O_WRONLY: + pfds->events = POLLOUT; + break; + case O_RDONLY: + pfds->events = POLLIN; + break; + case O_RDWR: + pfds->events = POLLOUT|POLLIN; + break; + default: + return -EIO; + } return 1; } return 0; } -int snd_timer_next_device(snd_timer_t *handle, snd_timer_id_t * tid) -{ - snd_timer_t *tmr; - - tmr = handle; - if (!tmr || !tid) - return -EINVAL; - if (ioctl(tmr->fd, SNDRV_TIMER_IOCTL_NEXT_DEVICE, tid) < 0) - return -errno; - return 0; -} - -int snd_timer_select(snd_timer_t *handle, snd_timer_select_t * tselect) +/** + * \brief set nonblock mode + * \param timer timer handle + * \param nonblock 0 = block, 1 = nonblock mode + * \return 0 on success otherwise a negative error code + */ +int snd_timer_nonblock(snd_timer_t *timer, int nonblock) { - snd_timer_t *tmr; - - tmr = handle; - if (!tmr || !tselect) - return -EINVAL; - if (ioctl(tmr->fd, SNDRV_TIMER_IOCTL_SELECT, tselect) < 0) - return -errno; + int err; + assert(timer); + if ((err = timer->ops->nonblock(timer, nonblock)) < 0) + return err; + if (nonblock) + timer->mode |= SND_TIMER_OPEN_NONBLOCK; + else + timer->mode &= ~SND_TIMER_OPEN_NONBLOCK; return 0; } -int snd_timer_info(snd_timer_t *handle, snd_timer_info_t * info) +/** + * \brief get information about timer handle + * \param timer timer handle + * \param info pointer to a snd_timer_info_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int snd_timer_info(snd_timer_t *timer, snd_timer_info_t * info) { - snd_timer_t *tmr; - - tmr = handle; - if (!tmr || !info) - return -EINVAL; - if (ioctl(tmr->fd, SNDRV_TIMER_IOCTL_INFO, info) < 0) - return -errno; - return 0; + assert(timer); + assert(info); + return timer->ops->info(timer, info); } -int snd_timer_params(snd_timer_t *handle, snd_timer_params_t * params) +/** + * \brief set parameters for timer handle + * \param timer timer handle + * \param params pointer to a #snd_timer_params_t structure + * \return 0 on success otherwise a negative error code + */ +int snd_timer_params(snd_timer_t *timer, snd_timer_params_t * params) { - snd_timer_t *tmr; - - tmr = handle; - if (!tmr || !params) - return -EINVAL; - if (ioctl(tmr->fd, SNDRV_TIMER_IOCTL_PARAMS, params) < 0) - return -errno; - return 0; + assert(timer); + assert(params); + return timer->ops->params(timer, params); } -int snd_timer_status(snd_timer_t *handle, snd_timer_status_t * status) +/** + * \brief get status from timer handle + * \param timer timer handle + * \param status pointer to a #snd_timer_status_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int snd_timer_status(snd_timer_t *timer, snd_timer_status_t * status) { - snd_timer_t *tmr; - - tmr = handle; - if (!tmr || !status) - return -EINVAL; - if (ioctl(tmr->fd, SNDRV_TIMER_IOCTL_STATUS, status) < 0) - return -errno; - return 0; + assert(timer); + assert(status); + return timer->ops->status(timer, status); } -int snd_timer_start(snd_timer_t *handle) +/** + * \brief start the timer + * \param timer timer handle + * \return 0 on success otherwise a negative error code + */ +int snd_timer_start(snd_timer_t *timer) { - snd_timer_t *tmr; - - tmr = handle; - if (!tmr) - return -EINVAL; - if (ioctl(tmr->fd, SNDRV_TIMER_IOCTL_START) < 0) - return -errno; - return 0; + assert(timer); + return timer->ops->rt_start(timer); } -int snd_timer_stop(snd_timer_t *handle) +/** + * \brief stop the timer + * \param timer timer handle + * \return 0 on success otherwise a negative error code + */ +int snd_timer_stop(snd_timer_t *timer) { - snd_timer_t *tmr; - - tmr = handle; - if (!tmr) - return -EINVAL; - if (ioctl(tmr->fd, SNDRV_TIMER_IOCTL_STOP) < 0) - return -errno; - return 0; + assert(timer); + return timer->ops->rt_stop(timer); } -int snd_timer_continue(snd_timer_t *handle) +/** + * \brief continue the timer + * \param timer timer handle + * \return 0 on success otherwise a negative error code + */ +int snd_timer_continue(snd_timer_t *timer) { - snd_timer_t *tmr; - - tmr = handle; - if (!tmr) - return -EINVAL; - if (ioctl(tmr->fd, SNDRV_TIMER_IOCTL_CONTINUE) < 0) - return -errno; - return 0; + assert(timer); + return timer->ops->rt_continue(timer); } -ssize_t snd_timer_read(snd_timer_t *handle, void *buffer, size_t size) +/** + * \brief read bytes using timer handle + * \param timer timer handle + * \param buffer buffer to store the input bytes + * \param size input buffer size in bytes + */ +ssize_t snd_timer_read(snd_timer_t *timer, void *buffer, size_t size) { - snd_timer_t *tmr; - ssize_t result; - - tmr = handle; - if (!tmr || (!buffer && size > 0)) - return -EINVAL; - result = read(tmr->fd, buffer, size); - if (result < 0) - return -errno; - return result; + assert(timer); + assert(((timer->mode & O_ACCMODE) == O_RDONLY) || ((timer->mode & O_ACCMODE) == O_RDWR)); + assert(buffer || size == 0); + return timer->ops->read(timer, buffer, size); } diff --git a/src/timer/timer_hw.c b/src/timer/timer_hw.c new file mode 100644 index 00000000..2424f8c9 --- /dev/null +++ b/src/timer/timer_hw.c @@ -0,0 +1,261 @@ +/* + * Timer Interface - main file + * Copyright (c) 1998-2001 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include "timer_local.h" + +#define SNDRV_FILE_TIMER "/dev/snd/timer" +#define SNDRV_TIMER_VERSION_MAX SNDRV_PROTOCOL_VERSION(2, 0, 0) + +static int snd_timer_hw_close(snd_timer_t *handle) +{ + snd_timer_t *tmr = handle; + int res; + + if (!tmr) + return -EINVAL; + res = close(tmr->poll_fd) < 0 ? -errno : 0; + return res; +} + +static int snd_timer_hw_nonblock(snd_timer_t *timer, int nonblock) +{ + long flags; + assert(timer); + if ((flags = fcntl(timer->poll_fd, F_GETFL)) < 0) + return -errno; + if (nonblock) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + if (fcntl(timer->poll_fd, F_SETFL, flags) < 0) + return -errno; + return 0; +} + +static int snd_timer_hw_info(snd_timer_t *handle, snd_timer_info_t * info) +{ + snd_timer_t *tmr; + + tmr = handle; + if (!tmr || !info) + return -EINVAL; + if (ioctl(tmr->poll_fd, SNDRV_TIMER_IOCTL_INFO, info) < 0) + return -errno; + return 0; +} + +static int snd_timer_hw_params(snd_timer_t *handle, snd_timer_params_t * params) +{ + snd_timer_t *tmr; + + tmr = handle; + if (!tmr || !params) + return -EINVAL; + if (ioctl(tmr->poll_fd, SNDRV_TIMER_IOCTL_PARAMS, params) < 0) + return -errno; + return 0; +} + +static int snd_timer_hw_status(snd_timer_t *handle, snd_timer_status_t * status) +{ + snd_timer_t *tmr; + + tmr = handle; + if (!tmr || !status) + return -EINVAL; + if (ioctl(tmr->poll_fd, SNDRV_TIMER_IOCTL_STATUS, status) < 0) + return -errno; + return 0; +} + +static int snd_timer_hw_start(snd_timer_t *handle) +{ + snd_timer_t *tmr; + + tmr = handle; + if (!tmr) + return -EINVAL; + if (ioctl(tmr->poll_fd, SNDRV_TIMER_IOCTL_START) < 0) + return -errno; + return 0; +} + +static int snd_timer_hw_stop(snd_timer_t *handle) +{ + snd_timer_t *tmr; + + tmr = handle; + if (!tmr) + return -EINVAL; + if (ioctl(tmr->poll_fd, SNDRV_TIMER_IOCTL_STOP) < 0) + return -errno; + return 0; +} + +static int snd_timer_hw_continue(snd_timer_t *handle) +{ + snd_timer_t *tmr; + + tmr = handle; + if (!tmr) + return -EINVAL; + if (ioctl(tmr->poll_fd, SNDRV_TIMER_IOCTL_CONTINUE) < 0) + return -errno; + return 0; +} + +static ssize_t snd_timer_hw_read(snd_timer_t *handle, void *buffer, size_t size) +{ + snd_timer_t *tmr; + ssize_t result; + + tmr = handle; + if (!tmr || (!buffer && size > 0)) + return -EINVAL; + result = read(tmr->poll_fd, buffer, size); + if (result < 0) + return -errno; + return result; +} + +static snd_timer_ops_t snd_timer_hw_ops = { + close: snd_timer_hw_close, + nonblock: snd_timer_hw_nonblock, + info: snd_timer_hw_info, + params: snd_timer_hw_params, + status: snd_timer_hw_status, + rt_start: snd_timer_hw_start, + rt_stop: snd_timer_hw_stop, + rt_continue: snd_timer_hw_continue, + read: snd_timer_hw_read, +}; + +int snd_timer_hw_open(snd_timer_t **handle, const char *name, int dev_class, int dev_sclass, int card, int device, int subdevice, int mode) +{ + int fd, ver, tmode; + snd_timer_t *tmr; + snd_timer_id_t id; + + *handle = NULL; + + tmode = O_RDONLY; + if (mode & SND_TIMER_OPEN_NONBLOCK) + tmode |= O_NONBLOCK; + if ((fd = open(SNDRV_FILE_TIMER, tmode)) < 0) + return -errno; + if (ioctl(fd, SNDRV_TIMER_IOCTL_PVERSION, &ver) < 0) { + close(fd); + return -errno; + } + if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_TIMER_VERSION_MAX)) { + close(fd); + return -SND_ERROR_INCOMPATIBLE_VERSION; + } + memset(&id, 0, sizeof(id)); + id.dev_class = dev_class; + id.dev_sclass = dev_sclass; + id.card = card; + id.device = device; + id.subdevice = subdevice; + if (ioctl(fd, SNDRV_TIMER_IOCTL_SELECT, &id) < 0) { + int err = -errno; + close(fd); + return err; + } + tmr = (snd_timer_t *) calloc(1, sizeof(snd_timer_t)); + if (tmr == NULL) { + close(fd); + return -ENOMEM; + } + tmr->type = SND_TIMER_TYPE_HW; + tmr->mode = tmode; + tmr->name = strdup(name); + tmr->poll_fd = fd; + tmr->ops = &snd_timer_hw_ops; + *handle = tmr; + return 0; +} + +int _snd_timer_hw_open(snd_timer_t **timer, char *name, + snd_config_t *root ATTRIBUTE_UNUSED, + snd_config_t *conf, int mode) +{ + snd_config_iterator_t i, next; + long dev_class = SND_TIMER_CLASS_GLOBAL, dev_sclass = SND_TIMER_SCLASS_NONE; + long card = 0, device = 0, subdevice = 0; + const char *str; + int err; + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + if (strcmp(id, "comment") == 0) + continue; + if (strcmp(id, "type") == 0) + continue; + if (strcmp(id, "class") == 0) { + err = snd_config_get_integer(n, &dev_class); + if (err < 0) + return err; + continue; + } + if (strcmp(id, "sclass") == 0) { + err = snd_config_get_integer(n, &dev_sclass); + if (err < 0) + return err; + continue; + } + if (strcmp(id, "card") == 0) { + err = snd_config_get_integer(n, &card); + if (err < 0) { + err = snd_config_get_string(n, &str); + if (err < 0) + return -EINVAL; + card = snd_card_get_index(str); + if (card < 0) + return card; + } + continue; + } + if (strcmp(id, "device") == 0) { + err = snd_config_get_integer(n, &device); + if (err < 0) + return err; + continue; + } + if (strcmp(id, "subdevice") == 0) { + err = snd_config_get_integer(n, &subdevice); + if (err < 0) + return err; + continue; + } + SNDERR("Unexpected field %s", id); + return -EINVAL; + } + if (card < 0) + return -EINVAL; + return snd_timer_hw_open(timer, name, dev_class, dev_sclass, card, device, subdevice, mode); +} diff --git a/src/timer/timer_local.h b/src/timer/timer_local.h new file mode 100644 index 00000000..da4e8470 --- /dev/null +++ b/src/timer/timer_local.h @@ -0,0 +1,64 @@ +/* + * Timer interface - local header file + * Copyright (c) 2001 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include "local.h" + +typedef struct { + int (*close)(snd_timer_t *timer); + int (*nonblock)(snd_timer_t *timer, int nonblock); + int (*info)(snd_timer_t *timer, snd_timer_info_t *info); + int (*params)(snd_timer_t *timer, snd_timer_params_t *params); + int (*status)(snd_timer_t *timer, snd_timer_status_t *status); + int (*rt_start)(snd_timer_t *timer); + int (*rt_stop)(snd_timer_t *timer); + int (*rt_continue)(snd_timer_t *timer); + ssize_t (*read)(snd_timer_t *timer, void *buffer, size_t size); +} snd_timer_ops_t; + +struct _snd_timer { + char *name; + snd_timer_type_t type; + int mode; + int poll_fd; + snd_timer_ops_t *ops; + void *private_data; +}; + +int snd_timer_hw_open(snd_timer_t **handle, const char *name, int dev_class, int dev_sclass, int card, int device, int subdevice, int mode); + +typedef struct { + int (*close)(snd_timer_query_t *timer); + int (*next_device)(snd_timer_query_t *timer, snd_timer_id_t *tid); +} snd_timer_query_ops_t; + +struct _snd_timer_query { + char *name; + snd_timer_type_t type; + int mode; + int poll_fd; + snd_timer_query_ops_t *ops; + void *private_data; +}; + +int snd_timer_query_hw_open(snd_timer_query_t **handle, const char *name, int mode); diff --git a/src/timer/timer_query.c b/src/timer/timer_query.c new file mode 100644 index 00000000..93d15260 --- /dev/null +++ b/src/timer/timer_query.c @@ -0,0 +1,193 @@ +/* + * \file timer/timer_query.c + * \author Jaroslav Kysela + * \date 2001 + * + * Timer Query Interface is designed to obtain identification of timers. + */ +/* + * Timer Query Interface - main file + * Copyright (c) 2001 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "timer_local.h" + +static int snd_timer_query_open_conf(snd_timer_query_t **timer, + const char *name, snd_config_t *timer_root, + snd_config_t *timer_conf, int mode) +{ + const char *str; + char buf[256]; + int err; + snd_config_t *conf, *type_conf; + snd_config_iterator_t i, next; + const char *lib = NULL, *open_name = NULL; + int (*open_func)(snd_timer_query_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL; + void *h; + if (snd_config_get_type(timer_conf) != SND_CONFIG_TYPE_COMPOUND) { + if (name) + SNDERR("Invalid type for TIMER %s definition", name); + else + SNDERR("Invalid type for TIMER definition"); + return -EINVAL; + } + err = snd_config_search(timer_conf, "type", &conf); + if (err < 0) { + SNDERR("type is not defined"); + return err; + } + err = snd_config_get_string(conf, &str); + if (err < 0) { + SNDERR("Invalid type for %s", snd_config_get_id(conf)); + return err; + } + err = snd_config_search_definition(timer_root, "timer_query_type", str, &type_conf); + if (err >= 0) { + if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("Invalid type for TIMER type %s definition", str); + goto _err; + } + snd_config_for_each(i, next, type_conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + if (strcmp(id, "comment") == 0) + continue; + if (strcmp(id, "lib") == 0) { + err = snd_config_get_string(n, &lib); + if (err < 0) { + SNDERR("Invalid type for %s", id); + goto _err; + } + continue; + } + if (strcmp(id, "open") == 0) { + err = snd_config_get_string(n, &open_name); + if (err < 0) { + SNDERR("Invalid type for %s", id); + goto _err; + } + continue; + } + SNDERR("Unknown field %s", id); + err = -EINVAL; + goto _err; + } + } + if (!open_name) { + open_name = buf; + snprintf(buf, sizeof(buf), "_snd_timer_query_%s_open", str); + } + if (!lib) + lib = ALSA_LIB; + h = dlopen(lib, RTLD_NOW); + open_func = h ? dlsym(h, open_name) : NULL; + if (!h) { + SNDERR("Cannot open shared library %s", lib); + err = -ENOENT; + } else if (!open_func) { + SNDERR("symbol %s is not defined inside %s", open_name, lib); + dlclose(h); + err = -ENXIO; + } + _err: + if (type_conf) + snd_config_delete(type_conf); + if (err >= 0) + err = open_func(timer, name, timer_root, timer_conf, mode); + if (err < 0) + return err; + return 0; +} + +static int snd_timer_query_open_noupdate(snd_timer_query_t **timer, snd_config_t *root, const char *name, int mode) +{ + int err; + snd_config_t *timer_conf; + err = snd_config_search_definition(root, "timer", name, &timer_conf); + if (err < 0) { + SNDERR("Unknown timer %s", name); + return err; + } + err = snd_timer_query_open_conf(timer, name, root, timer_conf, mode); + snd_config_delete(timer_conf); + return err; +} + +/** + * \brief Opens a new connection to the timer query interface. + * \param timer Returned handle (NULL if not wanted) + * \param name ASCII identifier of the RawMidi handle + * \param mode Open mode + * \return 0 on success otherwise a negative error code + * + * Opens a new connection to the RawMidi interface specified with + * an ASCII identifier and mode. + */ +int snd_timer_query_open(snd_timer_query_t **timer, const char *name, int mode) +{ + int err; + assert(timer && name); + err = snd_config_update(); + if (err < 0) + return err; + return snd_timer_query_open_noupdate(timer, snd_config, name, mode); +} + +/** + * \brief close timer query handle + * \param timer timer handle + * \return 0 on success otherwise a negative error code + * + * Closes the specified timer handle and frees all associated + * resources. + */ +int snd_timer_query_close(snd_timer_query_t *timer) +{ + int err; + assert(timer); + if ((err = timer->ops->close(timer)) < 0) + return err; + if (timer->name) + free(timer->name); + free(timer); + return 0; +} + +/** + * \brief obtain the next timer identification + * \param timer timer handle + * \param tid timer identification + * \return 0 on success otherwise a negative error code + * + * if tid->dev_class is -1, then the first device is returned + * if result tid->dev_class is -1, no more devices are left + */ +int snd_timer_query_next_device(snd_timer_query_t *timer, snd_timer_id_t *tid) +{ + assert(timer); + assert(tid); + return timer->ops->next_device(timer, tid); +} diff --git a/src/timer/timer_query_hw.c b/src/timer/timer_query_hw.c new file mode 100644 index 00000000..6d70a317 --- /dev/null +++ b/src/timer/timer_query_hw.c @@ -0,0 +1,107 @@ +/* + * Timer Interface - main file + * Copyright (c) 1998-2001 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include "timer_local.h" + +#define SNDRV_FILE_TIMER "/dev/snd/timer" +#define SNDRV_TIMER_VERSION_MAX SNDRV_PROTOCOL_VERSION(2, 0, 0) + +static int snd_timer_query_hw_close(snd_timer_query_t *handle) +{ + int res; + + if (!handle) + return -EINVAL; + res = close(handle->poll_fd) < 0 ? -errno : 0; + return res; +} + +static int snd_timer_query_hw_next_device(snd_timer_query_t *handle, snd_timer_id_t * tid) +{ + if (!handle || !tid) + return -EINVAL; + if (ioctl(handle->poll_fd, SNDRV_TIMER_IOCTL_NEXT_DEVICE, tid) < 0) + return -errno; + return 0; +} + +static snd_timer_query_ops_t snd_timer_query_hw_ops = { + close: snd_timer_query_hw_close, + next_device: snd_timer_query_hw_next_device, +}; + +int snd_timer_query_hw_open(snd_timer_query_t **handle, const char *name, int mode) +{ + int fd, ver, tmode; + snd_timer_query_t *tmr; + + *handle = NULL; + + tmode = O_RDONLY; + if (mode & SND_TIMER_OPEN_NONBLOCK) + tmode |= O_NONBLOCK; + if ((fd = open(SNDRV_FILE_TIMER, tmode)) < 0) + return -errno; + if (ioctl(fd, SNDRV_TIMER_IOCTL_PVERSION, &ver) < 0) { + close(fd); + return -errno; + } + if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_TIMER_VERSION_MAX)) { + close(fd); + return -SND_ERROR_INCOMPATIBLE_VERSION; + } + tmr = (snd_timer_query_t *) calloc(1, sizeof(snd_timer_t)); + if (tmr == NULL) { + close(fd); + return -ENOMEM; + } + tmr->type = SND_TIMER_TYPE_HW; + tmr->mode = tmode; + tmr->name = strdup(name); + tmr->poll_fd = fd; + tmr->ops = &snd_timer_query_hw_ops; + *handle = tmr; + return 0; +} + +int _snd_timer_query_hw_open(snd_timer_query_t **timer, char *name, + snd_config_t *root ATTRIBUTE_UNUSED, + snd_config_t *conf, int mode) +{ + snd_config_iterator_t i, next; + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id = snd_config_get_id(n); + if (strcmp(id, "comment") == 0) + continue; + if (strcmp(id, "type") == 0) + continue; + SNDERR("Unexpected field %s", id); + return -EINVAL; + } + return snd_timer_query_hw_open(timer, name, mode); +} -- 2.47.1