NetBSD/sys/dev/sequencer.c
jdolecek e0cc03a09b merge kqueue branch into -current
kqueue provides a stateful and efficient event notification framework
currently supported events include socket, file, directory, fifo,
pipe, tty and device changes, and monitoring of processes and signals

kqueue is supported by all writable filesystems in NetBSD tree
(with exception of Coda) and all device drivers supporting poll(2)

based on work done by Jonathan Lemon for FreeBSD
initial NetBSD port done by Luke Mewburn and Jason Thorpe
2002-10-23 09:10:23 +00:00

1494 lines
32 KiB
C

/* $NetBSD: sequencer.c,v 1.21 2002/10/23 09:13:05 jdolecek 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).
*
* 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.
*
* 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.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sequencer.c,v 1.21 2002/10/23 09:13:05 jdolecek Exp $");
#include "sequencer.h"
#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/kernel.h>
#include <sys/signalvar.h>
#include <sys/conf.h>
#include <sys/audioio.h>
#include <sys/midiio.h>
#include <sys/device.h>
#include <dev/midi_if.h>
#include <dev/midivar.h>
#include <dev/sequencervar.h>
#define ADDTIMEVAL(a, b) ( \
(a)->tv_sec += (b)->tv_sec, \
(a)->tv_usec += (b)->tv_usec, \
(a)->tv_usec > 1000000 ? ((a)->tv_sec++, (a)->tv_usec -= 1000000) : 0\
)
#define SUBTIMEVAL(a, b) ( \
(a)->tv_sec -= (b)->tv_sec, \
(a)->tv_usec -= (b)->tv_usec, \
(a)->tv_usec < 0 ? ((a)->tv_sec--, (a)->tv_usec += 1000000) : 0\
)
#ifdef AUDIO_DEBUG
#define DPRINTF(x) if (sequencerdebug) printf x
#define DPRINTFN(n,x) if (sequencerdebug >= (n)) printf x
int sequencerdebug = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
#define SEQ_CMD(b) ((b)->arr[0])
#define SEQ_EDEV(b) ((b)->arr[1])
#define SEQ_ECMD(b) ((b)->arr[2])
#define SEQ_ECHAN(b) ((b)->arr[3])
#define SEQ_ENOTE(b) ((b)->arr[4])
#define SEQ_EPARM(b) ((b)->arr[5])
#define SEQ_EP1(b) ((b)->arr[4])
#define SEQ_EP2(b) ((b)->arr[5])
#define SEQ_XCMD(b) ((b)->arr[1])
#define SEQ_XDEV(b) ((b)->arr[2])
#define SEQ_XCHAN(b) ((b)->arr[3])
#define SEQ_XNOTE(b) ((b)->arr[4])
#define SEQ_XVEL(b) ((b)->arr[5])
#define SEQ_TCMD(b) ((b)->arr[1])
#define SEQ_TPARM(b) ((b)->arr[4])
#define SEQ_NOTE_MAX 128
#define SEQ_NOTE_XXX 255
#define SEQ_VEL_OFF 0
#define RECALC_TICK(t) ((t)->tick = 60 * 1000000L / ((t)->tempo * (t)->timebase))
struct sequencer_softc seqdevs[NSEQUENCER];
void sequencerattach __P((int));
void seq_reset __P((struct sequencer_softc *));
int seq_do_command __P((struct sequencer_softc *, seq_event_rec *));
int seq_do_extcommand __P((struct sequencer_softc *, seq_event_rec *));
int seq_do_chnvoice __P((struct sequencer_softc *, seq_event_rec *));
int seq_do_chncommon __P((struct sequencer_softc *, seq_event_rec *));
int seq_do_timing __P((struct sequencer_softc *, seq_event_rec *));
int seq_do_local __P((struct sequencer_softc *, seq_event_rec *));
int seq_do_sysex __P((struct sequencer_softc *, seq_event_rec *));
int seq_do_fullsize __P((struct sequencer_softc *, seq_event_rec *,
struct uio *));
int seq_timer __P((struct sequencer_softc *, int, int, seq_event_rec *));
static int seq_input_event __P((struct sequencer_softc *, seq_event_rec *));
int seq_drain __P((struct sequencer_softc *));
void seq_startoutput __P((struct sequencer_softc *));
void seq_timeout __P((void *));
int seq_to_new __P((seq_event_rec *, struct uio *));
static int seq_sleep_timo(int *, char *, int);
static int seq_sleep(int *, char *);
static void seq_wakeup(int *);
struct midi_softc;
int midiseq_out __P((struct midi_dev *, u_char *, u_int, int));
struct midi_dev *midiseq_open __P((int, int));
void midiseq_close __P((struct midi_dev *));
void midiseq_reset __P((struct midi_dev *));
int midiseq_noteon __P((struct midi_dev *, int, int, int));
int midiseq_noteoff __P((struct midi_dev *, int, int, int));
int midiseq_keypressure __P((struct midi_dev *, int, int, int));
int midiseq_pgmchange __P((struct midi_dev *, int, int));
int midiseq_chnpressure __P((struct midi_dev *, int, int));
int midiseq_ctlchange __P((struct midi_dev *, int, int, int));
int midiseq_pitchbend __P((struct midi_dev *, int, int));
int midiseq_loadpatch __P((struct midi_dev *, struct sysex_info *,
struct uio *));
int midiseq_putc __P((struct midi_dev *, int));
void midiseq_in __P((struct midi_dev *, u_char *, int));
dev_type_open(sequenceropen);
dev_type_close(sequencerclose);
dev_type_read(sequencerread);
dev_type_write(sequencerwrite);
dev_type_ioctl(sequencerioctl);
dev_type_poll(sequencerpoll);
dev_type_kqfilter(sequencerkqfilter);
const struct cdevsw sequencer_cdevsw = {
sequenceropen, sequencerclose, sequencerread, sequencerwrite,
sequencerioctl, nostop, notty, sequencerpoll, nommap,
sequencerkqfilter,
};
void
sequencerattach(n)
int n;
{
for (n = 0; n < NSEQUENCER; n++)
callout_init(&seqdevs[n].sc_callout);
}
int
sequenceropen(dev, flags, ifmt, p)
dev_t dev;
int flags, ifmt;
struct proc *p;
{
int unit = SEQUENCERUNIT(dev);
struct sequencer_softc *sc;
struct midi_dev *md;
int nmidi;
DPRINTF(("sequenceropen\n"));
if (unit >= NSEQUENCER)
return (ENXIO);
sc = &seqdevs[unit];
if (sc->isopen)
return EBUSY;
if (SEQ_IS_OLD(unit))
sc->mode = SEQ_OLD;
else
sc->mode = SEQ_NEW;
sc->isopen++;
sc->flags = flags & (FREAD|FWRITE);
sc->rchan = 0;
sc->wchan = 0;
sc->pbus = 0;
sc->async = 0;
sc->input_stamp = ~0;
sc->nmidi = 0;
nmidi = midi_unit_count();
sc->devs = malloc(nmidi * sizeof(struct midi_dev *),
M_DEVBUF, M_WAITOK);
for (unit = 0; unit < nmidi; unit++) {
md = midiseq_open(unit, flags);
if (md) {
sc->devs[sc->nmidi++] = md;
md->seq = sc;
}
}
sc->timer.timebase = 100;
sc->timer.tempo = 60;
sc->doingsysex = 0;
RECALC_TICK(&sc->timer);
sc->timer.last = 0;
microtime(&sc->timer.start);
SEQ_QINIT(&sc->inq);
SEQ_QINIT(&sc->outq);
sc->lowat = SEQ_MAXQ / 2;
seq_reset(sc);
DPRINTF(("sequenceropen: mode=%d, nmidi=%d\n", sc->mode, sc->nmidi));
return 0;
}
static int
seq_sleep_timo(chan, label, timo)
int *chan;
char *label;
int timo;
{
int st;
if (!label)
label = "seq";
DPRINTFN(5, ("seq_sleep_timo: %p %s %d\n", chan, label, timo));
*chan = 1;
st = tsleep(chan, PWAIT | PCATCH, label, timo);
*chan = 0;
#ifdef MIDI_DEBUG
if (st != 0)
printf("seq_sleep: %d\n", st);
#endif
return st;
}
static int
seq_sleep(chan, label)
int *chan;
char *label;
{
return seq_sleep_timo(chan, label, 0);
}
static void
seq_wakeup(chan)
int *chan;
{
if (*chan) {
DPRINTFN(5, ("seq_wakeup: %p\n", chan));
wakeup(chan);
*chan = 0;
}
}
int
seq_drain(sc)
struct sequencer_softc *sc;
{
int error;
DPRINTFN(3, ("seq_drain: %p, len=%d\n", sc, SEQ_QLEN(&sc->outq)));
seq_startoutput(sc);
error = 0;
while(!SEQ_QEMPTY(&sc->outq) && !error)
error = seq_sleep_timo(&sc->wchan, "seq_dr", 60*hz);
return (error);
}
void
seq_timeout(addr)
void *addr;
{
struct sequencer_softc *sc = addr;
DPRINTFN(4, ("seq_timeout: %p\n", sc));
sc->timeout = 0;
seq_startoutput(sc);
if (SEQ_QLEN(&sc->outq) < sc->lowat) {
seq_wakeup(&sc->wchan);
selnotify(&sc->wsel, 0);
if (sc->async)
psignal(sc->async, SIGIO);
}
}
void
seq_startoutput(sc)
struct sequencer_softc *sc;
{
struct sequencer_queue *q = &sc->outq;
seq_event_rec cmd;
if (sc->timeout)
return;
DPRINTFN(4, ("seq_startoutput: %p, len=%d\n", sc, SEQ_QLEN(q)));
while(!SEQ_QEMPTY(q) && !sc->timeout) {
SEQ_QGET(q, cmd);
seq_do_command(sc, &cmd);
}
}
int
sequencerclose(dev, flags, ifmt, p)
dev_t dev;
int flags, ifmt;
struct proc *p;
{
struct sequencer_softc *sc = &seqdevs[SEQUENCERUNIT(dev)];
int n, s;
DPRINTF(("sequencerclose: %p\n", sc));
seq_drain(sc);
s = splaudio();
if (sc->timeout) {
callout_stop(&sc->sc_callout);
sc->timeout = 0;
}
splx(s);
for (n = 0; n < sc->nmidi; n++)
midiseq_close(sc->devs[n]);
free(sc->devs, M_DEVBUF);
sc->isopen = 0;
return (0);
}
static int
seq_input_event(sc, cmd)
struct sequencer_softc *sc;
seq_event_rec *cmd;
{
struct sequencer_queue *q = &sc->inq;
DPRINTFN(2, ("seq_input_event: %02x %02x %02x %02x %02x %02x %02x %02x\n",
cmd->arr[0], cmd->arr[1], cmd->arr[2], cmd->arr[3],
cmd->arr[4], cmd->arr[5], cmd->arr[6], cmd->arr[7]));
if (SEQ_QFULL(q))
return (ENOMEM);
SEQ_QPUT(q, *cmd);
seq_wakeup(&sc->rchan);
selnotify(&sc->rsel, 0);
if (sc->async)
psignal(sc->async, SIGIO);
return 0;
}
void
seq_event_intr(addr, iev)
void *addr;
seq_event_rec *iev;
{
struct sequencer_softc *sc = addr;
union {
u_int32_t l;
u_int8_t b[4];
} u;
u_long t;
struct timeval now;
seq_event_rec ev;
microtime(&now);
SUBTIMEVAL(&now, &sc->timer.start);
t = now.tv_sec * 1000000 + now.tv_usec;
t /= sc->timer.tick;
if (t != sc->input_stamp) {
ev.arr[0] = SEQ_TIMING;
ev.arr[1] = TMR_WAIT_ABS;
ev.arr[2] = 0;
ev.arr[3] = 0;
u.l = t;
ev.arr[4] = u.b[0];
ev.arr[5] = u.b[1];
ev.arr[6] = u.b[2];
ev.arr[7] = u.b[3];
seq_input_event(sc, &ev);
sc->input_stamp = t;
}
seq_input_event(sc, iev);
}
int
sequencerread(dev, uio, ioflag)
dev_t dev;
struct uio *uio;
int ioflag;
{
struct sequencer_softc *sc = &seqdevs[SEQUENCERUNIT(dev)];
struct sequencer_queue *q = &sc->inq;
seq_event_rec ev;
int error, s;
DPRINTFN(20, ("sequencerread: %p, count=%d, ioflag=%x\n",
sc, (int) uio->uio_resid, ioflag));
if (sc->mode == SEQ_OLD) {
DPRINTFN(-1,("sequencerread: old read\n"));
return (EINVAL); /* XXX unimplemented */
}
error = 0;
while (SEQ_QEMPTY(q)) {
if (ioflag & IO_NDELAY)
return EWOULDBLOCK;
else {
error = seq_sleep(&sc->rchan, "seq rd");
if (error)
return error;
}
}
s = splaudio();
while (uio->uio_resid >= sizeof ev && !error && !SEQ_QEMPTY(q)) {
SEQ_QGET(q, ev);
error = uiomove(&ev, sizeof ev, uio);
}
splx(s);
return error;
}
int
sequencerwrite(dev, uio, ioflag)
dev_t dev;
struct uio *uio;
int ioflag;
{
struct sequencer_softc *sc = &seqdevs[SEQUENCERUNIT(dev)];
struct sequencer_queue *q = &sc->outq;
int error;
seq_event_rec cmdbuf;
int size;
DPRINTFN(2, ("sequencerwrite: %p, count=%d\n", sc, (int) uio->uio_resid));
error = 0;
size = sc->mode == SEQ_NEW ? sizeof cmdbuf : SEQOLD_CMDSIZE;
while (uio->uio_resid >= size) {
error = uiomove(&cmdbuf, size, uio);
if (error)
break;
if (sc->mode == SEQ_OLD)
if (seq_to_new(&cmdbuf, uio))
continue;
if (SEQ_CMD(&cmdbuf) == SEQ_FULLSIZE) {
/* We do it like OSS does, asynchronously */
error = seq_do_fullsize(sc, &cmdbuf, uio);
if (error)
break;
continue;
}
while (SEQ_QFULL(q)) {
seq_startoutput(sc);
if (SEQ_QFULL(q)) {
if (ioflag & IO_NDELAY)
return EWOULDBLOCK;
error = seq_sleep(&sc->wchan, "seq_wr");
if (error)
return error;
}
}
SEQ_QPUT(q, cmdbuf);
}
seq_startoutput(sc);
#ifdef SEQUENCER_DEBUG
if (error)
DPRINTFN(2, ("sequencerwrite: error=%d\n", error));
#endif
return error;
}
int
sequencerioctl(dev, cmd, addr, flag, p)
dev_t dev;
u_long cmd;
caddr_t addr;
int flag;
struct proc *p;
{
struct sequencer_softc *sc = &seqdevs[SEQUENCERUNIT(dev)];
struct synth_info *si;
struct midi_dev *md;
int devno;
int error;
int t;
DPRINTFN(2, ("sequencerioctl: %p cmd=0x%08lx\n", sc, cmd));
error = 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;
DPRINTF(("sequencer_ioctl: FIOASYNC %p\n", p));
} else
sc->async = 0;
break;
case SEQUENCER_RESET:
seq_reset(sc);
break;
case SEQUENCER_PANIC:
seq_reset(sc);
/* Do more? OSS doesn't */
break;
case SEQUENCER_SYNC:
if (sc->flags == FREAD)
return 0;
seq_drain(sc);
error = 0;
break;
case SEQUENCER_INFO:
si = (struct synth_info*)addr;
devno = si->device;
if (devno < 0 || devno >= sc->nmidi)
return EINVAL;
md = sc->devs[devno];
strncpy(si->name, md->name, sizeof si->name);
si->synth_type = SYNTH_TYPE_MIDI;
si->synth_subtype = md->subtype;
si->nr_voices = md->nr_voices;
si->instr_bank_size = md->instr_bank_size;
si->capabilities = md->capabilities;
break;
case SEQUENCER_NRSYNTHS:
*(int *)addr = sc->nmidi;
break;
case SEQUENCER_NRMIDIS:
*(int *)addr = sc->nmidi;
break;
case SEQUENCER_OUTOFBAND:
DPRINTFN(3, ("sequencer_ioctl: OOB=%02x %02x %02x %02x %02x %02x %02x %02x\n",
*(u_char *)addr, *(u_char *)(addr+1),
*(u_char *)(addr+2), *(u_char *)(addr+3),
*(u_char *)(addr+4), *(u_char *)(addr+5),
*(u_char *)(addr+6), *(u_char *)(addr+7)));
error = seq_do_command(sc, (seq_event_rec *)addr);
break;
case SEQUENCER_TMR_TIMEBASE:
t = *(int *)addr;
if (t < 1)
t = 1;
if (t > 10000)
t = 10000;
sc->timer.timebase = t;
*(int *)addr = t;
RECALC_TICK(&sc->timer);
break;
case SEQUENCER_TMR_START:
error = seq_timer(sc, TMR_START, 0, 0);
break;
case SEQUENCER_TMR_STOP:
error = seq_timer(sc, TMR_STOP, 0, 0);
break;
case SEQUENCER_TMR_CONTINUE:
error = seq_timer(sc, TMR_CONTINUE, 0, 0);
break;
case SEQUENCER_TMR_TEMPO:
t = *(int *)addr;
if (t < 8)
t = 8;
if (t > 250)
t = 250;
sc->timer.tempo = t;
*(int *)addr = t;
RECALC_TICK(&sc->timer);
break;
case SEQUENCER_TMR_SOURCE:
*(int *)addr = SEQUENCER_TMR_INTERNAL;
break;
case SEQUENCER_TMR_METRONOME:
/* noop */
break;
case SEQUENCER_THRESHOLD:
t = SEQ_MAXQ - *(int *)addr / sizeof (seq_event_rec);
if (t < 1)
t = 1;
if (t > SEQ_MAXQ)
t = SEQ_MAXQ;
sc->lowat = t;
break;
case SEQUENCER_CTRLRATE:
*(int *)addr = (sc->timer.tempo*sc->timer.timebase + 30) / 60;
break;
case SEQUENCER_GETTIME:
{
struct timeval now;
u_long t;
microtime(&now);
SUBTIMEVAL(&now, &sc->timer.start);
t = now.tv_sec * 1000000 + now.tv_usec;
t /= sc->timer.tick;
*(int *)addr = t;
break;
}
default:
DPRINTFN(-1,("sequencer_ioctl: unimpl %08lx\n", cmd));
error = EINVAL;
break;
}
return error;
}
int
sequencerpoll(dev, events, p)
dev_t dev;
int events;
struct proc *p;
{
struct sequencer_softc *sc = &seqdevs[SEQUENCERUNIT(dev)];
int revents = 0;
DPRINTF(("sequencerpoll: %p events=0x%x\n", sc, events));
if (events & (POLLIN | POLLRDNORM))
if (!SEQ_QEMPTY(&sc->inq))
revents |= events & (POLLIN | POLLRDNORM);
if (events & (POLLOUT | POLLWRNORM))
if (SEQ_QLEN(&sc->outq) < sc->lowat)
revents |= events & (POLLOUT | POLLWRNORM);
if (revents == 0) {
if (events & (POLLIN | POLLRDNORM))
selrecord(p, &sc->rsel);
if (events & (POLLOUT | POLLWRNORM))
selrecord(p, &sc->wsel);
}
return revents;
}
static void
filt_sequencerrdetach(struct knote *kn)
{
struct sequencer_softc *sc = kn->kn_hook;
int s;
s = splaudio();
SLIST_REMOVE(&sc->rsel.si_klist, kn, knote, kn_selnext);
splx(s);
}
static int
filt_sequencerread(struct knote *kn, long hint)
{
struct sequencer_softc *sc = kn->kn_hook;
/* XXXLUKEM (thorpej): make sure this is correct */
if (SEQ_QEMPTY(&sc->inq))
return (0);
kn->kn_data = sizeof(seq_event_rec);
return (1);
}
static const struct filterops sequencerread_filtops =
{ 1, NULL, filt_sequencerrdetach, filt_sequencerread };
static void
filt_sequencerwdetach(struct knote *kn)
{
struct sequencer_softc *sc = kn->kn_hook;
int s;
s = splaudio();
SLIST_REMOVE(&sc->wsel.si_klist, kn, knote, kn_selnext);
splx(s);
}
static int
filt_sequencerwrite(struct knote *kn, long hint)
{
struct sequencer_softc *sc = kn->kn_hook;
/* XXXLUKEM (thorpej): make sure this is correct */
if (SEQ_QLEN(&sc->outq) >= sc->lowat)
return (0);
kn->kn_data = sizeof(seq_event_rec);
return (1);
}
static const struct filterops sequencerwrite_filtops =
{ 1, NULL, filt_sequencerwdetach, filt_sequencerwrite };
int
sequencerkqfilter(dev_t dev, struct knote *kn)
{
struct sequencer_softc *sc = &seqdevs[SEQUENCERUNIT(dev)];
struct klist *klist;
int s;
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &sc->rsel.si_klist;
kn->kn_fop = &sequencerread_filtops;
break;
case EVFILT_WRITE:
klist = &sc->wsel.si_klist;
kn->kn_fop = &sequencerwrite_filtops;
break;
default:
return (1);
}
kn->kn_hook = sc;
s = splaudio();
SLIST_INSERT_HEAD(klist, kn, kn_selnext);
splx(s);
return (0);
}
void
seq_reset(sc)
struct sequencer_softc *sc;
{
int i, chn;
struct midi_dev *md;
for (i = 0; i < sc->nmidi; i++) {
md = sc->devs[i];
midiseq_reset(md);
for (chn = 0; chn < MAXCHAN; chn++) {
midiseq_ctlchange(md, chn, MIDI_CTRL_ALLOFF, 0);
midiseq_ctlchange(md, chn, MIDI_CTRL_RESET, 0);
midiseq_pitchbend(md, chn, MIDI_BEND_NEUTRAL);
}
}
}
int
seq_do_command(sc, b)
struct sequencer_softc *sc;
seq_event_rec *b;
{
int dev;
DPRINTFN(4, ("seq_do_command: %p cmd=0x%02x\n", sc, SEQ_CMD(b)));
switch(SEQ_CMD(b)) {
case SEQ_LOCAL:
return seq_do_local(sc, b);
case SEQ_TIMING:
return seq_do_timing(sc, b);
case SEQ_CHN_VOICE:
return seq_do_chnvoice(sc, b);
case SEQ_CHN_COMMON:
return seq_do_chncommon(sc, b);
case SEQ_SYSEX:
return seq_do_sysex(sc, b);
/* COMPAT */
case SEQOLD_MIDIPUTC:
dev = b->arr[2];
if (dev < 0 || dev >= sc->nmidi)
return (ENXIO);
return midiseq_putc(sc->devs[dev], b->arr[1]);
default:
DPRINTFN(-1,("seq_do_command: unimpl command %02x\n",
SEQ_CMD(b)));
return (EINVAL);
}
}
int
seq_do_chnvoice(sc, b)
struct sequencer_softc *sc;
seq_event_rec *b;
{
int cmd, dev, chan, note, parm, voice;
int error;
struct midi_dev *md;
dev = SEQ_EDEV(b);
if (dev < 0 || dev >= sc->nmidi)
return ENXIO;
md = sc->devs[dev];
cmd = SEQ_ECMD(b);
chan = SEQ_ECHAN(b);
note = SEQ_ENOTE(b);
parm = SEQ_EPARM(b);
DPRINTFN(2,("seq_do_chnvoice: cmd=%02x dev=%d chan=%d note=%d parm=%d\n",
cmd, dev, chan, note, parm));
voice = chan;
if (cmd == MIDI_NOTEON && parm == 0) {
cmd = MIDI_NOTEOFF;
parm = MIDI_HALF_VEL;
}
switch(cmd) {
case MIDI_NOTEON:
DPRINTFN(5, ("seq_do_chnvoice: noteon %p %d %d %d\n",
md, voice, note, parm));
error = midiseq_noteon(md, voice, note, parm);
break;
case MIDI_NOTEOFF:
error = midiseq_noteoff(md, voice, note, parm);
break;
case MIDI_KEY_PRESSURE:
error = midiseq_keypressure(md, voice, note, parm);
break;
default:
DPRINTFN(-1,("seq_do_chnvoice: unimpl command %02x\n", cmd));
error = EINVAL;
break;
}
return error;
}
int
seq_do_chncommon(sc, b)
struct sequencer_softc *sc;
seq_event_rec *b;
{
int cmd, dev, chan, p1, w14;
int error;
struct midi_dev *md;
union {
int16_t s;
u_int8_t b[2];
} u;
dev = SEQ_EDEV(b);
if (dev < 0 || dev >= sc->nmidi)
return ENXIO;
md = sc->devs[dev];
cmd = SEQ_ECMD(b);
chan = SEQ_ECHAN(b);
p1 = SEQ_EP1(b);
u.b[0] = b->arr[6];
u.b[1] = b->arr[7];
w14 = u.s;
DPRINTFN(2,("seq_do_chncommon: %02x\n", cmd));
error = 0;
switch(cmd) {
case MIDI_PGM_CHANGE:
error = midiseq_pgmchange(md, chan, p1);
break;
case MIDI_CTL_CHANGE:
if (chan > 15 || p1 > 127)
return 0; /* EINVAL */
error = midiseq_ctlchange(md, chan, p1, w14);
break;
case MIDI_PITCH_BEND:
error = midiseq_pitchbend(md, chan, w14);
break;
case MIDI_CHN_PRESSURE:
error = midiseq_chnpressure(md, chan, p1);
break;
default:
DPRINTFN(-1,("seq_do_chncommon: unimpl command %02x\n", cmd));
error = EINVAL;
break;
}
return (error);
}
int
seq_do_timing(sc, b)
struct sequencer_softc *sc;
seq_event_rec *b;
{
union {
int32_t i;
u_int8_t b[4];
} u;
u.b[0] = b->arr[4];
u.b[1] = b->arr[5];
u.b[2] = b->arr[6];
u.b[3] = b->arr[7];
return seq_timer(sc, SEQ_TCMD(b), u.i, b);
}
int
seq_do_local(sc, b)
struct sequencer_softc *sc;
seq_event_rec *b;
{
return (EINVAL);
}
int
seq_do_sysex(sc, b)
struct sequencer_softc *sc;
seq_event_rec *b;
{
int dev, i;
struct midi_dev *md;
u_int8_t c, *buf = &b->arr[2];
dev = SEQ_EDEV(b);
if (dev < 0 || dev >= sc->nmidi)
return (ENXIO);
DPRINTF(("seq_do_sysex: dev=%d\n", dev));
md = sc->devs[dev];
if (!sc->doingsysex) {
c = MIDI_SYSEX_START;
midiseq_out(md, &c, 1, 0);
sc->doingsysex = 1;
}
for (i = 0; i < 6 && buf[i] != 0xff; i++)
;
midiseq_out(md, buf, i, 0);
if (i < 6 || (i > 0 && buf[i-1] == MIDI_SYSEX_END))
sc->doingsysex = 0;
return (0);
}
int
seq_timer(sc, cmd, parm, b)
struct sequencer_softc *sc;
int cmd, parm;
seq_event_rec *b;
{
struct syn_timer *t = &sc->timer;
struct timeval when;
int ticks;
int error;
long long usec;
DPRINTFN(2,("seq_timer: %02x %d\n", cmd, parm));
error = 0;
switch(cmd) {
case TMR_WAIT_REL:
parm += t->last;
/* fall into */
case TMR_WAIT_ABS:
t->last = parm;
usec = (long long)parm * (long long)t->tick; /* convert to usec */
when.tv_sec = usec / 1000000;
when.tv_usec = usec % 1000000;
DPRINTFN(4, ("seq_timer: parm=%d, sleep when=%ld.%06ld", parm,
when.tv_sec, when.tv_usec));
ADDTIMEVAL(&when, &t->start); /* abstime for end */
ticks = hzto(&when);
DPRINTFN(4, (" when+start=%ld.%06ld, tick=%d\n",
when.tv_sec, when.tv_usec, ticks));
if (ticks > 0) {
#ifdef DIAGNOSTIC
if (ticks > 20 * hz) {
/* Waiting more than 20s */
printf("seq_timer: funny ticks=%d, usec=%lld, parm=%d, tick=%ld\n",
ticks, usec, parm, t->tick);
}
#endif
sc->timeout = 1;
callout_reset(&sc->sc_callout, ticks,
seq_timeout, sc);
}
#ifdef SEQUENCER_DEBUG
else if (tick < 0)
DPRINTF(("seq_timer: ticks = %d\n", ticks));
#endif
break;
case TMR_START:
microtime(&t->start);
t->running = 1;
break;
case TMR_STOP:
microtime(&t->stop);
t->running = 0;
break;
case TMR_CONTINUE:
microtime(&when);
SUBTIMEVAL(&when, &t->stop);
ADDTIMEVAL(&t->start, &when);
t->running = 1;
break;
case TMR_TEMPO:
/* parm is ticks per minute / timebase */
if (parm < 8)
parm = 8;
if (parm > 360)
parm = 360;
t->tempo = parm;
RECALC_TICK(t);
break;
case TMR_ECHO:
error = seq_input_event(sc, b);
break;
case TMR_RESET:
t->last = 0;
microtime(&t->start);
break;
default:
DPRINTF(("seq_timer: unknown %02x\n", cmd));
error = EINVAL;
break;
}
return (error);
}
int
seq_do_fullsize(sc, b, uio)
struct sequencer_softc *sc;
seq_event_rec *b;
struct uio *uio;
{
struct sysex_info sysex;
u_int dev;
#ifdef DIAGNOSTIC
if (sizeof(seq_event_rec) != SEQ_SYSEX_HDRSIZE) {
printf("seq_do_fullsize: sysex size ??\n");
return EINVAL;
}
#endif
memcpy(&sysex, b, sizeof sysex);
dev = sysex.device_no;
DPRINTFN(2, ("seq_do_fullsize: fmt=%04x, dev=%d, len=%d\n",
sysex.key, dev, sysex.len));
return (midiseq_loadpatch(sc->devs[dev], &sysex, uio));
}
/* Convert an old sequencer event to a new one. */
int
seq_to_new(ev, uio)
seq_event_rec *ev;
struct uio *uio;
{
int cmd, chan, note, parm;
u_int32_t delay;
int error;
cmd = SEQ_CMD(ev);
chan = ev->arr[1];
note = ev->arr[2];
parm = ev->arr[3];
DPRINTFN(3, ("seq_to_new: 0x%02x %d %d %d\n", cmd, chan, note, parm));
if (cmd >= 0x80) {
/* Fill the event record */
if (uio->uio_resid >= sizeof *ev - SEQOLD_CMDSIZE) {
error = uiomove(&ev->arr[SEQOLD_CMDSIZE],
sizeof *ev - SEQOLD_CMDSIZE, uio);
if (error)
return error;
} else
return EINVAL;
}
switch(cmd) {
case SEQOLD_NOTEOFF:
note = 255;
SEQ_ECMD(ev) = MIDI_NOTEOFF;
goto onoff;
case SEQOLD_NOTEON:
SEQ_ECMD(ev) = MIDI_NOTEON;
onoff:
SEQ_CMD(ev) = SEQ_CHN_VOICE;
SEQ_EDEV(ev) = 0;
SEQ_ECHAN(ev) = chan;
SEQ_ENOTE(ev) = note;
SEQ_EPARM(ev) = parm;
break;
case SEQOLD_WAIT:
delay = *(u_int32_t *)ev->arr >> 8;
SEQ_CMD(ev) = SEQ_TIMING;
SEQ_TCMD(ev) = TMR_WAIT_REL;
*(u_int32_t *)&ev->arr[4] = delay;
break;
case SEQOLD_SYNCTIMER:
SEQ_CMD(ev) = SEQ_TIMING;
SEQ_TCMD(ev) = TMR_RESET;
break;
case SEQOLD_PGMCHANGE:
SEQ_ECMD(ev) = MIDI_PGM_CHANGE;
SEQ_CMD(ev) = SEQ_CHN_COMMON;
SEQ_EDEV(ev) = 0;
SEQ_ECHAN(ev) = chan;
SEQ_EP1(ev) = note;
break;
case SEQOLD_MIDIPUTC:
break; /* interpret in normal mode */
case SEQOLD_ECHO:
case SEQOLD_PRIVATE:
case SEQOLD_EXTENDED:
default:
DPRINTF(("seq_to_new: not impl 0x%02x\n", cmd));
return EINVAL;
/* In case new events show up */
case SEQ_TIMING:
case SEQ_CHN_VOICE:
case SEQ_CHN_COMMON:
case SEQ_FULLSIZE:
break;
}
return 0;
}
/**********************************************/
void
midiseq_in(md, msg, len)
struct midi_dev *md;
u_char *msg;
int len;
{
int unit = md->unit;
seq_event_rec ev;
int status, chan;
DPRINTFN(2, ("midiseq_in: %p %02x %02x %02x\n",
md, msg[0], msg[1], msg[2]));
status = MIDI_GET_STATUS(msg[0]);
chan = MIDI_GET_CHAN(msg[0]);
switch (status) {
case MIDI_NOTEON:
if (msg[2] == 0) {
status = MIDI_NOTEOFF;
msg[2] = MIDI_HALF_VEL;
}
/* fall into */
case MIDI_NOTEOFF:
case MIDI_KEY_PRESSURE:
SEQ_MK_CHN_VOICE(&ev, unit, status, chan, msg[1], msg[2]);
break;
case MIDI_CTL_CHANGE:
SEQ_MK_CHN_COMMON(&ev, unit, status, chan, msg[1], 0, msg[2]);
break;
case MIDI_PGM_CHANGE:
case MIDI_CHN_PRESSURE:
SEQ_MK_CHN_COMMON(&ev, unit, status, chan, msg[1], 0, 0);
break;
case MIDI_PITCH_BEND:
SEQ_MK_CHN_COMMON(&ev, unit, status, chan, 0, 0,
(msg[1] & 0x7f) | ((msg[2] & 0x7f) << 7));
break;
default:
return;
}
seq_event_intr(md->seq, &ev);
}
struct midi_dev *
midiseq_open(unit, flags)
int unit;
int flags;
{
extern struct cfdriver midi_cd;
extern const struct cdevsw midi_cdevsw;
int error;
struct midi_dev *md;
struct midi_softc *sc;
struct midi_info mi;
DPRINTFN(2, ("midiseq_open: %d %d\n", unit, flags));
error = (*midi_cdevsw.d_open)(makedev(0, unit), flags, 0, 0);
if (error)
return (0);
sc = midi_cd.cd_devs[unit];
sc->seqopen = 1;
md = malloc(sizeof *md, M_DEVBUF, M_WAITOK|M_ZERO);
sc->seq_md = md;
md->msc = sc;
midi_getinfo(makedev(0, unit), &mi);
md->unit = unit;
md->name = mi.name;
md->subtype = 0;
md->nr_voices = 128; /* XXX */
md->instr_bank_size = 128; /* XXX */
if (mi.props & MIDI_PROP_CAN_INPUT)
md->capabilities |= SYNTH_CAP_INPUT;
return (md);
}
void
midiseq_close(md)
struct midi_dev *md;
{
extern const struct cdevsw midi_cdevsw;
DPRINTFN(2, ("midiseq_close: %d\n", md->unit));
(*midi_cdevsw.d_close)(makedev(0, md->unit), 0, 0, 0);
free(md, M_DEVBUF);
}
void
midiseq_reset(md)
struct midi_dev *md;
{
/* XXX send GM reset? */
DPRINTFN(3, ("midiseq_reset: %d\n", md->unit));
}
int
midiseq_out(md, buf, cc, chk)
struct midi_dev *md;
u_char *buf;
u_int cc;
int chk;
{
DPRINTFN(5, ("midiseq_out: m=%p, unit=%d, buf[0]=0x%02x, cc=%d\n",
md->msc, md->unit, buf[0], cc));
/* The MIDI "status" byte does not have to be repeated. */
if (chk && md->last_cmd == buf[0])
buf++, cc--;
else
md->last_cmd = buf[0];
return midi_writebytes(md->unit, buf, cc);
}
int
midiseq_noteon(md, chan, note, vel)
struct midi_dev *md;
int chan, note, vel;
{
u_char buf[3];
DPRINTFN(6, ("midiseq_noteon 0x%02x %d %d\n",
MIDI_NOTEON | chan, note, vel));
if (chan < 0 || chan > 15 ||
note < 0 || note > 127)
return EINVAL;
if (vel < 0) vel = 0;
if (vel > 127) vel = 127;
buf[0] = MIDI_NOTEON | chan;
buf[1] = note;
buf[2] = vel;
return midiseq_out(md, buf, 3, 1);
}
int
midiseq_noteoff(md, chan, note, vel)
struct midi_dev *md;
int chan, note, vel;
{
u_char buf[3];
if (chan < 0 || chan > 15 ||
note < 0 || note > 127)
return EINVAL;
if (vel < 0) vel = 0;
if (vel > 127) vel = 127;
buf[0] = MIDI_NOTEOFF | chan;
buf[1] = note;
buf[2] = vel;
return midiseq_out(md, buf, 3, 1);
}
int
midiseq_keypressure(md, chan, note, vel)
struct midi_dev *md;
int chan, note, vel;
{
u_char buf[3];
if (chan < 0 || chan > 15 ||
note < 0 || note > 127)
return EINVAL;
if (vel < 0) vel = 0;
if (vel > 127) vel = 127;
buf[0] = MIDI_KEY_PRESSURE | chan;
buf[1] = note;
buf[2] = vel;
return midiseq_out(md, buf, 3, 1);
}
int
midiseq_pgmchange(md, chan, parm)
struct midi_dev *md;
int chan, parm;
{
u_char buf[2];
if (chan < 0 || chan > 15 ||
parm < 0 || parm > 127)
return EINVAL;
buf[0] = MIDI_PGM_CHANGE | chan;
buf[1] = parm;
return midiseq_out(md, buf, 2, 1);
}
int
midiseq_chnpressure(md, chan, parm)
struct midi_dev *md;
int chan, parm;
{
u_char buf[2];
if (chan < 0 || chan > 15 ||
parm < 0 || parm > 127)
return EINVAL;
buf[0] = MIDI_CHN_PRESSURE | chan;
buf[1] = parm;
return midiseq_out(md, buf, 2, 1);
}
int
midiseq_ctlchange(md, chan, parm, w14)
struct midi_dev *md;
int chan, parm, w14;
{
u_char buf[3];
if (chan < 0 || chan > 15 ||
parm < 0 || parm > 127)
return EINVAL;
buf[0] = MIDI_CTL_CHANGE | chan;
buf[1] = parm;
buf[2] = w14 & 0x7f;
return midiseq_out(md, buf, 3, 1);
}
int
midiseq_pitchbend(md, chan, parm)
struct midi_dev *md;
int chan, parm;
{
u_char buf[3];
if (chan < 0 || chan > 15)
return EINVAL;
buf[0] = MIDI_PITCH_BEND | chan;
buf[1] = parm & 0x7f;
buf[2] = (parm >> 7) & 0x7f;
return midiseq_out(md, buf, 3, 1);
}
int
midiseq_loadpatch(md, sysex, uio)
struct midi_dev *md;
struct sysex_info *sysex;
struct uio *uio;
{
u_char c, buf[128];
int i, cc, error;
if (sysex->key != SEQ_SYSEX_PATCH) {
DPRINTFN(-1,("midiseq_loadpatch: bad patch key 0x%04x\n",
sysex->key));
return (EINVAL);
}
if (uio->uio_resid < sysex->len)
/* adjust length, should be an error */
sysex->len = uio->uio_resid;
DPRINTFN(2, ("midiseq_loadpatch: len=%d\n", sysex->len));
if (sysex->len == 0)
return EINVAL;
error = uiomove(&c, 1, uio);
if (error)
return error;
if (c != MIDI_SYSEX_START) /* must start like this */
return EINVAL;
error = midiseq_out(md, &c, 1, 0);
if (error)
return error;
--sysex->len;
while (sysex->len > 0) {
cc = sysex->len;
if (cc > sizeof buf)
cc = sizeof buf;
error = uiomove(buf, cc, uio);
if (error)
break;
for(i = 0; i < cc && !MIDI_IS_STATUS(buf[i]); i++)
;
error = midiseq_out(md, buf, i, 0);
if (error)
break;
sysex->len -= i;
if (i != cc)
break;
}
/* Any leftover data in uio is rubbish;
* the SYSEX should be one write ending in SYSEX_END.
*/
uio->uio_resid = 0;
c = MIDI_SYSEX_END;
return midiseq_out(md, &c, 1, 0);
}
int
midiseq_putc(md, data)
struct midi_dev *md;
int data;
{
u_char c = data;
DPRINTFN(4,("midiseq_putc: 0x%02x\n", data));
return midiseq_out(md, &c, 1, 0);
}
#include "midi.h"
#if NMIDI == 0
dev_type_open(midiopen);
dev_type_close(midiclose);
const struct cdevsw midi_cdevsw = {
midiopen, midiclose, noread, nowrite, noioctl,
nostop, notty, nopoll, nommap,
};
/*
* If someone has a sequencer, but no midi devices there will
* be unresolved references, so we provide little stubs.
*/
int
midi_unit_count()
{
return (0);
}
int
midiopen(dev, flags, ifmt, p)
dev_t dev;
int flags, ifmt;
struct proc *p;
{
return (ENXIO);
}
struct cfdriver midi_cd;
void
midi_getinfo(dev, mi)
dev_t dev;
struct midi_info *mi;
{
}
int
midiclose(dev, flags, ifmt, p)
dev_t dev;
int flags, ifmt;
struct proc *p;
{
return (ENXIO);
}
int
midi_writebytes(unit, buf, cc)
int unit;
u_char *buf;
int cc;
{
return (ENXIO);
}
#endif /* NMIDI == 0 */