diff options
author | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2009-07-25 08:44:28 +0000 |
---|---|---|
committer | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2009-07-25 08:44:28 +0000 |
commit | 0844fac57a53bca70c73354af3517b8bea149328 (patch) | |
tree | 38194a88aaef0f05f48c3bc6df1dec993ead1163 /lib/libsndio | |
parent | 8c4eca591954bdaedc99cf39f7f3f83fdc5ae579 (diff) |
Currently midi capable programs can control midi hardware, but
cannot cooperate with other programs. The aim of this change is
to allow any program to send midi data to other programs as they
were midi hardware. For instance, this change should solve the
longstanding problem of using a midi sequencer with software
synthesizers. More precisely:
- new midicat(1) utility (actually hardlink to aucat(1)).
it creates software midi thru boxes, allowing programs
to send midi messages to other programs as they were
midi(4) hardware.
- new midi api in libsndio (see mio_open(3)), to access
midi(4) devices and midicat(1) sockets in a uniform way.
- new device naming scheme <service>:<unit>[.<option>],
common to audio and midi.
- new sndio(7) manual describing concepts and naming
The current audio device naming still works, but people having
scripts or configuration files containing device names could read
the sndio(7) man page and slowly start updating device names.
discussed with jakemsr@ and deraadt@, help form jmc@
Diffstat (limited to 'lib/libsndio')
-rw-r--r-- | lib/libsndio/Makefile | 15 | ||||
-rw-r--r-- | lib/libsndio/aucat.c | 27 | ||||
-rw-r--r-- | lib/libsndio/mio.c | 184 | ||||
-rw-r--r-- | lib/libsndio/mio_open.3 | 252 | ||||
-rw-r--r-- | lib/libsndio/mio_priv.h | 69 | ||||
-rw-r--r-- | lib/libsndio/mio_rmidi.c | 158 | ||||
-rw-r--r-- | lib/libsndio/mio_thru.c | 213 | ||||
-rw-r--r-- | lib/libsndio/shlib_version | 2 | ||||
-rw-r--r-- | lib/libsndio/sio_open.3 | 26 | ||||
-rw-r--r-- | lib/libsndio/sndio.c | 46 | ||||
-rw-r--r-- | lib/libsndio/sun.c | 8 |
11 files changed, 963 insertions, 37 deletions
diff --git a/lib/libsndio/Makefile b/lib/libsndio/Makefile index 42c2166a441..82251017210 100644 --- a/lib/libsndio/Makefile +++ b/lib/libsndio/Makefile @@ -1,8 +1,8 @@ -# $OpenBSD: Makefile,v 1.3 2009/04/21 19:16:26 deraadt Exp $ +# $OpenBSD: Makefile,v 1.4 2009/07/25 08:44:26 ratchov Exp $ LIB= sndio -MAN= sio_open.3 -SRCS= aucat.c sun.c sndio.c +MAN= sio_open.3 mio_open.3 sndio.7 +SRCS= aucat.c sun.c sndio.c mio_rmidi.c mio_thru.c mio.c CFLAGS+=-Wall -Wstrict-prototypes -Werror -Wundef -DDEBUG \ -I${.CURDIR} -I${.CURDIR}/../../usr.bin/aucat @@ -22,6 +22,13 @@ MLINKS = \ sio_open.3 sio_eof.3 \ sio_open.3 sio_setvol.3 \ sio_open.3 sio_onvol.3 \ - sio_open.3 sio_initpar.3 + sio_open.3 sio_initpar.3 \ + mio_open.3 mio_close.3 \ + mio_open.3 mio_read.3 \ + mio_open.3 mio_write.3 \ + mio_open.3 mio_nfds.3 \ + mio_open.3 mio_pollfd.3 \ + mio_open.3 mio_revents.3 \ + mio_open.3 mio_eof.3 .include <bsd.lib.mk> diff --git a/lib/libsndio/aucat.c b/lib/libsndio/aucat.c index cefa0443f0d..5e249f35961 100644 --- a/lib/libsndio/aucat.c +++ b/lib/libsndio/aucat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aucat.c,v 1.21 2009/05/16 12:10:52 ratchov Exp $ */ +/* $OpenBSD: aucat.c,v 1.22 2009/07/25 08:44:26 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -167,22 +167,34 @@ aucat_runmsg(struct aucat_hdl *hdl) } struct sio_hdl * -sio_open_aucat(char *path, unsigned mode, int nbio) +sio_open_aucat(char *str, unsigned mode, int nbio) { extern char *__progname; int s; + char unit[4], *sep, *opt; struct aucat_hdl *hdl; struct sockaddr_un ca; socklen_t len = sizeof(struct sockaddr_un); uid_t uid; - if (path == NULL) - path = SIO_AUCAT_PATH; + sep = strchr(str, '.'); + if (sep == NULL) { + opt = "default"; + strlcpy(unit, str, sizeof(unit)); + } else { + opt = sep + 1; + if (sep - str >= sizeof(unit)) { + DPRINTF("sio_open_aucat: %s: too long\n", str); + return NULL; + } + strlcpy(unit, str, opt - str); + } + DPRINTF("sio_open_aucat: trying %s -> %s.%s\n", str, unit, opt); uid = geteuid(); - if (strchr(path, '/') != NULL) + if (strchr(str, '/') != NULL) return NULL; snprintf(ca.sun_path, sizeof(ca.sun_path), - "/tmp/aucat-%u/%s", uid, path); + "/tmp/aucat-%u/softaudio%s", uid, unit); ca.sun_family = AF_UNIX; hdl = malloc(sizeof(struct aucat_hdl)); @@ -196,6 +208,7 @@ sio_open_aucat(char *path, unsigned mode, int nbio) while (connect(s, (struct sockaddr *)&ca, len) < 0) { if (errno == EINTR) continue; + DPERROR("sio_open_aucat: connect"); goto bad_connect; } if (fcntl(s, F_SETFD, FD_CLOEXEC) < 0) { @@ -222,6 +235,8 @@ sio_open_aucat(char *path, unsigned mode, int nbio) hdl->wmsg.u.hello.proto |= AMSG_REC; strlcpy(hdl->wmsg.u.hello.who, __progname, sizeof(hdl->wmsg.u.hello.who)); + strlcpy(hdl->wmsg.u.hello.opt, opt, + sizeof(hdl->wmsg.u.hello.opt)); hdl->wtodo = sizeof(struct amsg); if (!aucat_wmsg(hdl)) goto bad_connect; diff --git a/lib/libsndio/mio.c b/lib/libsndio/mio.c new file mode 100644 index 00000000000..b03582d1635 --- /dev/null +++ b/lib/libsndio/mio.c @@ -0,0 +1,184 @@ +/* $OpenBSD: mio.c,v 1.1 2009/07/25 08:44:26 ratchov Exp $ */ +/* + * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "mio_priv.h" + +#ifdef DEBUG +/* + * debug level, -1 means uninitialized + */ +int mio_debug = -1; +#endif + +struct mio_hdl * +mio_open(char *str, unsigned mode, int nbio) +{ + static char prefix_midithru[] = "midithru"; + static char prefix_rmidi[] = "rmidi"; + struct mio_hdl *hdl; + struct stat sb; + char *sep, buf[4]; + int len; +#ifdef DEBUG + char *dbg; + + if (mio_debug < 0) { + dbg = issetugid() ? NULL : getenv("MIO_DEBUG"); + if (!dbg || sscanf(dbg, "%u", &mio_debug) != 1) + mio_debug = 0; + } +#endif + if ((mode & (MIO_OUT | MIO_IN)) == 0) + return NULL; + if (str == NULL && !issetugid()) + str = getenv("MIDIDEVICE"); + if (str == NULL) { + hdl = mio_open_thru("0", mode, nbio); + if (hdl != NULL) + return hdl; + return mio_open_rmidi("0", mode, nbio); + } + sep = strchr(str, ':'); + if (sep == NULL) { + /* + * try legacy "/dev/rmidioxxx" device name + */ + if (stat(str, &sb) < 0 || !S_ISCHR(sb.st_mode)) { + DPRINTF("mio_open: %s: missing ':' separator\n", str); + return NULL; + } + snprintf(buf, sizeof(buf), "%u", minor(sb.st_rdev)); + return mio_open_rmidi(buf, mode, nbio); + } + if (sep == str) { + /* + * legacy "/dev/rmidixxx" device name + */ + if (stat(str, &sb) < 0) { + DPERROR("mio_open: stat"); + return NULL; + } + if (!S_ISCHR(sb.st_mode)) { + DPRINTF("mio_open: %s: not a char dev\n", str); + return NULL; + } + snprintf(buf, sizeof(buf), "%u", minor(sb.st_rdev)); + return mio_open_rmidi(buf, mode, nbio); + } + + len = sep - str; + if (len == strlen(prefix_midithru) && + memcmp(str, prefix_midithru, len) == 0) + return mio_open_thru(sep + 1, mode, nbio); + if (len == strlen(prefix_rmidi) && + memcmp(str, prefix_rmidi, len) == 0) + return mio_open_rmidi(sep + 1, mode, nbio); + DPRINTF("mio_open: %s: unknown device type\n", str); + return NULL; +} + +void +mio_create(struct mio_hdl *hdl, struct mio_ops *ops, unsigned mode, int nbio) +{ + hdl->ops = ops; + hdl->mode = mode; + hdl->nbio = nbio; + hdl->eof = 0; +} + +void +mio_close(struct mio_hdl *hdl) +{ + return hdl->ops->close(hdl); +} + +size_t +mio_read(struct mio_hdl *hdl, void *buf, size_t len) +{ + if (hdl->eof) { + DPRINTF("mio_read: eof\n"); + return 0; + } + if (!(hdl->mode & MIO_IN)) { + DPRINTF("mio_read: not input device\n"); + hdl->eof = 1; + return 0; + } + if (len == 0) { + DPRINTF("mio_read: zero length read ignored\n"); + return 0; + } + return hdl->ops->read(hdl, buf, len); +} + +size_t +mio_write(struct mio_hdl *hdl, void *buf, size_t len) +{ + if (hdl->eof) { + DPRINTF("mio_write: eof\n"); + return 0; + } + if (!(hdl->mode & MIO_OUT)) { + DPRINTF("mio_write: not output device\n"); + hdl->eof = 1; + return 0; + } + if (len == 0) { + DPRINTF("mio_write: zero length write ignored\n"); + return 0; + } + return hdl->ops->write(hdl, buf, len); +} + +int +mio_nfds(struct mio_hdl *hdl) +{ + return 1; +} + +int +mio_pollfd(struct mio_hdl *hdl, struct pollfd *pfd, int events) +{ + if (hdl->eof) + return 0; + return hdl->ops->pollfd(hdl, pfd, events); +} + +int +mio_revents(struct mio_hdl *hdl, struct pollfd *pfd) +{ + if (hdl->eof) + return POLLHUP; + return hdl->ops->revents(hdl, pfd); +} + +int +mio_eof(struct mio_hdl *hdl) +{ + return hdl->eof; +} diff --git a/lib/libsndio/mio_open.3 b/lib/libsndio/mio_open.3 new file mode 100644 index 00000000000..d8ac43dc04c --- /dev/null +++ b/lib/libsndio/mio_open.3 @@ -0,0 +1,252 @@ +.\" $OpenBSD: mio_open.3,v 1.1 2009/07/25 08:44:26 ratchov Exp $ +.\" +.\" Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: July 25 2009 $ +.Dt MIO_OPEN 3 +.Os +.Sh NAME +.Nm mio_open , +.Nm mio_close , +.Nm mio_read , +.Nm mio_write , +.Nm mio_nfds , +.Nm mio_pollfd , +.Nm mio_revents , +.Nm mio_eof +.Nd interface to MIDI streams +.Sh SYNOPSIS +.Fd #include <sndio.h> +.Ft "struct mio_hdl *" +.Fn "mio_open" "char *name" "unsigned mode" "int nbio_flag" +.Ft "void" +.Fn "mio_close" "struct mio_hdl *hdl" +.Ft "size_t" +.Fn "mio_read" "struct mio_hdl *hdl" "void *addr" "size_t nbytes" +.Ft "size_t" +.Fn "mio_write" "struct mio_hdl *hdl" "void *addr" "size_t nbytes" +.Ft "int" +.Fn "mio_nfds" "struct mio_hdl *hdl" +.Ft "int" +.Fn "mio_pollfd" "struct mio_hdl *hdl" "struct pollfd *pfd" "int events" +.Ft "int" +.Fn "mio_revents" "struct mio_hdl *hdl" "struct pollfd *pfd" +.Ft "int" +.Fn "mio_eof" "struct mio_hdl *hdl" +.Sh DESCRIPTION +The +.Nm sndio +library allows user processes to access +.Xr midi 4 +hardware and +.Xr midicat 1 +MIDI thru boxes in a uniform way. +.Ss Opening and closing an MIDI stream +First the application must call the +.Fn mio_open +function to obtain a handle representing the newly created stream; +later it will be passed as the +.Ar hdl +argument of most other functions. +The +.Fn mio_open +function tries to connect to the +.Xr midicat 1 +software MIDI thru box or to use the +.Xr midi 4 +hardware device. +The +.Ar name +parameter gives the device string discussed in +.Xr sndio 7 . +If the program is using a single device and is providing no device chooser, +it should be set to NULL to allow the user to select it using the +.Ev MIDIDEVICE +environment variable. +.Pp +The +.Ar mode +parameter gives the direction of the stream. +The following are supported: +.Bl -tag -width "MIO_OUT | MIO_IN" +.It MIO_OUT +The stream is output-only; data written to the stream will be sent +to the hardware or other programs. +.It MIO_IN +The stream is input-only; received data from the hardware or +other programs must be read from the stream. +.It MIO_IN | MIO_OUT +The stream sends and receives data. +This mode should be used rather using twice +.Fn mio_open . +.El +.Pp +If the +.Ar nbio_flag +argument is true (i.e. non-zero), then the +.Fn mio_read +and +.Fn mio_write +functions (see below) will be non-blocking. +.Pp +The +.Fn mio_close +function closes the stream and frees all allocated resources +associated with the +.Nm libsndio +handle. +.Ss Sending and receiving data +When input mode is selected, the +.Fn mio_read +function must be called to retrieve received data; it must be called +often enough to ensure that internal buffers will not overrun. +It will store at most +.Ar nbytes +bytes at the +.Ar addr +location and return the number of bytes stored. +Unless the +.Ar nbio_flag +flag is set, it will block until data becomes available and +will return zero only on error. +.Pp +When output mode is selected, the +.Fn mio_write +function can be called to provide data to transmit. +Unless the +.Ar nbio_flag +is set, +.Fn mio_write +will block until the requested amount of data is written. +.Ss Non-blocking mode operation +If the +.Ar nbio_flag +is set on +.Fn mio_open , +then the +.Fn mio_read +and +.Fn mio_write +functions will never block; if no data is available, they will +return zero immediately. +.Pp +To avoid busy loops when non-blocking mode is used, the +.Xr poll 2 +system call can be used to check if data can be +read from or written to the stream. +The +.Fn mio_pollfd +function fills the array +.Ar pfd +of +.Va pollfd +structures, used by +.Xr poll 2 , +with +.Ar events ; +the latter is a bit-mask of +.Va POLLIN +and +.Va POLLOUT +constants; refer to +.Xr poll 2 +for more details. +.Fn mio_pollfd +returns the number of +.Va pollfd +structures filled. +The +.Fn mio_revents +function returns the bit-mask set by +.Xr poll 2 +in the +.Va pfd +array of +.Va pollfd +structures. +If +.Va POLLIN +is set, +.Fn mio_read +can be called without blocking. +If +.Va POLLOUT +is set, +.Fn mio_write +can be called without blocking. +POLLHUP may be set if an error occurs, even if +it is not selected with +.Fn mio_pollfd . +.Pp +The +.Fn mio_nfds +function returns the number of +.Va pollfd +structures the caller must preallocate in order to be sure +that +.Fn mio_pollfd +will never overrun. +.Ss Error handling +Errors related to the MIDI subsystem +(like hardware errors or dropped connections) and +programming errors (such as a call to +.Fn mio_read +on a play-only stream) are considered fatal. +Once an error occurs, all functions which take a +.Va mio_hdl +argument, except +.Fn mio_close +and +.Fn mio_eof , +stop working (i.e. always return 0). +.Pp +The +.Fn mio_eof +function can be used at any stage; +it returns 0 if there's no pending error, and a non-zero +value if there's an error. +.Sh RETURN VALUES +The +.Fn mio_open +function returns the newly created handle on success or NULL +on failure. +The +.Fn mio_pollfd +function returns 1 on success and 0 on failure. +The +.Fn mio_read +and +.Fn mio_write +functions return the number of bytes transferred. +.Sh ENVIRONMENT +.Bl -tag -width "MIO_DEBUGXXX" -compact +.It Ev MIO_DEBUG +The debug level: +may be a value between 0 and 2. +.El +.Sh FILES +.Bl -tag -width "/tmp/aucat-<uid>/midithru0" -compact +.It Pa /tmp/aucat-<uid>/midithru0 +Default path to +.Xr midicat 1 +socket to connect to. +.It Pa /dev/rmidiX +.Xr midi 4 +devices. +.El +.Sh SEE ALSO +.Xr midicat 1 , +.Xr midi 4 , +.Xr sndio 7 diff --git a/lib/libsndio/mio_priv.h b/lib/libsndio/mio_priv.h new file mode 100644 index 00000000000..240267333ba --- /dev/null +++ b/lib/libsndio/mio_priv.h @@ -0,0 +1,69 @@ +/* $OpenBSD: mio_priv.h,v 1.1 2009/07/25 08:44:26 ratchov Exp $ */ +/* + * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef MIO_PRIV_H +#define MIO_PRIV_H + +#include <sys/param.h> +#include "sndio.h" + +#ifdef DEBUG +#define DPRINTF(...) \ + do { \ + if (mio_debug > 0) \ + fprintf(stderr, __VA_ARGS__); \ + } while(0) +#define DPERROR(s) \ + do { \ + if (mio_debug > 0) \ + perror(s); \ + } while(0) +#else +#define DPRINTF(...) do {} while(0) +#define DPERROR(s) do {} while(0) +#endif + +/* + * private ``handle'' structure + */ +struct mio_hdl { + struct mio_ops *ops; + unsigned mode; /* MIO_PLAY | MIO_REC */ + int nbio; /* true if non-blocking io */ + int eof; /* true if error occured */ +}; + +/* + * operations every device should support + */ +struct mio_ops { + void (*close)(struct mio_hdl *); + size_t (*write)(struct mio_hdl *, void *, size_t); + size_t (*read)(struct mio_hdl *, void *, size_t); + int (*pollfd)(struct mio_hdl *, struct pollfd *, int); + int (*revents)(struct mio_hdl *, struct pollfd *); +}; + +struct mio_hdl *mio_open_rmidi(char *, unsigned, int); +struct mio_hdl *mio_open_thru(char *, unsigned, int); +void mio_create(struct mio_hdl *, struct mio_ops *, unsigned, int); +void mio_destroy(struct mio_hdl *); + +#ifdef DEBUG +extern int mio_debug; +#endif + +#endif /* !defined(MIO_PRIV_H) */ diff --git a/lib/libsndio/mio_rmidi.c b/lib/libsndio/mio_rmidi.c new file mode 100644 index 00000000000..9aa460633e4 --- /dev/null +++ b/lib/libsndio/mio_rmidi.c @@ -0,0 +1,158 @@ +/* $OpenBSD: mio_rmidi.c,v 1.1 2009/07/25 08:44:26 ratchov Exp $ */ +/* + * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <limits.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mio_priv.h" + +#define RMIDI_PATH "/dev/rmidi0" + +struct rmidi_hdl { + struct mio_hdl mio; + int fd; +}; + +static void rmidi_close(struct mio_hdl *); +static size_t rmidi_read(struct mio_hdl *, void *, size_t); +static size_t rmidi_write(struct mio_hdl *, void *, size_t); +static int rmidi_pollfd(struct mio_hdl *, struct pollfd *, int); +static int rmidi_revents(struct mio_hdl *, struct pollfd *); + +static struct mio_ops rmidi_ops = { + rmidi_close, + rmidi_write, + rmidi_read, + rmidi_pollfd, + rmidi_revents, +}; + +struct mio_hdl * +mio_open_rmidi(char *str, unsigned mode, int nbio) +{ + int fd, flags; + struct rmidi_hdl *hdl; + char path[PATH_MAX]; + + hdl = malloc(sizeof(struct rmidi_hdl)); + if (hdl == NULL) + return NULL; + mio_create(&hdl->mio, &rmidi_ops, mode, nbio); + + snprintf(path, sizeof(path), "/dev/rmidi%s", str); + if (mode == (MIO_OUT | MIO_IN)) + flags = O_RDWR; + else + flags = (mode & MIO_OUT) ? O_WRONLY : O_RDONLY; + + while ((fd = open(path, flags | O_NONBLOCK)) < 0) { + if (errno == EINTR) + continue; + DPERROR(path); + goto bad_free; + } + if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { + DPERROR("FD_CLOEXEC"); + goto bad_close; + } + hdl->fd = fd; + return (struct mio_hdl *)hdl; + bad_close: + while (close(hdl->fd) < 0 && errno == EINTR) + ; /* retry */ + bad_free: + free(hdl); + return NULL; +} + +static void +rmidi_close(struct mio_hdl *sh) +{ + struct rmidi_hdl *hdl = (struct rmidi_hdl *)sh; + int rc; + + do { + rc = close(hdl->fd); + } while (rc < 0 && errno == EINTR); + free(hdl); +} + +static size_t +rmidi_read(struct mio_hdl *sh, void *buf, size_t len) +{ + struct rmidi_hdl *hdl = (struct rmidi_hdl *)sh; + ssize_t n; + + while ((n = read(hdl->fd, buf, len)) < 0) { + if (errno == EINTR) + continue; + if (errno != EAGAIN) { + DPERROR("rmidi_read: read"); + hdl->mio.eof = 1; + } + return 0; + } + if (n == 0) { + DPRINTF("rmidi_read: eof\n"); + hdl->mio.eof = 1; + return 0; + } + return n; +} + +static size_t +rmidi_write(struct mio_hdl *sh, void *buf, size_t len) +{ + struct rmidi_hdl *hdl = (struct rmidi_hdl *)sh; + ssize_t n; + + while ((n = write(hdl->fd, buf, len)) < 0) { + if (errno == EINTR) + continue; + if (errno != EAGAIN) { + DPERROR("rmidi_write: write"); + hdl->mio.eof = 1; + return 0; + } + return 0; + } + return n; +} + +static int +rmidi_pollfd(struct mio_hdl *sh, struct pollfd *pfd, int events) +{ + struct rmidi_hdl *hdl = (struct rmidi_hdl *)sh; + + pfd->fd = hdl->fd; + pfd->events = events; + return 1; +} + +int +rmidi_revents(struct mio_hdl *sh, struct pollfd *pfd) +{ + return pfd->revents; +} diff --git a/lib/libsndio/mio_thru.c b/lib/libsndio/mio_thru.c new file mode 100644 index 00000000000..d3e2953cf18 --- /dev/null +++ b/lib/libsndio/mio_thru.c @@ -0,0 +1,213 @@ +/* $OpenBSD: mio_thru.c,v 1.1 2009/07/25 08:44:27 ratchov Exp $ */ +/* + * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> + +#include "amsg.h" +#include "mio_priv.h" + +#define THRU_SOCKET "midithru" + +struct thru_hdl { + struct mio_hdl mio; + int fd; +}; + +static void thru_close(struct mio_hdl *); +static size_t thru_read(struct mio_hdl *, void *, size_t); +static size_t thru_write(struct mio_hdl *, void *, size_t); +static int thru_pollfd(struct mio_hdl *, struct pollfd *, int); +static int thru_revents(struct mio_hdl *, struct pollfd *); + +static struct mio_ops thru_ops = { + thru_close, + thru_write, + thru_read, + thru_pollfd, + thru_revents, +}; + +struct mio_hdl * +mio_open_thru(char *str, unsigned mode, int nbio) +{ + extern char *__progname; + struct amsg msg; + int s, n, todo; + unsigned char *data; + struct thru_hdl *hdl; + struct sockaddr_un ca; + socklen_t len = sizeof(struct sockaddr_un); + uid_t uid; + + uid = geteuid(); + if (strchr(str, '/') != NULL) + return NULL; + snprintf(ca.sun_path, sizeof(ca.sun_path), + "/tmp/aucat-%u/midithru%s", uid, str); + ca.sun_family = AF_UNIX; + + hdl = malloc(sizeof(struct thru_hdl)); + if (hdl == NULL) + return NULL; + mio_create(&hdl->mio, &thru_ops, mode, nbio); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + goto bad_free; + while (connect(s, (struct sockaddr *)&ca, len) < 0) { + if (errno == EINTR) + continue; + DPERROR("mio_open_thru: connect"); + goto bad_connect; + } + if (fcntl(s, F_SETFD, FD_CLOEXEC) < 0) { + DPERROR("FD_CLOEXEC"); + goto bad_connect; + } + hdl->fd = s; + + /* + * say hello to server + */ + AMSG_INIT(&msg); + msg.cmd = AMSG_HELLO; + msg.u.hello.proto = 0; + if (mode & MIO_IN) + msg.u.hello.proto |= AMSG_MIDIIN; + if (mode & MIO_OUT) + msg.u.hello.proto |= AMSG_MIDIOUT; + strlcpy(msg.u.hello.who, __progname, sizeof(msg.u.hello.who)); + n = write(s, &msg, sizeof(struct amsg)); + if (n < 0) { + DPERROR("mio_open_thru"); + goto bad_connect; + } + if (n != sizeof(struct amsg)) { + DPRINTF("mio_open_thru: short write\n"); + goto bad_connect; + } + todo = sizeof(struct amsg); + data = (unsigned char *)&msg; + while (todo > 0) { + n = read(s, data, todo); + if (n < 0) { + DPERROR("mio_open_thru"); + goto bad_connect; + } + if (n == 0) { + DPRINTF("mio_open_thru: eof\n"); + goto bad_connect; + } + todo -= n; + data += n; + } + if (msg.cmd != AMSG_ACK) { + DPRINTF("mio_open_thru: proto error\n"); + goto bad_connect; + } + if (nbio && fcntl(hdl->fd, F_SETFL, O_NONBLOCK) < 0) { + DPERROR("mio_open_thru: fcntl(NONBLOCK)"); + goto bad_connect; + } + return (struct mio_hdl *)hdl; + bad_connect: + while (close(s) < 0 && errno == EINTR) + ; /* retry */ + bad_free: + free(hdl); + return NULL; +} + +static void +thru_close(struct mio_hdl *sh) +{ + struct thru_hdl *hdl = (struct thru_hdl *)sh; + int rc; + + do { + rc = close(hdl->fd); + } while (rc < 0 && errno == EINTR); + free(hdl); +} + +static size_t +thru_read(struct mio_hdl *sh, void *buf, size_t len) +{ + struct thru_hdl *hdl = (struct thru_hdl *)sh; + ssize_t n; + + while ((n = read(hdl->fd, buf, len)) < 0) { + if (errno == EINTR) + continue; + if (errno != EAGAIN) { + DPERROR("thru_read: read"); + hdl->mio.eof = 1; + } + return 0; + } + if (n == 0) { + DPRINTF("thru_read: eof\n"); + hdl->mio.eof = 1; + return 0; + } + return n; +} + +static size_t +thru_write(struct mio_hdl *sh, void *buf, size_t len) +{ + struct thru_hdl *hdl = (struct thru_hdl *)sh; + ssize_t n; + + while ((n = write(hdl->fd, buf, len)) < 0) { + if (errno == EINTR) + continue; + if (errno != EAGAIN) { + DPERROR("thru_write: write"); + hdl->mio.eof = 1; + return 0; + } + return 0; + } + return n; +} + +static int +thru_pollfd(struct mio_hdl *sh, struct pollfd *pfd, int events) +{ + struct thru_hdl *hdl = (struct thru_hdl *)sh; + + pfd->fd = hdl->fd; + pfd->events = events; + return 1; +} + +int +thru_revents(struct mio_hdl *sh, struct pollfd *pfd) +{ + return pfd->revents; +} diff --git a/lib/libsndio/shlib_version b/lib/libsndio/shlib_version index 83a67c373cc..b25072f4e52 100644 --- a/lib/libsndio/shlib_version +++ b/lib/libsndio/shlib_version @@ -1,2 +1,2 @@ major=3 -minor=2 +minor=3 diff --git a/lib/libsndio/sio_open.3 b/lib/libsndio/sio_open.3 index 78658d12c2b..744a556d009 100644 --- a/lib/libsndio/sio_open.3 +++ b/lib/libsndio/sio_open.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sio_open.3,v 1.19 2009/05/15 11:07:41 ratchov Exp $ +.\" $OpenBSD: sio_open.3,v 1.20 2009/07/25 08:44:27 ratchov Exp $ .\" .\" Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 15 2009 $ +.Dd $Mdocdate: July 25 2009 $ .Dt SIO_OPEN 3 .Os .Sh NAME @@ -104,11 +104,8 @@ If that fails, it then tries to use the hardware device. The .Ar name -parameter gives the name of the -.Xr aucat 1 -socket or the path of the -.Xr audio 4 -device. +parameter gives the device string discussed in +.Xr sndio 7 . In most cases it should be set to NULL to allow the user to select it using the .Ev AUDIODEVICE @@ -728,18 +725,18 @@ functions return the number of bytes transferred. .Sh ENVIRONMENT .Bl -tag -width "AUDIODEVICEXXX" -compact .It Ev AUDIODEVICE -Name of the -.Xr aucat 1 -socket to connect to, or path to the -.Xr audio 4 -device to use. +Device to use if +.Fn sio_open +is called with a NULL +.Va name +argument. .It Ev SIO_DEBUG The debug level: may be a value between 0 and 2. .El .Sh FILES -.Bl -tag -width "/tmp/aucat-<uid>/default" -compact -.It Pa /tmp/aucat-<uid>/default +.Bl -tag -width "/tmp/aucat-<uid>/softaudio0" -compact +.It Pa /tmp/aucat-<uid>/softaudio0 Default path to .Xr aucat 1 socket to connect to. @@ -754,6 +751,7 @@ device to use. .Sh SEE ALSO .Xr aucat 1 , .Xr audio 4 , +.Xr sndio 7 , .Xr audio 9 .Sh BUGS The diff --git a/lib/libsndio/sndio.c b/lib/libsndio/sndio.c index 6656cc67bb1..c7ab3750c81 100644 --- a/lib/libsndio/sndio.c +++ b/lib/libsndio/sndio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sndio.c,v 1.15 2009/05/15 13:04:52 ratchov Exp $ */ +/* $OpenBSD: sndio.c,v 1.16 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -17,6 +17,7 @@ #include <sys/param.h> #include <sys/types.h> #include <sys/time.h> +#include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <poll.h> @@ -163,11 +164,15 @@ done: return p - istr; } - struct sio_hdl * sio_open(char *str, unsigned mode, int nbio) { + static char prefix_aucat[] = "aucat"; + static char prefix_sun[] = "sun"; struct sio_hdl *hdl; + struct stat sb; + char *sep, buf[NAME_MAX]; + int len; #ifdef DEBUG char *dbg; @@ -181,12 +186,37 @@ sio_open(char *str, unsigned mode, int nbio) return NULL; if (str == NULL && !issetugid()) str = getenv("AUDIODEVICE"); - hdl = sio_open_aucat(str, mode, nbio); - if (hdl != NULL) - return hdl; - hdl = sio_open_sun(str, mode, nbio); - if (hdl != NULL) - return hdl; + if (str == NULL) { + hdl = sio_open_aucat("0", mode, nbio); + if (hdl != NULL) + return hdl; + if (stat("/dev/audio", &sb) == 0 && S_ISCHR(sb.st_mode)) { + snprintf(buf, sizeof(buf), "%u", + minor(sb.st_rdev) & 0xf); + } else + strlcpy(buf, "0", sizeof(buf)); + return sio_open_sun(buf, mode, nbio); + } + sep = strchr(str, ':'); + if (sep == NULL) { + /* + * try legacy "/dev/audioxxx" or ``socket'' device name + */ + if (stat(str, &sb) < 0 || !S_ISCHR(sb.st_mode)) { + snprintf(buf, sizeof(buf), "0.%s", str); + return sio_open_aucat(buf, mode, nbio); + } + snprintf(buf, sizeof(buf), "%u", minor(sb.st_rdev) & 0xf); + return sio_open_sun(buf, mode, nbio); + } + len = sep - str; + if (len == strlen(prefix_aucat) && + memcmp(str, prefix_aucat, len) == 0) + return sio_open_aucat(sep + 1, mode, nbio); + if (len == strlen(prefix_sun) && + memcmp(str, prefix_sun, len) == 0) + return sio_open_sun(sep + 1, mode, nbio); + DPRINTF("sio_open: %s: unknown device type\n", str); return NULL; } diff --git a/lib/libsndio/sun.c b/lib/libsndio/sun.c index 74aaf49a8da..b4030c7dc0f 100644 --- a/lib/libsndio/sun.c +++ b/lib/libsndio/sun.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sun.c,v 1.17 2009/05/15 13:16:58 ratchov Exp $ */ +/* $OpenBSD: sun.c,v 1.18 2009/07/25 08:44:27 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -342,20 +342,20 @@ sun_setvol(struct sio_hdl *sh, unsigned vol) } struct sio_hdl * -sio_open_sun(char *path, unsigned mode, int nbio) +sio_open_sun(char *str, unsigned mode, int nbio) { int fd, flags, fullduplex; struct sun_hdl *hdl; struct audio_info aui; struct sio_par par; + char path[PATH_MAX]; hdl = malloc(sizeof(struct sun_hdl)); if (hdl == NULL) return NULL; sio_create(&hdl->sio, &sun_ops, mode, nbio); - if (path == NULL) - path = SIO_SUN_PATH; + snprintf(path, sizeof(path), "/dev/audio%s", str); if (mode == (SIO_PLAY | SIO_REC)) flags = O_RDWR; else |