diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 2004-06-27 19:44:49 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 2004-06-27 19:44:49 +0000 |
commit | af9cf440474893bc7dab5537c61e8bebcbc81807 (patch) | |
tree | adcdf8143d4db11402ff24fedfaf1d5bd6362ef7 /sys/dev/midi.c | |
parent | 739008766bfd25d4eb6ba3069aa0b9562d33c322 (diff) |
better midi stuff from alex@caoua.org
Diffstat (limited to 'sys/dev/midi.c')
-rw-r--r-- | sys/dev/midi.c | 1140 |
1 files changed, 505 insertions, 635 deletions
diff --git a/sys/dev/midi.c b/sys/dev/midi.c index 710f59d1456..ed9fe56c21c 100644 --- a/sys/dev/midi.c +++ b/sys/dev/midi.c @@ -1,778 +1,648 @@ -/* $OpenBSD: midi.c,v 1.9 2003/09/23 16:51:12 millert Exp $ */ -/* $NetBSD: midi.c,v 1.10 1998/12/20 14:26:44 drochner Exp $ */ - /* - * Copyright (c) 1998 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Lennart Augustsson (augustss@netbsd.org). + * Copyright (c) 2003, 2004 Alexandre Ratchov * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * 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. * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * 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. + */ + +/* + * TODO + * - put the sequencer stuff in sequencer.c and sequencervar.h + * there is no reason to have it here. The sequencer + * driver need only to open the midi hw_if thus it does not + * need this driver */ #include "midi.h" -#include "audio.h" #include "sequencer.h" +#if NMIDI > 0 #include <sys/param.h> -#include <sys/ioctl.h> #include <sys/fcntl.h> -#include <sys/vnode.h> -#include <sys/select.h> -#include <sys/poll.h> -#include <sys/malloc.h> -#include <sys/proc.h> #include <sys/systm.h> -#include <sys/syslog.h> +#include <sys/ioctl.h> +#include <sys/exec.h> +#include <sys/conf.h> +#include <sys/lkm.h> +#include <sys/proc.h> +#include <sys/poll.h> #include <sys/kernel.h> +#include <sys/timeout.h> +#include <sys/vnode.h> #include <sys/signalvar.h> -#include <sys/conf.h> -#include <sys/audioio.h> -#include <sys/midiio.h> +#include <sys/malloc.h> #include <sys/device.h> -#include <dev/audio_if.h> #include <dev/midi_if.h> +#include <dev/audio_if.h> #include <dev/midivar.h> -#if NMIDI > 0 - -#ifdef AUDIO_DEBUG -#define DPRINTF(x) if (mididebug) printf x -#define DPRINTFN(n,x) if (mididebug >= (n)) printf x -int mididebug = 0; -#else -#define DPRINTF(x) -#define DPRINTFN(n,x) -#endif +int midiopen(dev_t, int, int, struct proc *); +int midiclose(dev_t, int, int, struct proc *); +int midiread(dev_t, struct uio *, int); +int midiwrite(dev_t, struct uio *, int); +int midipoll(dev_t, int, struct proc *); +int midiioctl(dev_t, u_long, caddr_t, int, struct proc *); +int midiprobe(struct device *, void *, void *); +void midiattach(struct device *, struct device *, void *); +int mididetach(struct device *, int); +int midiprint(void *, const char *); -int midi_wait; +void midi_iintr(void *, int); +void midi_ointr(void *); +void midi_out_start(struct midi_softc *); +void midi_out_stop(struct midi_softc *); +void midi_out_do(struct midi_softc *); +void midi_attach(struct midi_softc *, struct device *); -void midi_in(void *, int); -void midi_out(void *); -int midi_start_output(struct midi_softc *, int); -int midi_sleep_timo(int *, char *, int); -int midi_sleep(int *, char *); -void midi_wakeup(int *); -void midi_initbuf(struct midi_buffer *); -void midi_timeout(void *); -#define __BROKEN_INDIRECT_CONFIG /* XXX */ -#ifdef __BROKEN_INDIRECT_CONFIG -int midiprobe(struct device *, void *, void *); -#else -int midiprobe(struct device *, struct cfdata *, void *); +#if NSEQUENCER > 0 +int midi_unit_count(void); +struct midi_hw_if *midi_get_hwif(int); +void midi_toevent(struct midi_softc *, int); +int midi_writebytes(int, u_char *, int); +void midiseq_in(struct midi_dev *, u_char *, int); #endif -void midiattach(struct device *, struct device *, void *); struct cfattach midi_ca = { - sizeof(struct midi_softc), midiprobe, midiattach + sizeof(struct midi_softc), midiprobe, midiattach, mididetach }; struct cfdriver midi_cd = { NULL, "midi", DV_DULL }; -#ifdef MIDI_SAVE -#define MIDI_SAVE_SIZE 100000 -int midicnt; -struct { - int cnt; - u_char buf[MIDI_SAVE_SIZE]; -} midisave; -#define MIDI_GETSAVE _IOWR('m', 100, int) - -#endif - -int -midiprobe(parent, match, aux) - struct device *parent; -#ifdef __BROKEN_INDIRECT_CONFIG - void *match; -#else - struct cfdata *match; -#endif - void *aux; -{ - struct audio_attach_args *sa = aux; - - DPRINTFN(6,("midiprobe: type=%d sa=%p hw=%p\n", - sa->type, sa, sa->hwif)); - return ((sa->type == AUDIODEV_TYPE_MIDI) ? 1 : 0); -} void -midiattach(parent, self, aux) - struct device *parent, *self; - void *aux; +midi_iintr(void *addr, int data) { - struct midi_softc *sc = (void *)self; - struct audio_attach_args *sa = aux; - struct midi_hw_if *hwp = sa->hwif; - void *hdlp = sa->hdl; - - DPRINTFN(6, ("MIDI attach\n")); - -#ifdef DIAGNOSTIC - if (hwp == 0 || - hwp->open == 0 || - hwp->close == 0 || - hwp->output == 0 || - hwp->getinfo == 0) { - printf("midi: missing method\n"); + struct midi_softc *sc = (struct midi_softc *)addr; + struct midi_buffer *mb = &sc->inbuf; + int s; + + if (sc->isdying || !sc->isopen || !(sc->flags & FREAD)) return; + +#if NSEQUENCER > 0 + if (sc->seqopen) { + midi_toevent(sc, data); return; } #endif - sc->hw_if = hwp; - sc->hw_hdl = hdlp; - midi_attach(sc, parent); + if (MIDIBUF_ISFULL(mb)) + return; /* discard data */ + + s = splaudio(); + MIDIBUF_WRITE(mb, data); + splx(s); + + if (sc->rchan) { + sc->rchan = 0; + wakeup(&sc->rchan); + } + selwakeup(&sc->rsel); + if (sc->async) + psignal(sc->async, SIGIO); } -void -midi_attach(sc, parent) - struct midi_softc *sc; - struct device *parent; -{ - struct midi_info mi; - - sc->isopen = 0; - - midi_wait = MIDI_WAIT * hz / 1000000; - if (midi_wait == 0) - midi_wait = 1; - - sc->sc_dev = parent; - sc->hw_if->getinfo(sc->hw_hdl, &mi); - sc->props = mi.props; - timeout_set(&sc->timeo, midi_timeout, sc); - printf(": <%s>\n", mi.name); -} int -midi_unit_count() +midiread(dev_t dev, struct uio *uio, int ioflag) { - return (midi_cd.cd_ndevs); -} + struct midi_softc *sc = MIDI_DEV2SC(dev); + struct midi_buffer *mb = &sc->inbuf; + unsigned count; + int s, error; + + if (!(sc->flags & FREAD)) + return ENXIO; + + /* if there is no data then sleep (unless IO_NDELAY flag is set) */ -void -midi_initbuf(mb) - struct midi_buffer *mb; -{ - mb->used = 0; - mb->usedhigh = MIDI_BUFSIZE; - mb->end = mb->start + mb->usedhigh; - mb->inp = mb->outp = mb->start; + s = splaudio(); + while(MIDIBUF_ISEMPTY(mb)) { + if (sc->isdying) { + splx(s); + return EIO; + } + if (ioflag & IO_NDELAY) { + splx(s); + return EWOULDBLOCK; + } + sc->rchan = 1; + error = tsleep(&sc->rchan, PWAIT|PCATCH, "mid_rd", 0); + if (error) { + splx(s); + return error; + } + } + + /* at this stage, there is at least 1 byte */ + + while (uio->uio_resid > 0 && mb->used > 0) { + count = MIDIBUF_SIZE - mb->start; + if (count > mb->used) + count = mb->used; + if (count > uio->uio_resid) + count = uio->uio_resid; + error = uiomove(mb->data + mb->start, count, uio); + if (error) { + splx(s); + return error; + } + MIDIBUF_REMOVE(mb, count); + } + splx(s); + return 0; } -int -midi_sleep_timo(chan, label, timo) - int *chan; - char *label; - int timo; -{ - int st; - - if (!label) - label = "midi"; - DPRINTFN(5, ("midi_sleep_timo: %p %s %d\n", chan, label, timo)); - *chan = 1; - st = tsleep(chan, PWAIT | PCATCH, label, timo); - *chan = 0; +void +midi_ointr(void *addr) +{ + struct midi_softc *sc = (struct midi_softc *)addr; + struct midi_buffer *mb; + int s; + + if (sc->isopen && !sc->isdying) { #ifdef MIDI_DEBUG - if (st != 0) - printf("midi_sleep: %d\n", st); + if (!sc->isbusy) { + printf("midi_ointr: output should be busy\n"); + } #endif - return (st); + mb = &sc->outbuf; + s = splaudio(); + if (mb->used == 0) + midi_out_stop(sc); + else + midi_out_do(sc); /* restart output */ + splx(s); + } } -int -midi_sleep(chan, label) - int *chan; - char *label; -{ - return (midi_sleep_timo(chan, label, 0)); -} void -midi_wakeup(chan) - int *chan; +midi_out_start(struct midi_softc *sc) { - if (*chan) { - DPRINTFN(5, ("midi_wakeup: %p\n", chan)); - wakeup(chan); - *chan = 0; + if (!sc->isbusy) { + sc->isbusy = 1; + midi_out_do(sc); } } -static int midi_lengths[] = { 2,2,2,2,1,1,2,0 }; -/* Number of bytes in a MIDI command */ -#define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7]) - void -midi_in(addr, data) - void *addr; - int data; +midi_out_stop(struct midi_softc *sc) { - struct midi_softc *sc = addr; - struct midi_buffer *mb = &sc->inbuf; - int i; - - if (!sc->isopen) - return; - if (data == MIDI_ACK) - return; - DPRINTFN(3, ("midi_in: %p 0x%02x\n", sc, data)); - if (!(sc->flags & FREAD)) - return; /* discard data if not reading */ - - switch(sc->in_state) { - case MIDI_IN_START: - if (MIDI_IS_STATUS(data)) { - switch(data) { - case 0xf0: /* Sysex */ - sc->in_state = MIDI_IN_SYSEX; - break; - case 0xf1: /* MTC quarter frame */ - case 0xf3: /* Song select */ - sc->in_state = MIDI_IN_DATA; - sc->in_msg[0] = data; - sc->in_pos = 1; - sc->in_left = 1; - break; - case 0xf2: /* Song position pointer */ - sc->in_state = MIDI_IN_DATA; - sc->in_msg[0] = data; - sc->in_pos = 1; - sc->in_left = 2; - break; - default: - if (MIDI_IS_COMMON(data)) { - sc->in_msg[0] = data; - sc->in_pos = 1; - goto deliver; - } else { - sc->in_state = MIDI_IN_DATA; - sc->in_msg[0] = sc->in_status = data; - sc->in_pos = 1; - sc->in_left = - MIDI_LENGTH(sc->in_status); - } - break; - } - } else { - if (MIDI_IS_STATUS(sc->in_status)) { - sc->in_state = MIDI_IN_DATA; - sc->in_msg[0] = sc->in_status; - sc->in_msg[1] = data; - sc->in_pos = 2; - sc->in_left = MIDI_LENGTH(sc->in_status) - 1; - } - } - return; - case MIDI_IN_DATA: - sc->in_msg[sc->in_pos++] = data; - if (--sc->in_left <= 0) - break; /* deliver data */ - return; - case MIDI_IN_SYSEX: - if (data == MIDI_SYSEX_END) - sc->in_state = MIDI_IN_START; - return; - } -deliver: - sc->in_state = MIDI_IN_START; -#if NSEQUENCER > 0 - if (sc->seqopen) { - extern void midiseq_in(struct midi_dev *,u_char *,int); - midiseq_in(sc->seq_md, sc->in_msg, sc->in_pos); - return; - } -#endif - - if (mb->used + sc->in_pos > mb->usedhigh) { - DPRINTF(("midi_in: buffer full, discard data=0x%02x\n", - sc->in_msg[0])); - return; + sc->isbusy = 0; + if (sc->wchan) { + sc->wchan = 0; + wakeup(&sc->wchan); } - for (i = 0; i < sc->in_pos; i++) { - *mb->inp++ = sc->in_msg[i]; - if (mb->inp >= mb->end) - mb->inp = mb->start; - mb->used++; - } - midi_wakeup(&sc->rchan); - selwakeup(&sc->rsel); + selwakeup(&sc->wsel); if (sc->async) psignal(sc->async, SIGIO); } -void -midi_out(addr) - void *addr; -{ - struct midi_softc *sc = addr; - - if (!sc->isopen) - return; - DPRINTFN(3, ("midi_out: %p\n", sc)); - midi_start_output(sc, 1); -} -int -midiopen(dev, flags, ifmt, p) - dev_t dev; - int flags, ifmt; - struct proc *p; + /* + * drain output buffer, must be called with + * interrupts disabled + */ +void +midi_out_do(struct midi_softc *sc) { - int unit = MIDIUNIT(dev); - struct midi_softc *sc; - struct midi_hw_if *hw; - int error; - - if (unit >= midi_cd.cd_ndevs || - (sc = midi_cd.cd_devs[unit]) == NULL) - return (ENXIO); - DPRINTF(("midiopen %p\n", sc)); - - hw = sc->hw_if; - if (!hw) - return (ENXIO); - if (sc->isopen) - return (EBUSY); - sc->in_state = MIDI_IN_START; - sc->in_status = 0; - error = hw->open(sc->hw_hdl, flags, midi_in, midi_out, sc); - if (error) - return (error); - sc->isopen++; - midi_initbuf(&sc->outbuf); - midi_initbuf(&sc->inbuf); - sc->flags = flags; - sc->rchan = 0; - sc->wchan = 0; - sc->pbus = 0; - sc->async = 0; + struct midi_buffer *mb = &sc->outbuf; + unsigned i, max; + unsigned data; + int error; + + /* + * If output interrupts are not supported then we write MIDI_MAXWRITE + * bytes instead of 1, and then we wait sc->wait + */ + + max = sc->props & MIDI_PROP_OUT_INTR ? 1 : MIDI_MAXWRITE; + for (i = max; i != 0;) { + if (mb->used == 0) + break; -#ifdef MIDI_SAVE - if (midicnt != 0) { - midisave.cnt = midicnt; - midicnt = 0; + MIDIBUF_READ(mb, data); + error = sc->hw_if->output(sc->hw_hdl, data); + /* + * EINPROGRESS means that data has been handled, + * but will not be sent immediately and thus will + * not generate interrupt, in this case we can + * send another byte + */ + if (error == EINPROGRESS) { + if (MIDIBUF_ISEMPTY(mb)) { + midi_out_stop(sc); + return; + } + } else + i--; } -#endif - - return (0); -} - -int -midiclose(dev, flags, ifmt, p) - dev_t dev; - int flags, ifmt; - struct proc *p; -{ - int unit = MIDIUNIT(dev); - struct midi_softc *sc = midi_cd.cd_devs[unit]; - struct midi_hw_if *hw = sc->hw_if; - int s, error; - - DPRINTF(("midiclose %p\n", sc)); - - midi_start_output(sc, 0); - error = 0; - s = splaudio(); - while (sc->outbuf.used > 0 && !error) { - DPRINTFN(2,("midiclose sleep used=%d\n", sc->outbuf.used)); - error = midi_sleep_timo(&sc->wchan, "mid_dr", 30*hz); + + if (!(sc->props & MIDI_PROP_OUT_INTR)) { + if (i < max) { + if (MIDIBUF_ISEMPTY(mb)) + midi_out_stop(sc); + } else + timeout_add(&sc->timeo, sc->wait); } - splx(s); - sc->isopen = 0; - hw->close(sc->hw_hdl); -#if NSEQUENCER > 0 - sc->seqopen = 0; - sc->seq_md = 0; -#endif - return (0); } + int -midiread(dev, uio, ioflag) - dev_t dev; - struct uio *uio; - int ioflag; +midiwrite(dev_t dev, struct uio *uio, int ioflag) { - int unit = MIDIUNIT(dev); - struct midi_softc *sc = midi_cd.cd_devs[unit]; - struct midi_buffer *mb = &sc->inbuf; - int error; - u_char *outp; - int used, cc, n, resid; - int s; - - DPRINTF(("midiread: %p, count=%d\n", sc, uio->uio_resid)); - - error = 0; - resid = uio->uio_resid; - while (uio->uio_resid == resid && !error) { + struct midi_softc *sc = MIDI_DEV2SC(dev); + struct midi_buffer *mb = &sc->outbuf; + unsigned count; + int s, error; + + if (!(sc->flags & FWRITE)) + return ENXIO; + if (sc->isdying) + return EIO; + + /* + * If IO_NDELAY flag is set then check if there is enough room + * in the buffer to store at least one byte. If not then dont + * start the write process. + */ + + if ((ioflag & IO_NDELAY) && MIDIBUF_ISFULL(mb) && + (uio->uio_resid > 0)) + return EWOULDBLOCK; + + while (uio->uio_resid > 0) { s = splaudio(); - while (mb->used <= 0) { + while (MIDIBUF_ISFULL(mb)) { if (ioflag & IO_NDELAY) { + /* + * At this stage at least one byte is already + * moved so we do not return EWOULDBLOCK + */ splx(s); - return (EWOULDBLOCK); + return 0; } - error = midi_sleep(&sc->rchan, "mid rd"); + sc->wchan = 1; + error = tsleep(&sc->wchan, PWAIT|PCATCH, "mid_wr", 0); if (error) { splx(s); - return (error); + return error; + } + if (sc->isdying) { + splx(s); + return EIO; } } - used = mb->used; - outp = mb->outp; - splx(s); - cc = used; /* maximum to read */ - n = mb->end - outp; - if (n < cc) - cc = n; /* don't read beyond end of buffer */ - if (uio->uio_resid < cc) - cc = uio->uio_resid; /* and no more than we want */ - DPRINTFN(3, ("midiread: uiomove cc=%d\n", cc)); - error = uiomove(outp, cc, uio); - if (error) - break; - used -= cc; - outp += cc; - if (outp >= mb->end) - outp = mb->start; - s = splaudio(); - mb->outp = outp; - mb->used = used; + + count = MIDIBUF_SIZE - MIDIBUF_END(mb); + if (count > MIDIBUF_AVAIL(mb)) + count = MIDIBUF_AVAIL(mb); + if (count > uio->uio_resid) + count = uio->uio_resid; + error = uiomove(mb->data + MIDIBUF_END(mb), count, uio); + if (error) { + splx(s); + return error; + } + mb->used += count; + midi_out_start(sc); splx(s); } - return (error); + return 0; } -void -midi_timeout(arg) - void *arg; -{ - struct midi_softc *sc = arg; - - DPRINTFN(3,("midi_timeout: %p\n", sc)); - midi_start_output(sc, 1); -} int -midi_start_output(sc, intr) - struct midi_softc *sc; - int intr; +midipoll(dev_t dev, int events, struct proc *p) { - struct midi_buffer *mb = &sc->outbuf; - u_char *outp; - int error; - int s; - int i, mmax; + struct midi_softc *sc = MIDI_DEV2SC(dev); + int s, revents; + + if (sc->isdying) + return EIO; - error = 0; - mmax = sc->props & MIDI_PROP_OUT_INTR ? 1 : MIDI_MAX_WRITE; + revents = 0; s = splaudio(); - if (sc->pbus && !intr) { - DPRINTFN(4, ("midi_start_output: busy\n")); - splx(s); - return (0); + if (events & (POLLIN | POLLRDNORM)) { + if (!MIDIBUF_ISEMPTY(&sc->inbuf)) + revents |= events & (POLLIN | POLLRDNORM); } - sc->pbus = 1; - for (i = 0; i < mmax && mb->used > 0 && !error; i++) { - outp = mb->outp; - splx(s); - DPRINTFN(4, ("midi_start_output: %p i=%d, data=0x%02x\n", - sc, i, *outp)); -#ifdef MIDI_SAVE - midisave.buf[midicnt] = *outp; - midicnt = (midicnt + 1) % MIDI_SAVE_SIZE; -#endif - error = sc->hw_if->output(sc->hw_hdl, *outp++); - if (outp >= mb->end) - outp = mb->start; - s = splaudio(); - mb->outp = outp; - mb->used--; + if (events & (POLLOUT | POLLWRNORM)) { + if (!MIDIBUF_ISFULL(&sc->outbuf)) + revents |= events & (POLLOUT | POLLWRNORM); + } + if (revents == 0) { + if (events & (POLLIN | POLLRDNORM)) + selrecord(p, &sc->rsel); + if (events & (POLLOUT | POLLWRNORM)) + selrecord(p, &sc->wsel); } - midi_wakeup(&sc->wchan); - selwakeup(&sc->wsel); - if (sc->async) - psignal(sc->async, SIGIO); - if (mb->used > 0) { - if (!(sc->props & MIDI_PROP_OUT_INTR)) - timeout_add(&sc->timeo, midi_wait); - } else - sc->pbus = 0; splx(s); - return (error); + return (revents); } + int -midiwrite(dev, uio, ioflag) - dev_t dev; - struct uio *uio; - int ioflag; +midiioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { - int unit = MIDIUNIT(dev); - struct midi_softc *sc = midi_cd.cd_devs[unit]; - struct midi_buffer *mb = &sc->outbuf; - int error; - u_char *inp; - int used, cc, n; - int s; + struct midi_softc *sc = MIDI_DEV2SC(dev); - DPRINTFN(2, ("midiwrite: %p, unit=%d, count=%d\n", sc, unit, - uio->uio_resid)); + if (sc->isdying) return EIO; - error = 0; - while (uio->uio_resid > 0 && !error) { - s = splaudio(); - if (mb->used >= mb->usedhigh) { - DPRINTFN(3,("midi_write: sleep used=%d hiwat=%d\n", - mb->used, mb->usedhigh)); - if (ioflag & IO_NDELAY) { - splx(s); - return (EWOULDBLOCK); - } - error = midi_sleep(&sc->wchan, "mid wr"); - if (error) { - splx(s); - return (error); - } - } - used = mb->used; - inp = mb->inp; - splx(s); - cc = mb->usedhigh - used; /* maximum to write */ - n = mb->end - inp; - if (n < cc) - cc = n; /* don't write beyond end of buffer */ - if (uio->uio_resid < cc) - cc = uio->uio_resid; /* and no more than we have */ - error = uiomove(inp, cc, uio); -#ifdef MIDI_DEBUG - if (error) - printf("midi_write:(1) uiomove failed %d; " - "cc=%d inp=%p\n", - error, cc, inp); -#endif - if (error) - break; - inp = mb->inp + cc; - if (inp >= mb->end) - inp = mb->start; - s = splaudio(); - mb->inp = inp; - mb->used += cc; - splx(s); - error = midi_start_output(sc, 0); + switch(cmd) { + case FIONBIO: + /* All handled in the upper FS layer */ + break; + case FIOASYNC: + if (*(int *)addr) { + if (sc->async) return EBUSY; + sc->async = p; + } else + sc->async = 0; + break; + default: + return ENOTTY; + break; } - return (error); + return 0; } -/* - * This write routine is only called from sequencer code and expects - * a write that is smaller than the MIDI buffer. - */ + int -midi_writebytes(unit, buf, cc) - int unit; - u_char *buf; - int cc; +midiopen(dev_t dev, int flags, int mode, struct proc *p) { - struct midi_softc *sc = midi_cd.cd_devs[unit]; - struct midi_buffer *mb = &sc->outbuf; - int n, s; + struct midi_softc *sc; + int err; + + if (MIDI_UNIT(dev) >= midi_cd.cd_ndevs) + return ENXIO; + sc = MIDI_DEV2SC(dev); + if (sc == NULL) /* there may be more units than devices */ + return ENXIO; + if (sc->isdying) + return EIO; + if (sc->isopen) + return EBUSY; - DPRINTFN(2, ("midi_writebytes: %p, unit=%d, cc=%d\n", sc, unit, cc)); - DPRINTFN(3, ("midi_writebytes: %x %x %x\n",buf[0],buf[1],buf[2])); + MIDIBUF_INIT(&sc->inbuf); + MIDIBUF_INIT(&sc->outbuf); + sc->isbusy = 0; + sc->rchan = sc->wchan = 0; + sc->async = 0; + sc->flags = flags; - s = splaudio(); - if (mb->used + cc >= mb->usedhigh) { - splx(s); - return (EWOULDBLOCK); - } - n = mb->end - mb->inp; - if (cc < n) - n = cc; - mb->used += cc; - bcopy(buf, mb->inp, n); - mb->inp += n; - if (mb->inp >= mb->end) { - mb->inp = mb->start; - cc -= n; - if (cc > 0) { - bcopy(buf + n, mb->inp, cc); - mb->inp += cc; + err = sc->hw_if->open(sc->hw_hdl, flags, midi_iintr, midi_ointr, sc); + if (err) + return err; + sc->isopen = 1; +#if NSEQUENCER > 0 + sc->seq_md = 0; + sc->seqopen = 0; + sc->evstatus = 0xff; +#endif + return 0; +} + + +int +midiclose(dev_t dev, int fflag, int devtype, struct proc *p) +{ + struct midi_softc *sc = MIDI_DEV2SC(dev); + struct midi_buffer *mb; + int error; + int s; + + mb = &sc->outbuf; + if (!sc->isdying) { + /* start draining output buffer */ + s = splaudio(); + if (!MIDIBUF_ISEMPTY(mb)) + midi_out_start(sc); + while (sc->isbusy) { + sc->wchan = 1; + error = tsleep(&sc->wchan, PWAIT|PCATCH, "mid_dr", 0); + if (error || sc->isdying) + break; } + splx(s); } - splx(s); - return (midi_start_output(sc, 0)); + + /* + * some hw_if->close() reset immediately the midi uart + * which flushes the internal buffer of the uart device, + * so we may lose some (important) data. To avoid this, we sleep 2*wait, + * which gives the time to the uart to drain its internal buffers. + * + * Note: we'd better sleep in the corresponding hw_if->close() + */ + + tsleep(&sc->wchan, PWAIT|PCATCH, "mid_cl", 2 * sc->wait); + sc->hw_if->close(sc->hw_hdl); + sc->isopen = 0; + return 0; } + int -midiioctl(dev, cmd, addr, flag, p) - dev_t dev; - u_long cmd; - caddr_t addr; - int flag; - struct proc *p; +midiprobe(struct device *parent, void *match, void *aux) { - int unit = MIDIUNIT(dev); - struct midi_softc *sc = midi_cd.cd_devs[unit]; - struct midi_hw_if *hw = sc->hw_if; - int error; - - DPRINTF(("midiioctl: %p cmd=0x%08lx\n", sc, cmd)); - error = 0; - switch (cmd) { - case FIONBIO: - /* All handled in the upper FS layer. */ - break; + struct audio_attach_args *sa = aux; + return (sa != NULL && (sa->type == AUDIODEV_TYPE_MIDI) ? 1 : 0); +} - case FIOASYNC: - if (*(int *)addr) { - if (sc->async) - return (EBUSY); - sc->async = p; - DPRINTF(("midi_ioctl: FIOASYNC %p\n", p)); - } else - sc->async = 0; - break; -#if 0 - case MIDI_PRETIME: - /* XXX OSS - * This should set up a read timeout, but that's - * why we have poll(), so there's nothing yet. */ - error = EINVAL; - break; -#endif +void +midi_attach(struct midi_softc *sc, struct device *parent) +{ + struct midi_info mi; + + sc->isdying = 0; + sc->wait = (hz * MIDI_MAXWRITE) / MIDI_RATE; + if (sc->wait == 0) + sc->wait = 1; + sc->hw_if->getinfo(sc->hw_hdl, &mi); + sc->props = mi.props; + sc->isopen = 0; + timeout_set(&sc->timeo, midi_ointr, sc); + printf(": <%s>\n", mi.name); +} -#ifdef MIDI_SAVE - case MIDI_GETSAVE: - error = copyout(&midisave, *(void **)addr, sizeof midisave); - break; -#endif - default: - if (hw->ioctl) - error = hw->ioctl(sc->hw_hdl, cmd, addr, flag, p); - else - error = ENOTTY; - break; +void +midiattach(struct device *parent, struct device *self, void *aux) +{ + struct midi_softc *sc = (struct midi_softc *)self; + struct audio_attach_args *sa = (struct audio_attach_args *)aux; + struct midi_hw_if *hwif = sa->hwif; + void *hdl = sa->hdl; + +#ifdef DIAGNOSTIC + if (hwif == 0 || + hwif->open == 0 || + hwif->close == 0 || + hwif->output == 0 || + hwif->getinfo == 0) { + printf("midi: missing method\n"); + return; } - return (error); +#endif + sc->hw_if = hwif; + sc->hw_hdl = hdl; + midi_attach(sc, parent); } + int -midipoll(dev, events, p) - dev_t dev; - int events; - struct proc *p; +mididetach(struct device *self, int flags) { - int unit = MIDIUNIT(dev); - struct midi_softc *sc = midi_cd.cd_devs[unit]; - int revents = 0, s = splaudio(); - - DPRINTF(("midipoll: %p events=0x%x\n", sc, events)); - - if (events & (POLLIN | POLLRDNORM)) { - if (sc->inbuf.used > 0) - revents |= events & (POLLIN | POLLRDNORM); - } - if (events & (POLLOUT | POLLWRNORM)) { - if (sc->outbuf.used < sc->outbuf.usedhigh) - revents |= events & (POLLOUT | POLLWRNORM); + struct midi_softc *sc = (struct midi_softc *)self; + int maj, mn; + + sc->isdying = 1; + if (sc->wchan) { + sc->wchan = 0; + wakeup(&sc->wchan); } - if (revents == 0) { - if (events & (POLLIN | POLLRDNORM)) - selrecord(p, &sc->rsel); - if (events & (POLLOUT | POLLWRNORM)) - selrecord(p, &sc->wsel); + if (sc->rchan) { + sc->rchan = 0; + wakeup(&sc->rchan); } + + /* locate the major number */ + for (maj = 0; maj < nchrdev; maj++) + if (cdevsw[maj].d_open == midiopen) + break; - splx(s); - return (revents); + /* Nuke the vnodes for any open instances (calls close). */ + mn = self->dv_unit; + vdevgone(maj, mn, mn, VCHR); + + return 0; } -void -midi_getinfo(dev, mi) - dev_t dev; - struct midi_info *mi; + +int +midiprint(void *aux, const char *pnp) { - int unit = MIDIUNIT(dev); - struct midi_softc *sc; + if (pnp) + printf("midi at %s", pnp); + return (UNCONF); +} + - if (unit >= midi_cd.cd_ndevs || - (sc = midi_cd.cd_devs[unit]) == NULL) +void +midi_getinfo(dev_t dev, struct midi_info *mi) +{ + struct midi_softc *sc = MIDI_DEV2SC(dev); + if (MIDI_UNIT(dev) >= midi_cd.cd_ndevs || sc == NULL || sc->isdying) { + mi->name = "unconfigured"; + mi->props = 0; return; + } sc->hw_if->getinfo(sc->hw_hdl, mi); } -#endif /* NMIDI > 0 */ - -#if (NMIDI > 0 || NMIDIBUS > 0) && NAUDIO > 0 - -int midiprint(void *, const char *); struct device * -midi_attach_mi(mhwp, hdlp, dev) - struct midi_hw_if *mhwp; - void *hdlp; - struct device *dev; +midi_attach_mi(struct midi_hw_if *hwif, void *hdl, struct device *dev) { struct audio_attach_args arg; -#ifdef DIAGNOSTIC - if (mhwp == NULL) { - printf("midi_attach_mi: NULL\n"); - return 0; - } -#endif arg.type = AUDIODEV_TYPE_MIDI; - arg.hwif = mhwp; - arg.hdl = hdlp; + arg.hwif = hwif; + arg.hdl = hdl; return config_found(dev, &arg, midiprint); } + int -midiprint(aux, pnp) - void *aux; - const char *pnp; +midi_unit_count(void) { - if (pnp) - printf("midi at %s", pnp); - return (UNCONF); + return midi_cd.cd_ndevs; } -#endif /* NMIDI > 0 || NMIDIBUS > 0 */ + +#if NSEQUENCER > 0 +#define MIDI_EVLEN(status) (midi_evlen[((status) >> 4) & 7]) +unsigned midi_evlen[] = { 2, 2, 2, 2, 1, 1, 2 }; + +void +midi_toevent(struct midi_softc *sc, int data) +{ + unsigned char mesg[3]; + + if (data >= 0xf8) { /* is it a realtime message ? */ + switch(data) { + case 0xf8: /* midi timer tic */ + case 0xfa: /* midi timer start */ + case 0xfb: /* midi timer continue (after stop) */ + case 0xfc: /* midi timer stop */ + mesg[0] = data; + midiseq_in(sc->seq_md, mesg, 1); + break; + default: + break; + } + } else if (data >= 0x80) { /* is it a common or voice message ? */ + sc->evstatus = data; + sc->evindex = 0; + } else { /* else it is a data byte */ + /* strip common messages and bogus data */ + if (sc->evstatus >= 0xf0 || sc->evstatus < 0x80) + return; + + sc->evdata[sc->evindex++] = data; + if (sc->evindex == MIDI_EVLEN(sc->evstatus)) { + sc->evindex = 0; + mesg[0] = sc->evstatus; + mesg[1] = sc->evdata[0]; + mesg[2] = sc->evdata[1]; + midiseq_in(sc->seq_md, mesg, 1 + MIDI_EVLEN(sc->evstatus)); + } + } +} + + +int +midi_writebytes(int unit, unsigned char *mesg, int mesglen) +{ + struct midi_softc *sc = midi_cd.cd_devs[unit]; + struct midi_buffer *mb = &sc->outbuf; + unsigned count; + int s; + + s = splaudio(); + if (mesglen > MIDIBUF_AVAIL(mb)) { + splx(s); + return EWOULDBLOCK; + } + + while (mesglen > 0) { + count = MIDIBUF_SIZE - MIDIBUF_END(mb); + if (count > MIDIBUF_AVAIL(mb)) count = MIDIBUF_AVAIL(mb); + if (count > mesglen) count = mesglen; + bcopy(mesg, mb->data + MIDIBUF_END(mb), count); + mb->used += count; + mesg += count; + mesglen -= count; + midi_out_start(sc); + } + splx(s); + return 0; +} + +#endif /* NSEQUENCER > 0 */ +#endif /* NMIDI > 0 */ |