/* * Sequencer Interface - main file * Copyright (c) 2000 by Jaroslav Kysela * 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 #include #include "seq_local.h" #ifndef PIC /* entry for static linking */ const char *_snd_module_seq_hw = ""; #endif #ifndef DOC_HIDDEN #define SNDRV_FILE_SEQ ALSA_DEVICE_DIRECTORY "seq" #define SNDRV_FILE_ALOADSEQ ALOAD_DEVICE_DIRECTORY "aloadSEQ" #define SNDRV_SEQ_VERSION_MAX SNDRV_PROTOCOL_VERSION(1, 0, 2) typedef struct { int fd; int version; } snd_seq_hw_t; #endif /* DOC_HIDDEN */ static int snd_seq_hw_close(snd_seq_t *seq) { snd_seq_hw_t *hw = seq->private_data; int err = 0; if (close(hw->fd)) { err = -errno; SYSERR("close failed\n"); } free(hw); return err; } static int snd_seq_hw_nonblock(snd_seq_t *seq, int nonblock) { snd_seq_hw_t *hw = seq->private_data; long flags; if ((flags = fcntl(hw->fd, F_GETFL)) < 0) { SYSERR("F_GETFL failed"); return -errno; } if (nonblock) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; if (fcntl(hw->fd, F_SETFL, flags) < 0) { SYSERR("F_SETFL for O_NONBLOCK failed"); return -errno; } return 0; } static int snd_seq_hw_client_id(snd_seq_t *seq) { snd_seq_hw_t *hw = seq->private_data; int client; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_CLIENT_ID, &client) < 0) { SYSERR("SNDRV_SEQ_IOCTL_CLIENT_ID failed"); return -errno; } return client; } static int snd_seq_hw_system_info(snd_seq_t *seq, snd_seq_system_info_t * info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SYSTEM_INFO, info) < 0) { SYSERR("SNDRV_SEQ_IOCTL_SYSTEM_INFO failed"); return -errno; } return 0; } static int snd_seq_hw_get_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_GET_CLIENT_INFO failed");*/ return -errno; } if (hw->version < SNDRV_PROTOCOL_VERSION(1, 0, 2)) { info->card = -1; info->pid = -1; } return 0; } static int snd_seq_hw_set_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SET_CLIENT_INFO failed");*/ return -errno; } return 0; } static int snd_seq_hw_create_port(snd_seq_t *seq, snd_seq_port_info_t * port) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_CREATE_PORT, port) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_CREATE_PORT failed");*/ return -errno; } return 0; } static int snd_seq_hw_delete_port(snd_seq_t *seq, snd_seq_port_info_t * port) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_DELETE_PORT, port) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_DELETE_PORT failed");*/ return -errno; } return 0; } static int snd_seq_hw_get_port_info(snd_seq_t *seq, snd_seq_port_info_t * info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_PORT_INFO, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_GET_PORT_INFO failed");*/ return -errno; } return 0; } static int snd_seq_hw_set_port_info(snd_seq_t *seq, snd_seq_port_info_t * info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_PORT_INFO, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SET_PORT_INFO failed");*/ return -errno; } return 0; } static int snd_seq_hw_get_port_subscription(snd_seq_t *seq, snd_seq_port_subscribe_t * sub) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, sub) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION failed");*/ return -errno; } return 0; } static int snd_seq_hw_subscribe_port(snd_seq_t *seq, snd_seq_port_subscribe_t * sub) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, sub) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT failed");*/ return -errno; } return 0; } static int snd_seq_hw_unsubscribe_port(snd_seq_t *seq, snd_seq_port_subscribe_t * sub) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, sub) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT failed");*/ return -errno; } return 0; } static int snd_seq_hw_query_port_subscribers(snd_seq_t *seq, snd_seq_query_subscribe_t * subs) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_QUERY_SUBS, subs) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_QUERY_SUBS failed");*/ return -errno; } return 0; } static int snd_seq_hw_get_queue_status(snd_seq_t *seq, snd_seq_queue_status_t * status) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, status) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS failed");*/ return -errno; } return 0; } static int snd_seq_hw_get_queue_tempo(snd_seq_t *seq, snd_seq_queue_tempo_t * tempo) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, tempo) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO failed");*/ return -errno; } return 0; } static int snd_seq_hw_set_queue_tempo(snd_seq_t *seq, snd_seq_queue_tempo_t * tempo) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, tempo) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO failed");*/ return -errno; } return 0; } static int snd_seq_hw_get_queue_timer(snd_seq_t *seq, snd_seq_queue_timer_t * timer) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, timer) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER failed");*/ return -errno; } return 0; } static int snd_seq_hw_set_queue_timer(snd_seq_t *seq, snd_seq_queue_timer_t * timer) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, timer) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER failed");*/ return -errno; } return 0; } static int snd_seq_hw_get_queue_client(snd_seq_t *seq, snd_seq_queue_client_t * info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT failed");*/ return -errno; } return 0; } static int snd_seq_hw_set_queue_client(snd_seq_t *seq, snd_seq_queue_client_t * info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT failed");*/ return -errno; } return 0; } static int snd_seq_hw_create_queue(snd_seq_t *seq, snd_seq_queue_info_t *info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_CREATE_QUEUE, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_CREATE_QUEUE failed");*/ return -errno; } return 0; } static int snd_seq_hw_delete_queue(snd_seq_t *seq, snd_seq_queue_info_t *info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_DELETE_QUEUE, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_DELETE_QUEUE failed");*/ return -errno; } return 0; } static int snd_seq_hw_get_queue_info(snd_seq_t *seq, snd_seq_queue_info_t *info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_INFO failed");*/ return -errno; } return 0; } static int snd_seq_hw_set_queue_info(snd_seq_t *seq, snd_seq_queue_info_t *info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_INFO failed");*/ return -errno; } return 0; } static int snd_seq_hw_get_named_queue(snd_seq_t *seq, snd_seq_queue_info_t *info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE failed");*/ return -errno; } return 0; } static ssize_t snd_seq_hw_write(snd_seq_t *seq, void *buf, size_t len) { snd_seq_hw_t *hw = seq->private_data; ssize_t result = write(hw->fd, buf, len); if (result < 0) return -errno; return result; } static ssize_t snd_seq_hw_read(snd_seq_t *seq, void *buf, size_t len) { snd_seq_hw_t *hw = seq->private_data; ssize_t result = read(hw->fd, buf, len); if (result < 0) return -errno; return result; } static int snd_seq_hw_remove_events(snd_seq_t *seq, snd_seq_remove_events_t *rmp) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, rmp) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_REMOVE_EVENTS failed");*/ return -errno; } return 0; } static int snd_seq_hw_get_client_pool(snd_seq_t *seq, snd_seq_client_pool_t *info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_GET_CLIENT_POOL failed");*/ return -errno; } return 0; } static int snd_seq_hw_set_client_pool(snd_seq_t *seq, snd_seq_client_pool_t *info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SET_CLIENT_POOL failed");*/ return -errno; } return 0; } static int snd_seq_hw_query_next_client(snd_seq_t *seq, snd_seq_client_info_t *info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT failed");*/ return -errno; } if (hw->version < SNDRV_PROTOCOL_VERSION(1, 0, 2)) { info->card = -1; info->pid = -1; } return 0; } static int snd_seq_hw_query_next_port(snd_seq_t *seq, snd_seq_port_info_t *info) { snd_seq_hw_t *hw = seq->private_data; if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT failed");*/ return -errno; } return 0; } static const snd_seq_ops_t snd_seq_hw_ops = { .close = snd_seq_hw_close, .nonblock = snd_seq_hw_nonblock, .system_info = snd_seq_hw_system_info, .get_client_info = snd_seq_hw_get_client_info, .set_client_info = snd_seq_hw_set_client_info, .create_port = snd_seq_hw_create_port, .delete_port = snd_seq_hw_delete_port, .get_port_info = snd_seq_hw_get_port_info, .set_port_info = snd_seq_hw_set_port_info, .get_port_subscription = snd_seq_hw_get_port_subscription, .subscribe_port = snd_seq_hw_subscribe_port, .unsubscribe_port = snd_seq_hw_unsubscribe_port, .query_port_subscribers = snd_seq_hw_query_port_subscribers, .get_queue_status = snd_seq_hw_get_queue_status, .get_queue_tempo = snd_seq_hw_get_queue_tempo, .set_queue_tempo = snd_seq_hw_set_queue_tempo, .get_queue_timer = snd_seq_hw_get_queue_timer, .set_queue_timer = snd_seq_hw_set_queue_timer, .get_queue_client = snd_seq_hw_get_queue_client, .set_queue_client = snd_seq_hw_set_queue_client, .create_queue = snd_seq_hw_create_queue, .delete_queue = snd_seq_hw_delete_queue, .get_queue_info = snd_seq_hw_get_queue_info, .set_queue_info = snd_seq_hw_set_queue_info, .get_named_queue = snd_seq_hw_get_named_queue, .write = snd_seq_hw_write, .read = snd_seq_hw_read, .remove_events = snd_seq_hw_remove_events, .get_client_pool = snd_seq_hw_get_client_pool, .set_client_pool = snd_seq_hw_set_client_pool, .query_next_client = snd_seq_hw_query_next_client, .query_next_port = snd_seq_hw_query_next_port, }; int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode) { int fd, ver, client, fmode, ret; const char *filename; snd_seq_t *seq; snd_seq_hw_t *hw; *handle = NULL; switch (streams) { case SND_SEQ_OPEN_OUTPUT: fmode = O_WRONLY; break; case SND_SEQ_OPEN_INPUT: fmode = O_RDONLY; break; case SND_SEQ_OPEN_DUPLEX: fmode = O_RDWR; break; default: assert(0); return -EINVAL; } if (mode & SND_SEQ_NONBLOCK) fmode |= O_NONBLOCK; filename = SNDRV_FILE_SEQ; fd = snd_open_device(filename, fmode); #ifdef SUPPORT_ALOAD if (fd < 0) { fd = snd_open_device(SNDRV_FILE_ALOADSEQ, fmode); if (fd >= 0) close(fd); fd = snd_open_device(filename, fmode); } #endif if (fd < 0) { SYSERR("open %s failed", filename); return -errno; } if (ioctl(fd, SNDRV_SEQ_IOCTL_PVERSION, &ver) < 0) { SYSERR("SNDRV_SEQ_IOCTL_PVERSION failed"); ret = -errno; close(fd); return ret; } if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_SEQ_VERSION_MAX)) { close(fd); return -SND_ERROR_INCOMPATIBLE_VERSION; } hw = calloc(1, sizeof(snd_seq_hw_t)); if (hw == NULL) { close(fd); return -ENOMEM; } seq = calloc(1, sizeof(snd_seq_t)); if (seq == NULL) { free(hw); close(fd); return -ENOMEM; } hw->fd = fd; hw->version = ver; if (streams & SND_SEQ_OPEN_OUTPUT) { seq->obuf = (char *) malloc(seq->obufsize = SND_SEQ_OBUF_SIZE); if (!seq->obuf) { free(hw); free(seq); close(fd); return -ENOMEM; } } if (streams & SND_SEQ_OPEN_INPUT) { seq->ibuf = (snd_seq_event_t *) calloc(sizeof(snd_seq_event_t), seq->ibufsize = SND_SEQ_IBUF_SIZE); if (!seq->ibuf) { free(seq->obuf); free(hw); free(seq); close(fd); return -ENOMEM; } } if (name) seq->name = strdup(name); seq->type = SND_SEQ_TYPE_HW; seq->streams = streams; seq->mode = mode; seq->tmpbuf = NULL; seq->tmpbufsize = 0; seq->poll_fd = fd; seq->ops = &snd_seq_hw_ops; seq->private_data = hw; client = snd_seq_hw_client_id(seq); if (client < 0) { snd_seq_close(seq); return client; } else seq->client = client; #ifdef SNDRV_SEQ_IOCTL_RUNNING_MODE { struct snd_seq_running_info run_mode; /* check running mode */ memset(&run_mode, 0, sizeof(run_mode)); run_mode.client = client; #ifdef SNDRV_BIG_ENDIAN run_mode.big_endian = 1; #else run_mode.big_endian = 0; #endif run_mode.cpu_mode = sizeof(long); ioctl(fd, SNDRV_SEQ_IOCTL_RUNNING_MODE, &run_mode); } #endif *handle = seq; return 0; } int _snd_seq_hw_open(snd_seq_t **handlep, char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, int streams, 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; if (snd_config_get_id(n, &id) < 0) continue; if (_snd_conf_generic_id(id)) continue; return -EINVAL; } return snd_seq_hw_open(handlep, name, streams, mode); } SND_DLSYM_BUILD_VERSION(_snd_seq_hw_open, SND_SEQ_DLSYM_VERSION);