#include <sys/cdefs.h>
__FBSDID("$FreeBSD: head/sys/dev/sound/midi/sequencer.c 274035 2014-11-03 11:11:45Z bapt $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/filio.h>
#include <sys/lock.h>
#include <sys/sockio.h>
#include <sys/fcntl.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/syslog.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/bus.h>
#include <machine/clock.h>
#include <sys/soundcard.h>
#include <sys/rman.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/condvar.h>
#include <sys/kthread.h>
#include <sys/unistd.h>
#include <sys/device.h>
#ifdef HAVE_KERNEL_OPTION_HEADERS
#include "opt_snd.h"
#endif
#include <dev/sound/midi/midi.h>
#include <dev/sound/midi/midiq.h>
#include "synth_if.h"
#include <dev/sound/midi/sequencer.h>
#define TMR_TIMERBASE 13
#define SND_DEV_SEQ 1
#define SND_DEV_MUSIC 8
#define EV_SZ 8
#define IEV_SZ 8
#define LOOKUP_EXIST (0)
#define LOOKUP_OPEN (1)
#define LOOKUP_CLOSE (2)
#define PCMMKMINOR(u, d, c) \
((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
#define MIDIMKMINOR(u, d, c) PCMMKMINOR(u, d, c)
#define MIDIUNIT(y) ((dev2unit(y) >> 4) & 0x0f)
#define MIDIDEV(y) (dev2unit(y) & 0x0f)
static d_open_t seq_open;
static d_close_t seq_close;
static d_ioctl_t seq_ioctl;
static d_read_t seq_read;
static d_write_t seq_write;
static d_kqfilter_t seq_kqfilter;
static void seq_filter_detach(struct knote *);
static int seq_filter_read(struct knote *, long);
static int seq_filter_write(struct knote *, long);
static struct dev_ops seq_ops = {
{ "sequencer", 0, D_MPSAFE },
.d_open = seq_open,
.d_close = seq_close,
.d_read = seq_read,
.d_write = seq_write,
.d_ioctl = seq_ioctl,
.d_kqfilter = seq_kqfilter,
};
static struct filterops seq_read_filterops =
{ FILTEROP_ISFD, NULL, seq_filter_detach, seq_filter_read };
static struct filterops seq_write_filterops =
{ FILTEROP_ISFD, NULL, seq_filter_detach, seq_filter_write };
struct seq_softc {
KOBJ_FIELDS;
struct lock seq_lock, q_lock;
struct cv empty_cv, reset_cv, in_cv, out_cv, state_cv, th_cv;
MIDIQ_HEAD(, u_char) in_q, out_q;
u_long flags;
int fflags;
int music;
int out_water;
snd_sync_parm sync_parm;
struct thread *sync_thread;
struct kqinfo in_kq, out_kq;
int midi_number;
struct cdev *seqdev, *musicdev;
int unit;
int maxunits;
kobj_t *midis;
int *midi_flags;
kobj_t mapper;
void *mapper_cookie;
struct timeval timerstop, timersub;
int timerbase, tempo;
int timerrun;
int done;
int playing;
int recording;
int busy;
int pre_event_timeout;
int waiting;
};
SYSCTL_NODE(_hw_midi, OID_AUTO, seq, CTLFLAG_RD, 0, "Midi sequencer");
int seq_debug;
SYSCTL_INT(_hw_midi_seq, OID_AUTO, debug, CTLFLAG_RW, &seq_debug, 0, "");
midi_cmdtab cmdtab_seqevent[] = {
{SEQ_NOTEOFF, "SEQ_NOTEOFF"},
{SEQ_NOTEON, "SEQ_NOTEON"},
{SEQ_WAIT, "SEQ_WAIT"},
{SEQ_PGMCHANGE, "SEQ_PGMCHANGE"},
{SEQ_SYNCTIMER, "SEQ_SYNCTIMER"},
{SEQ_MIDIPUTC, "SEQ_MIDIPUTC"},
{SEQ_DRUMON, "SEQ_DRUMON"},
{SEQ_DRUMOFF, "SEQ_DRUMOFF"},
{SEQ_ECHO, "SEQ_ECHO"},
{SEQ_AFTERTOUCH, "SEQ_AFTERTOUCH"},
{SEQ_CONTROLLER, "SEQ_CONTROLLER"},
{SEQ_BALANCE, "SEQ_BALANCE"},
{SEQ_VOLMODE, "SEQ_VOLMODE"},
{SEQ_FULLSIZE, "SEQ_FULLSIZE"},
{SEQ_PRIVATE, "SEQ_PRIVATE"},
{SEQ_EXTENDED, "SEQ_EXTENDED"},
{EV_SEQ_LOCAL, "EV_SEQ_LOCAL"},
{EV_TIMING, "EV_TIMING"},
{EV_CHN_COMMON, "EV_CHN_COMMON"},
{EV_CHN_VOICE, "EV_CHN_VOICE"},
{EV_SYSEX, "EV_SYSEX"},
{-1, NULL},
};
midi_cmdtab cmdtab_seqioctl[] = {
{SNDCTL_SEQ_RESET, "SNDCTL_SEQ_RESET"},
{SNDCTL_SEQ_SYNC, "SNDCTL_SEQ_SYNC"},
{SNDCTL_SYNTH_INFO, "SNDCTL_SYNTH_INFO"},
{SNDCTL_SEQ_CTRLRATE, "SNDCTL_SEQ_CTRLRATE"},
{SNDCTL_SEQ_GETOUTCOUNT, "SNDCTL_SEQ_GETOUTCOUNT"},
{SNDCTL_SEQ_GETINCOUNT, "SNDCTL_SEQ_GETINCOUNT"},
{SNDCTL_SEQ_PERCMODE, "SNDCTL_SEQ_PERCMODE"},
{SNDCTL_FM_LOAD_INSTR, "SNDCTL_FM_LOAD_INSTR"},
{SNDCTL_SEQ_TESTMIDI, "SNDCTL_SEQ_TESTMIDI"},
{SNDCTL_SEQ_RESETSAMPLES, "SNDCTL_SEQ_RESETSAMPLES"},
{SNDCTL_SEQ_NRSYNTHS, "SNDCTL_SEQ_NRSYNTHS"},
{SNDCTL_SEQ_NRMIDIS, "SNDCTL_SEQ_NRMIDIS"},
{SNDCTL_SEQ_GETTIME, "SNDCTL_SEQ_GETTIME"},
{SNDCTL_MIDI_INFO, "SNDCTL_MIDI_INFO"},
{SNDCTL_SEQ_THRESHOLD, "SNDCTL_SEQ_THRESHOLD"},
{SNDCTL_SYNTH_MEMAVL, "SNDCTL_SYNTH_MEMAVL"},
{SNDCTL_FM_4OP_ENABLE, "SNDCTL_FM_4OP_ENABLE"},
{SNDCTL_PMGR_ACCESS, "SNDCTL_PMGR_ACCESS"},
{SNDCTL_SEQ_PANIC, "SNDCTL_SEQ_PANIC"},
{SNDCTL_SEQ_OUTOFBAND, "SNDCTL_SEQ_OUTOFBAND"},
{SNDCTL_TMR_TIMEBASE, "SNDCTL_TMR_TIMEBASE"},
{SNDCTL_TMR_START, "SNDCTL_TMR_START"},
{SNDCTL_TMR_STOP, "SNDCTL_TMR_STOP"},
{SNDCTL_TMR_CONTINUE, "SNDCTL_TMR_CONTINUE"},
{SNDCTL_TMR_TEMPO, "SNDCTL_TMR_TEMPO"},
{SNDCTL_TMR_SOURCE, "SNDCTL_TMR_SOURCE"},
{SNDCTL_TMR_METRONOME, "SNDCTL_TMR_METRONOME"},
{SNDCTL_TMR_SELECT, "SNDCTL_TMR_SELECT"},
{SNDCTL_MIDI_PRETIME, "SNDCTL_MIDI_PRETIME"},
{AIONWRITE, "AIONWRITE"},
{AIOGSIZE, "AIOGSIZE"},
{AIOSSIZE, "AIOSSIZE"},
{AIOGFMT, "AIOGFMT"},
{AIOSFMT, "AIOSFMT"},
{AIOGMIX, "AIOGMIX"},
{AIOSMIX, "AIOSMIX"},
{AIOSTOP, "AIOSTOP"},
{AIOSYNC, "AIOSYNC"},
{AIOGCAP, "AIOGCAP"},
{-1, NULL},
};
midi_cmdtab cmdtab_timer[] = {
{TMR_WAIT_REL, "TMR_WAIT_REL"},
{TMR_WAIT_ABS, "TMR_WAIT_ABS"},
{TMR_STOP, "TMR_STOP"},
{TMR_START, "TMR_START"},
{TMR_CONTINUE, "TMR_CONTINUE"},
{TMR_TEMPO, "TMR_TEMPO"},
{TMR_ECHO, "TMR_ECHO"},
{TMR_CLOCK, "TMR_CLOCK"},
{TMR_SPP, "TMR_SPP"},
{TMR_TIMESIG, "TMR_TIMESIG"},
{-1, NULL},
};
midi_cmdtab cmdtab_seqcv[] = {
{MIDI_NOTEOFF, "MIDI_NOTEOFF"},
{MIDI_NOTEON, "MIDI_NOTEON"},
{MIDI_KEY_PRESSURE, "MIDI_KEY_PRESSURE"},
{-1, NULL},
};
midi_cmdtab cmdtab_seqccmn[] = {
{MIDI_CTL_CHANGE, "MIDI_CTL_CHANGE"},
{MIDI_PGM_CHANGE, "MIDI_PGM_CHANGE"},
{MIDI_CHN_PRESSURE, "MIDI_CHN_PRESSURE"},
{MIDI_PITCH_BEND, "MIDI_PITCH_BEND"},
{MIDI_SYSTEM_PREFIX, "MIDI_SYSTEM_PREFIX"},
{-1, NULL},
};
#ifndef KOBJMETHOD_END
#define KOBJMETHOD_END { NULL, NULL }
#endif
static kobj_method_t seq_methods[] = {
KOBJMETHOD_END
};
DEFINE_CLASS(sequencer, seq_methods, 0);
static int seq_convertold(u_char *event, u_char *out);
static void seq_reset(struct seq_softc *scp);
static int seq_sync(struct seq_softc *scp);
static int seq_processevent(struct seq_softc *scp, u_char *event);
static int seq_timing(struct seq_softc *scp, u_char *event);
static int seq_local(struct seq_softc *scp, u_char *event);
static int seq_chnvoice(struct seq_softc *scp, kobj_t md, u_char *event);
static int seq_chncommon(struct seq_softc *scp, kobj_t md, u_char *event);
static int seq_sysex(struct seq_softc *scp, kobj_t md, u_char *event);
static int seq_fetch_mid(struct seq_softc *scp, int unit, kobj_t *md);
void seq_copytoinput(struct seq_softc *scp, u_char *event, int len);
int seq_modevent(module_t mod, int type, void *data);
struct seq_softc *seqs[10];
static struct lock seqinfo_lock;
static u_long nseq = 0;
static void timer_start(struct seq_softc *t);
static void timer_stop(struct seq_softc *t);
static void timer_setvals(struct seq_softc *t, int tempo, int timerbase);
static void timer_wait(struct seq_softc *t, int ticks, int wait_abs);
static int timer_now(struct seq_softc *t);
static void
timer_start(struct seq_softc *t)
{
t->timerrun = 1;
getmicrotime(&t->timersub);
}
static void
timer_continue(struct seq_softc *t)
{
struct timeval now;
if (t->timerrun == 1)
return;
t->timerrun = 1;
getmicrotime(&now);
timevalsub(&now, &t->timerstop);
timevaladd(&t->timersub, &now);
}
static void
timer_stop(struct seq_softc *t)
{
t->timerrun = 0;
getmicrotime(&t->timerstop);
}
static void
timer_setvals(struct seq_softc *t, int tempo, int timerbase)
{
t->tempo = tempo;
t->timerbase = timerbase;
}
static void
timer_wait(struct seq_softc *t, int ticks, int wait_abs)
{
struct timeval now, when;
int ret;
unsigned long long i;
while (t->timerrun == 0) {
SEQ_DEBUG(2, kprintf("Timer wait when timer isn't running\n"));
cv_wait(&t->reset_cv, &t->seq_lock);
if (t->playing == 0)
return;
}
i = ticks * 60ull * 1000000ull / (t->tempo * t->timerbase);
when.tv_sec = i / 1000000;
when.tv_usec = i % 1000000;
#if 0
kprintf("timer_wait tempo %d timerbase %d ticks %d abs %d u_sec %llu\n",
t->tempo, t->timerbase, ticks, wait_abs, i);
#endif
if (wait_abs != 0) {
getmicrotime(&now);
timevalsub(&now, &t->timersub);
timevalsub(&when, &now);
}
if (when.tv_sec < 0 || when.tv_usec < 0) {
SEQ_DEBUG(3,
kprintf("seq_timer error negative time %lds.%06lds\n",
(long)when.tv_sec, (long)when.tv_usec));
return;
}
i = when.tv_sec * 1000000ull;
i += when.tv_usec;
i *= hz;
i /= 1000000ull;
#if 0
kprintf("seq_timer usec %llu ticks %llu\n",
when.tv_sec * 1000000ull + when.tv_usec, i);
#endif
t->waiting = 1;
ret = cv_timedwait(&t->reset_cv, &t->seq_lock, i + 1);
t->waiting = 0;
if (ret != EWOULDBLOCK)
SEQ_DEBUG(3, kprintf("seq_timer didn't timeout\n"));
}
static int
timer_now(struct seq_softc *t)
{
struct timeval now;
unsigned long long i;
int ret;
if (t->timerrun == 0)
now = t->timerstop;
else
getmicrotime(&now);
timevalsub(&now, &t->timersub);
i = now.tv_sec * 1000000ull;
i += now.tv_usec;
i *= t->timerbase;
i /= 1000000ull;
ret = i;
return ret;
}
static void
seq_eventthread(void *arg)
{
struct seq_softc *scp = arg;
char event[EV_SZ];
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
SEQ_DEBUG(2, kprintf("seq_eventthread started\n"));
while (scp->done == 0) {
restart:
while (scp->playing == 0) {
cv_wait(&scp->state_cv, &scp->seq_lock);
if (scp->done)
goto done;
}
while (MIDIQ_EMPTY(scp->out_q)) {
cv_broadcast(&scp->empty_cv);
cv_wait(&scp->out_cv, &scp->seq_lock);
if (scp->playing == 0)
goto restart;
if (scp->done)
goto done;
}
MIDIQ_DEQ(scp->out_q, event, EV_SZ);
if (MIDIQ_AVAIL(scp->out_q) < scp->out_water) {
cv_broadcast(&scp->out_cv);
KNOTE(&scp->out_kq.ki_note, 0);
}
seq_processevent(scp, event);
}
done:
cv_broadcast(&scp->th_cv);
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(2, kprintf("seq_eventthread finished\n"));
kthread_exit();
}
static int
seq_processevent(struct seq_softc *scp, u_char *event)
{
int ret;
kobj_t m;
ret = 0;
if (event[0] == EV_SEQ_LOCAL)
ret = seq_local(scp, event);
else if (event[0] == EV_TIMING)
ret = seq_timing(scp, event);
else if (event[0] != EV_CHN_VOICE &&
event[0] != EV_CHN_COMMON &&
event[0] != EV_SYSEX &&
event[0] != SEQ_MIDIPUTC) {
ret = 1;
SEQ_DEBUG(2, kprintf("seq_processevent not known %d\n",
event[0]));
} else if (seq_fetch_mid(scp, event[1], &m) != 0) {
ret = 1;
SEQ_DEBUG(2, kprintf("seq_processevent midi unit not found %d\n",
event[1]));
} else
switch (event[0]) {
case EV_CHN_VOICE:
ret = seq_chnvoice(scp, m, event);
break;
case EV_CHN_COMMON:
ret = seq_chncommon(scp, m, event);
break;
case EV_SYSEX:
ret = seq_sysex(scp, m, event);
break;
case SEQ_MIDIPUTC:
lockmgr(&scp->seq_lock, LK_RELEASE);
ret = SYNTH_WRITERAW(m, &event[2], 1);
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
break;
}
return ret;
}
static int
seq_addunit(void)
{
struct seq_softc *scp;
int ret;
u_char *buf;
ret = ENOMEM;
scp = kmalloc(sizeof(*scp), M_DEVBUF, M_WAITOK | M_ZERO);
kobj_init((kobj_t)scp, &sequencer_class);
buf = kmalloc(sizeof(*buf) * EV_SZ * 1024, M_TEMP, M_WAITOK | M_ZERO);
MIDIQ_INIT(scp->in_q, buf, EV_SZ * 1024);
buf = kmalloc(sizeof(*buf) * EV_SZ * 1024, M_TEMP, M_WAITOK | M_ZERO);
MIDIQ_INIT(scp->out_q, buf, EV_SZ * 1024);
ret = EINVAL;
scp->midis = kmalloc(sizeof(kobj_t) * 32, M_TEMP, M_WAITOK | M_ZERO);
scp->midi_flags = kmalloc(sizeof(*scp->midi_flags) * 32, M_TEMP,
M_WAITOK | M_ZERO);
scp->flags = 0;
lockinit(&scp->seq_lock, "seqflq", 0, LK_CANRECURSE);
cv_init(&scp->state_cv, "seqstate");
cv_init(&scp->empty_cv, "seqempty");
cv_init(&scp->reset_cv, "seqtimer");
cv_init(&scp->out_cv, "seqqout");
cv_init(&scp->in_cv, "seqqin");
cv_init(&scp->th_cv, "seqstart");
scp->mapper = midimapper_addseq(scp, &scp->unit, &scp->mapper_cookie);
if (scp->mapper == NULL)
goto err;
scp->seqdev = make_dev(&seq_ops,
MIDIMKMINOR(scp->unit, SND_DEV_SEQ, 0), UID_ROOT,
GID_WHEEL, 0666, "sequencer%d", scp->unit);
scp->musicdev = make_dev(&seq_ops,
MIDIMKMINOR(scp->unit, SND_DEV_MUSIC, 0), UID_ROOT,
GID_WHEEL, 0666, "music%d", scp->unit);
if (scp->seqdev == NULL || scp->musicdev == NULL)
goto err;
ret =
kthread_create
(seq_eventthread, scp, NULL,
"sequencer %02d", scp->unit);
if (ret)
goto err;
scp->seqdev->si_drv1 = scp->musicdev->si_drv1 = scp;
SEQ_DEBUG(2, kprintf("sequencer %d created scp %p\n", scp->unit, scp));
ret = 0;
lockmgr(&seqinfo_lock, LK_EXCLUSIVE);
seqs[nseq++] = scp;
lockmgr(&seqinfo_lock, LK_RELEASE);
goto ok;
err:
if (scp != NULL) {
if (scp->seqdev != NULL)
destroy_dev(scp->seqdev);
if (scp->musicdev != NULL)
destroy_dev(scp->musicdev);
if (scp->midis != NULL)
kfree(scp->midis, M_TEMP);
if (scp->midi_flags != NULL)
kfree(scp->midi_flags, M_TEMP);
if (scp->out_q.b)
kfree(scp->out_q.b, M_TEMP);
if (scp->in_q.b)
kfree(scp->in_q.b, M_TEMP);
kfree(scp, M_DEVBUF);
}
ok:
return ret;
}
static int
seq_delunit(int unit)
{
struct seq_softc *scp = seqs[unit];
int i;
SEQ_DEBUG(1, kprintf("seq_delunit: 1 \n"));
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
scp->playing = 0;
scp->done = 1;
cv_broadcast(&scp->out_cv);
cv_broadcast(&scp->state_cv);
cv_broadcast(&scp->reset_cv);
SEQ_DEBUG(1, kprintf("seq_delunit: 2 \n"));
cv_wait(&scp->th_cv, &scp->seq_lock);
SEQ_DEBUG(1, kprintf("seq_delunit: 3.0 \n"));
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(1, kprintf("seq_delunit: 3.1 \n"));
cv_destroy(&scp->state_cv);
SEQ_DEBUG(1, kprintf("seq_delunit: 4 \n"));
cv_destroy(&scp->empty_cv);
SEQ_DEBUG(1, kprintf("seq_delunit: 5 \n"));
cv_destroy(&scp->reset_cv);
SEQ_DEBUG(1, kprintf("seq_delunit: 6 \n"));
cv_destroy(&scp->out_cv);
SEQ_DEBUG(1, kprintf("seq_delunit: 7 \n"));
cv_destroy(&scp->in_cv);
SEQ_DEBUG(1, kprintf("seq_delunit: 8 \n"));
cv_destroy(&scp->th_cv);
SEQ_DEBUG(1, kprintf("seq_delunit: 10 \n"));
if (scp->seqdev)
destroy_dev(scp->seqdev);
SEQ_DEBUG(1, kprintf("seq_delunit: 11 \n"));
if (scp->musicdev)
destroy_dev(scp->musicdev);
SEQ_DEBUG(1, kprintf("seq_delunit: 12 \n"));
scp->seqdev = scp->musicdev = NULL;
if (scp->midis != NULL)
kfree(scp->midis, M_TEMP);
SEQ_DEBUG(1, kprintf("seq_delunit: 13 \n"));
if (scp->midi_flags != NULL)
kfree(scp->midi_flags, M_TEMP);
SEQ_DEBUG(1, kprintf("seq_delunit: 14 \n"));
kfree(scp->out_q.b, M_TEMP);
SEQ_DEBUG(1, kprintf("seq_delunit: 15 \n"));
kfree(scp->in_q.b, M_TEMP);
SEQ_DEBUG(1, kprintf("seq_delunit: 16 \n"));
lockuninit(&scp->seq_lock);
SEQ_DEBUG(1, kprintf("seq_delunit: 17 \n"));
kfree(scp, M_DEVBUF);
lockmgr(&seqinfo_lock, LK_EXCLUSIVE);
for (i = unit; i < (nseq - 1); i++)
seqs[i] = seqs[i + 1];
nseq--;
lockmgr(&seqinfo_lock, LK_RELEASE);
return 0;
}
int
seq_modevent(module_t mod, int type, void *data)
{
int retval, r;
retval = 0;
switch (type) {
case MOD_LOAD:
lockinit(&seqinfo_lock, "seqmod", 0, LK_CANRECURSE);
retval = seq_addunit();
break;
case MOD_UNLOAD:
while (nseq) {
r = seq_delunit(nseq - 1);
if (r) {
retval = r;
break;
}
}
if (nseq == 0) {
retval = 0;
lockuninit(&seqinfo_lock);
}
break;
default:
break;
}
return retval;
}
static int
seq_fetch_mid(struct seq_softc *scp, int unit, kobj_t *md)
{
if (unit > scp->midi_number || unit < 0)
return EINVAL;
*md = scp->midis[unit];
return 0;
}
int
seq_open(struct dev_open_args *ap)
{
cdev_t i_dev = ap->a_head.a_dev;
int flags = ap->a_oflags;
struct seq_softc *scp = i_dev->si_drv1;
int i;
if (scp == NULL)
return ENXIO;
SEQ_DEBUG(3, kprintf("seq_open: scp %p unit %d, flags 0x%x.\n",
scp, scp->unit, flags));
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
if (scp->busy) {
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(2, kprintf("seq_open: unit %d is busy.\n", scp->unit));
return EBUSY;
}
scp->fflags = flags;
scp->music = MIDIDEV(i_dev) == SND_DEV_MUSIC;
scp->midi_number = 0;
scp->maxunits = midimapper_open(scp->mapper, &scp->mapper_cookie);
if (scp->maxunits == 0)
SEQ_DEBUG(2, kprintf("seq_open: no midi devices\n"));
for (i = 0; i < scp->maxunits; i++) {
scp->midis[scp->midi_number] =
midimapper_fetch_synth(scp->mapper, scp->mapper_cookie, i);
if (scp->midis[scp->midi_number]) {
if (SYNTH_OPEN(scp->midis[scp->midi_number], scp,
scp->fflags) != 0)
scp->midis[scp->midi_number] = NULL;
else {
scp->midi_flags[scp->midi_number] =
SYNTH_QUERY(scp->midis[scp->midi_number]);
scp->midi_number++;
}
}
}
timer_setvals(scp, 60, 100);
timer_start(scp);
timer_stop(scp);
scp->out_water = MIDIQ_SIZE(scp->out_q) / 2;
scp->busy = 1;
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(2, kprintf("seq_open: opened, mode %s.\n",
scp->music ? "music" : "sequencer"));
SEQ_DEBUG(2,
kprintf("Sequencer %d %p opened maxunits %d midi_number %d:\n",
scp->unit, scp, scp->maxunits, scp->midi_number));
for (i = 0; i < scp->midi_number; i++)
SEQ_DEBUG(3, kprintf(" midi %d %p\n", i, scp->midis[i]));
return 0;
}
int
seq_close(struct dev_close_args *ap)
{
cdev_t i_dev = ap->a_head.a_dev;
int i;
struct seq_softc *scp = i_dev->si_drv1;
int ret;
if (scp == NULL)
return ENXIO;
SEQ_DEBUG(2, kprintf("seq_close: unit %d.\n", scp->unit));
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
ret = ENXIO;
if (scp->busy == 0)
goto err;
seq_reset(scp);
seq_sync(scp);
for (i = 0; i < scp->midi_number; i++)
if (scp->midis[i])
SYNTH_CLOSE(scp->midis[i]);
midimapper_close(scp->mapper, scp->mapper_cookie);
timer_stop(scp);
scp->busy = 0;
ret = 0;
err:
SEQ_DEBUG(3, kprintf("seq_close: closed ret = %d.\n", ret));
lockmgr(&scp->seq_lock, LK_RELEASE);
return ret;
}
int
seq_read(struct dev_read_args *ap)
{
cdev_t i_dev = ap->a_head.a_dev;
struct uio *uio = ap->a_uio;
int ioflag = ap->a_ioflag;
int retval, used;
struct seq_softc *scp = i_dev->si_drv1;
#define SEQ_RSIZE 32
u_char buf[SEQ_RSIZE];
if (scp == NULL)
return ENXIO;
SEQ_DEBUG(7, kprintf("seq_read: unit %d, resid %zd.\n",
scp->unit, uio->uio_resid));
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
if ((scp->fflags & FREAD) == 0) {
SEQ_DEBUG(2, kprintf("seq_read: unit %d is not for reading.\n",
scp->unit));
retval = EIO;
goto err1;
}
while (uio->uio_resid > 0) {
while (MIDIQ_EMPTY(scp->in_q)) {
retval = EWOULDBLOCK;
if (scp->fflags & O_NONBLOCK)
goto err1;
if (ioflag & O_NONBLOCK)
goto err1;
retval = cv_wait_sig(&scp->in_cv, &scp->seq_lock);
if (retval == EINTR)
goto err1;
}
used = MIN(MIDIQ_LEN(scp->in_q), uio->uio_resid);
used = MIN(used, SEQ_RSIZE);
SEQ_DEBUG(8, kprintf("midiread: uiomove cc=%d\n", used));
MIDIQ_DEQ(scp->in_q, buf, used);
retval = uiomove(buf, used, uio);
if (retval)
goto err1;
}
retval = 0;
err1:
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(6, kprintf("seq_read: ret %d, resid %zd.\n",
retval, uio->uio_resid));
return retval;
}
int
seq_write(struct dev_write_args *ap)
{
cdev_t i_dev = ap->a_head.a_dev;
struct uio *uio = ap->a_uio;
int ioflag = ap->a_ioflag;
u_char event[EV_SZ], newevent[EV_SZ], ev_code;
struct seq_softc *scp = i_dev->si_drv1;
int retval;
int used;
SEQ_DEBUG(7, kprintf("seq_write: unit %d, resid %zd.\n",
scp->unit, uio->uio_resid));
if (scp == NULL)
return ENXIO;
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
if ((scp->fflags & FWRITE) == 0) {
SEQ_DEBUG(2, kprintf("seq_write: unit %d is not for writing.\n",
scp->unit));
retval = EIO;
goto err0;
}
while (uio->uio_resid > 0) {
while (MIDIQ_AVAIL(scp->out_q) == 0) {
retval = EWOULDBLOCK;
if (scp->fflags & O_NONBLOCK)
goto err0;
if (ioflag & O_NONBLOCK)
goto err0;
SEQ_DEBUG(8, kprintf("seq_write cvwait\n"));
scp->playing = 1;
cv_broadcast(&scp->out_cv);
cv_broadcast(&scp->state_cv);
retval = cv_wait_sig(&scp->out_cv, &scp->seq_lock);
if (retval == EINTR)
goto err0;
#if 0
if (scp != i_dev->si_drv1)
retval = ENXIO;
#endif
}
used = MIN(uio->uio_resid, 4);
SEQ_DEBUG(8, kprintf("seqout: resid %zd len %jd avail %jd\n",
uio->uio_resid, (intmax_t)MIDIQ_LEN(scp->out_q),
(intmax_t)MIDIQ_AVAIL(scp->out_q)));
if (used != 4) {
retval = ENXIO;
goto err0;
}
retval = uiomove(event, used, uio);
if (retval)
goto err0;
ev_code = event[0];
SEQ_DEBUG(8, kprintf("seq_write: unit %d, event %s.\n",
scp->unit, midi_cmdname(ev_code, cmdtab_seqevent)));
if (ev_code == SEQ_FULLSIZE) {
#if 0
midiunit = *(u_short *)&event[2];
lockmgr(&sd->seq_lock, LK_EXCLUSIVE);
ret = lookup_mididev(scp, midiunit, LOOKUP_OPEN, &md);
lockmgr(&sd->seq_lock, LK_RELEASE);
if (ret != 0)
return (ret);
SEQ_DEBUG(kprintf("seq_write: loading a patch to the unit %d.\n", midiunit));
ret = md->synth.loadpatch(md, *(short *)&event[0], buf,
p + 4, count, 0);
return (ret);
#else
SEQ_DEBUG(2,
kprintf("seq_write: SEQ_FULLSIZE flusing buffer.\n"));
while (uio->uio_resid > 0) {
retval = uiomove(event, EV_SZ, uio);
if (retval)
goto err0;
}
retval = 0;
goto err0;
#endif
}
retval = EINVAL;
if (ev_code >= 128) {
if (scp->music && ev_code == SEQ_EXTENDED) {
SEQ_DEBUG(2, kprintf("seq_write: invalid level two event %x.\n", ev_code));
goto err0;
}
if (uiomove((caddr_t)&event[4], 4, uio)) {
SEQ_DEBUG(2,
kprintf("seq_write: user memory mangled?\n"));
goto err0;
}
} else {
if (scp->music) {
SEQ_DEBUG(2, kprintf("seq_write: four byte event in music mode.\n"));
goto err0;
}
}
if (ev_code == SEQ_MIDIPUTC) {
}
if (scp->music) {
#ifdef not_ever_ever
if (event[0] == EV_TIMING &&
(event[1] == TMR_START || event[1] == TMR_STOP)) {
seq_processevent(scp, event);
} else
MIDIQ_ENQ(scp->out_q, event, EV_SZ);
#else
MIDIQ_ENQ(scp->out_q, event, EV_SZ);
#endif
} else {
if (seq_convertold(event, newevent) > 0)
MIDIQ_ENQ(scp->out_q, newevent, EV_SZ);
#if 0
else
goto err0;
#endif
}
}
scp->playing = 1;
cv_broadcast(&scp->state_cv);
cv_broadcast(&scp->out_cv);
retval = 0;
err0:
SEQ_DEBUG(6,
kprintf("seq_write done: leftover buffer length %zd retval %d\n",
uio->uio_resid, retval));
lockmgr(&scp->seq_lock, LK_RELEASE);
return retval;
}
int
seq_ioctl(struct dev_ioctl_args *ap)
{
cdev_t i_dev = ap->a_head.a_dev;
u_long cmd = ap->a_cmd;
caddr_t arg = ap->a_data;
int mode = ap->a_fflag;
int midiunit, ret, tmp;
struct seq_softc *scp = i_dev->si_drv1;
struct synth_info *synthinfo;
struct midi_info *midiinfo;
u_char event[EV_SZ];
u_char newevent[EV_SZ];
kobj_t md;
if (scp == NULL)
return ENXIO;
SEQ_DEBUG(6, kprintf("seq_ioctl: unit %d, cmd %s.\n",
scp->unit, midi_cmdname(cmd, cmdtab_seqioctl)));
ret = 0;
switch (cmd) {
case SNDCTL_SEQ_GETTIME:
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
*(int *)arg = timer_now(scp);
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(6, kprintf("seq_ioctl: gettime %d.\n", *(int *)arg));
ret = 0;
break;
case SNDCTL_TMR_METRONOME:
case SNDCTL_TMR_SOURCE:
ret = 0;
break;
case SNDCTL_TMR_TEMPO:
event[1] = TMR_TEMPO;
event[4] = *(int *)arg & 0xFF;
event[5] = (*(int *)arg >> 8) & 0xFF;
event[6] = (*(int *)arg >> 16) & 0xFF;
event[7] = (*(int *)arg >> 24) & 0xFF;
goto timerevent;
case SNDCTL_TMR_TIMEBASE:
event[1] = TMR_TIMERBASE;
event[4] = *(int *)arg & 0xFF;
event[5] = (*(int *)arg >> 8) & 0xFF;
event[6] = (*(int *)arg >> 16) & 0xFF;
event[7] = (*(int *)arg >> 24) & 0xFF;
goto timerevent;
case SNDCTL_TMR_START:
event[1] = TMR_START;
goto timerevent;
case SNDCTL_TMR_STOP:
event[1] = TMR_STOP;
goto timerevent;
case SNDCTL_TMR_CONTINUE:
event[1] = TMR_CONTINUE;
timerevent:
event[0] = EV_TIMING;
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
if (!scp->music) {
ret = EINVAL;
lockmgr(&scp->seq_lock, LK_RELEASE);
break;
}
seq_processevent(scp, event);
lockmgr(&scp->seq_lock, LK_RELEASE);
break;
case SNDCTL_TMR_SELECT:
SEQ_DEBUG(2,
kprintf("seq_ioctl: SNDCTL_TMR_SELECT not supported\n"));
ret = EINVAL;
break;
case SNDCTL_SEQ_SYNC:
if (mode == O_RDONLY) {
ret = 0;
break;
}
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
ret = seq_sync(scp);
lockmgr(&scp->seq_lock, LK_RELEASE);
break;
case SNDCTL_SEQ_PANIC:
case SNDCTL_SEQ_RESET:
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
seq_reset(scp);
lockmgr(&scp->seq_lock, LK_RELEASE);
ret = 0;
break;
case SNDCTL_SEQ_TESTMIDI:
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
lockmgr(&scp->seq_lock, LK_RELEASE);
break;
#if 0
case SNDCTL_SEQ_GETINCOUNT:
if (mode == O_WRONLY)
*(int *)arg = 0;
else {
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
*(int *)arg = scp->in_q.rl;
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(kprintf("seq_ioctl: incount %d.\n",
*(int *)arg));
}
ret = 0;
break;
case SNDCTL_SEQ_GETOUTCOUNT:
if (mode == O_RDONLY)
*(int *)arg = 0;
else {
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
*(int *)arg = scp->out_q.fl;
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(kprintf("seq_ioctl: outcount %d.\n",
*(int *)arg));
}
ret = 0;
break;
#endif
case SNDCTL_SEQ_CTRLRATE:
if (*(int *)arg != 0) {
ret = EINVAL;
break;
}
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
*(int *)arg = scp->timerbase;
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(3, kprintf("seq_ioctl: ctrlrate %d.\n", *(int *)arg));
ret = 0;
break;
#if 0
case SNDCTL_SEQ_RESETSAMPLES:
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
ret = lookup_mididev(scp, *(int *)arg, LOOKUP_OPEN, &md);
lockmgr(&scp->seq_lock, LK_RELEASE);
if (ret != 0)
break;
ret = midi_ioctl(MIDIMKDEV(major(i_dev), *(int *)arg,
SND_DEV_MIDIN), cmd, arg, mode, td);
break;
#endif
case SNDCTL_SEQ_NRSYNTHS:
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
*(int *)arg = scp->midi_number;
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(3, kprintf("seq_ioctl: synths %d.\n", *(int *)arg));
ret = 0;
break;
case SNDCTL_SEQ_NRMIDIS:
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
if (scp->music)
*(int *)arg = 0;
else {
*(int *)arg = scp->midi_number;
}
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(3, kprintf("seq_ioctl: midis %d.\n", *(int *)arg));
ret = 0;
break;
#if 0
case SNDCTL_SYNTH_MEMAVL:
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
ret = lookup_mididev(scp, *(int *)arg, LOOKUP_OPEN, &md);
lockmgr(&scp->seq_lock, LK_RELEASE);
if (ret != 0)
break;
ret = midi_ioctl(MIDIMKDEV(major(i_dev), *(int *)arg,
SND_DEV_MIDIN), cmd, arg, mode, td);
break;
#endif
case SNDCTL_SEQ_OUTOFBAND:
for (ret = 0; ret < EV_SZ; ret++)
event[ret] = (u_char)arg[0];
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
if (scp->music)
ret = seq_processevent(scp, event);
else {
if (seq_convertold(event, newevent) > 0)
ret = seq_processevent(scp, newevent);
else
ret = EINVAL;
}
lockmgr(&scp->seq_lock, LK_RELEASE);
break;
case SNDCTL_SYNTH_INFO:
synthinfo = (struct synth_info *)arg;
midiunit = synthinfo->device;
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
if (seq_fetch_mid(scp, midiunit, &md) == 0) {
bzero(synthinfo, sizeof(*synthinfo));
synthinfo->name[0] = 'f';
synthinfo->name[1] = 'a';
synthinfo->name[2] = 'k';
synthinfo->name[3] = 'e';
synthinfo->name[4] = 's';
synthinfo->name[5] = 'y';
synthinfo->name[6] = 'n';
synthinfo->name[7] = 't';
synthinfo->name[8] = 'h';
synthinfo->device = midiunit;
synthinfo->synth_type = SYNTH_TYPE_MIDI;
synthinfo->capabilities = scp->midi_flags[midiunit];
ret = 0;
} else
ret = EINVAL;
lockmgr(&scp->seq_lock, LK_RELEASE);
break;
case SNDCTL_MIDI_INFO:
midiinfo = (struct midi_info *)arg;
midiunit = midiinfo->device;
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
if (seq_fetch_mid(scp, midiunit, &md) == 0) {
bzero(midiinfo, sizeof(*midiinfo));
midiinfo->name[0] = 'f';
midiinfo->name[1] = 'a';
midiinfo->name[2] = 'k';
midiinfo->name[3] = 'e';
midiinfo->name[4] = 'm';
midiinfo->name[5] = 'i';
midiinfo->name[6] = 'd';
midiinfo->name[7] = 'i';
midiinfo->device = midiunit;
midiinfo->capabilities = scp->midi_flags[midiunit];
midiinfo->dev_type = 0x01;
ret = 0;
} else
ret = EINVAL;
lockmgr(&scp->seq_lock, LK_RELEASE);
break;
case SNDCTL_SEQ_THRESHOLD:
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
RANGE(*(int *)arg, 1, MIDIQ_SIZE(scp->out_q) - 1);
scp->out_water = *(int *)arg;
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(3, kprintf("seq_ioctl: water %d.\n", *(int *)arg));
ret = 0;
break;
case SNDCTL_MIDI_PRETIME:
tmp = *(int *)arg;
if (tmp < 0)
tmp = 0;
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
scp->pre_event_timeout = (hz * tmp) / 10;
*(int *)arg = scp->pre_event_timeout;
lockmgr(&scp->seq_lock, LK_RELEASE);
SEQ_DEBUG(3, kprintf("seq_ioctl: pretime %d.\n", *(int *)arg));
ret = 0;
break;
case SNDCTL_FM_4OP_ENABLE:
case SNDCTL_PMGR_IFACE:
case SNDCTL_PMGR_ACCESS:
default:
SEQ_DEBUG(2,
kprintf("seq_ioctl: unsupported IOCTL %ld.\n", cmd));
ret = EINVAL;
break;
}
return ret;
}
int
seq_kqfilter(struct dev_kqfilter_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct knote *kn = ap->a_kn;
struct seq_softc *scp;
struct klist *klist;
ap->a_result = 0;
scp = dev->si_drv1;
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &seq_read_filterops;
kn->kn_hook = (caddr_t)scp;
klist = &scp->in_kq.ki_note;
break;
case EVFILT_WRITE:
kn->kn_fop = &seq_write_filterops;
kn->kn_hook = (caddr_t)scp;
klist = &scp->out_kq.ki_note;
break;
default:
ap->a_result = EOPNOTSUPP;
return (0);
}
knote_insert(klist, kn);
return(0);
}
static void
seq_filter_detach(struct knote *kn)
{
struct seq_softc *scp = (struct seq_softc *)kn->kn_hook;
struct klist *in_klist = &scp->in_kq.ki_note;
struct klist *out_klist = &scp->out_kq.ki_note;
knote_remove(in_klist, kn);
knote_remove(out_klist, kn);
}
static int
seq_filter_read(struct knote *kn, long hint)
{
struct seq_softc *scp = (struct seq_softc *)kn->kn_hook;
int ready = 0;
int lim;
SEQ_DEBUG(3, kprintf("seq_filter_read: unit %d.\n", scp->unit));
SEQ_DEBUG(1, kprintf("seq_filter_read: unit %d.\n", scp->unit));
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
lim = 1;
if (MIDIQ_LEN(scp->in_q) >= lim) {
ready = 1;
}
lockmgr(&scp->seq_lock, LK_RELEASE);
return (ready);
}
static int
seq_filter_write(struct knote *kn, long hint)
{
struct seq_softc *scp = (struct seq_softc *)kn->kn_hook;
int ready = 0;
int lim;
SEQ_DEBUG(3, kprintf("seq_filter_write: unit %d.\n", scp->unit));
SEQ_DEBUG(1, kprintf("seq_filter_write: unit %d.\n", scp->unit));
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
scp->playing = 1;
cv_broadcast(&scp->state_cv);
cv_broadcast(&scp->out_cv);
lim = scp->out_water;
if (MIDIQ_AVAIL(scp->out_q) >= lim) {
ready = 1;
}
lockmgr(&scp->seq_lock, LK_RELEASE);
return (ready);
}
#if 0
static void
sein_qtr(void *p, void *md)
{
struct seq_softc *scp;
scp = (struct seq_softc *)p;
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
if (scp->queueout_pending)
seq_callback(scp, SEQ_CB_START | SEQ_CB_WR);
if ((scp->flags & SEQ_F_READING) != 0)
seq_midiinput(scp, md);
lockmgr(&scp->seq_lock, LK_RELEASE);
}
#endif
static int
seq_convertold(u_char *event, u_char *out)
{
int used;
u_char dev, chn, note, vel;
out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] =
out[7] = 0;
dev = 0;
chn = event[1];
note = event[2];
vel = event[3];
used = 0;
restart:
switch (event[0]) {
case EV_TIMING:
case EV_CHN_VOICE:
case EV_CHN_COMMON:
case EV_SYSEX:
case EV_SEQ_LOCAL:
out[0] = event[0];
out[1] = event[1];
out[2] = event[2];
out[3] = event[3];
out[4] = event[4];
out[5] = event[5];
out[6] = event[6];
out[7] = event[7];
used += 8;
break;
case SEQ_NOTEOFF:
out[0] = EV_CHN_VOICE;
out[1] = dev;
out[2] = MIDI_NOTEOFF;
out[3] = chn;
out[4] = note;
out[5] = 255;
used += 4;
break;
case SEQ_NOTEON:
out[0] = EV_CHN_VOICE;
out[1] = dev;
out[2] = MIDI_NOTEON;
out[3] = chn;
out[4] = note;
out[5] = vel;
used += 4;
break;
case SEQ_PGMCHANGE:
out[0] = EV_CHN_COMMON;
out[1] = dev;
out[2] = MIDI_PGM_CHANGE;
out[3] = chn;
out[4] = note;
out[5] = vel;
used += 4;
break;
case SEQ_MIDIPUTC:
SEQ_DEBUG(4,
kprintf("seq_playevent: put data 0x%02x, unit %d.\n",
event[1], event[2]));
out[0] = SEQ_MIDIPUTC;
out[1] = dev;
out[2] = chn;
used += 4;
break;
#ifdef notyet
case SEQ_ECHO:
if (seq_copytoinput(scp, event, 4) == EAGAIN) {
ret = QUEUEFULL;
break;
}
ret = MORE;
break;
#endif
case SEQ_EXTENDED:
switch (event[1]) {
case SEQ_NOTEOFF:
case SEQ_NOTEON:
case SEQ_PGMCHANGE:
event++;
used = 4;
goto restart;
break;
case SEQ_AFTERTOUCH:
case SEQ_BALANCE:
case SEQ_CONTROLLER:
case SEQ_VOLMODE:
default:
SEQ_DEBUG(2,
kprintf("seq_convertold: SEQ_EXTENDED type %d"
"not handled\n", event[1]));
break;
}
break;
case SEQ_WAIT:
out[0] = EV_TIMING;
out[1] = TMR_WAIT_REL;
out[4] = event[2];
out[5] = event[3];
out[6] = event[4];
SEQ_DEBUG(5, kprintf("SEQ_WAIT %d",
event[2] + (event[3] << 8) + (event[4] << 24)));
used += 4;
break;
case SEQ_ECHO:
case SEQ_SYNCTIMER:
case SEQ_PRIVATE:
default:
SEQ_DEBUG(2,
kprintf("seq_convertold: event type %d not handled %d %d %d\n",
event[0], event[1], event[2], event[3]));
break;
}
return used;
}
void
seq_copytoinput(struct seq_softc *scp, u_char *event, int len)
{
KKASSERT(lockowned(&scp->seq_lock));
if (MIDIQ_AVAIL(scp->in_q) < len) {
SEQ_DEBUG(2, kprintf("seq_copytoinput: queue full\n"));
} else {
MIDIQ_ENQ(scp->in_q, event, len);
KNOTE(&scp->in_kq.ki_note, 0);
cv_broadcast(&scp->in_cv);
}
}
static int
seq_chnvoice(struct seq_softc *scp, kobj_t md, u_char *event)
{
int ret, voice;
u_char cmd, chn, note, parm;
ret = 0;
cmd = event[2];
chn = event[3];
note = event[4];
parm = event[5];
KKASSERT(lockowned(&scp->seq_lock));
SEQ_DEBUG(5, kprintf("seq_chnvoice: unit %d, dev %d, cmd %s,"
" chn %d, note %d, parm %d.\n", scp->unit, event[1],
midi_cmdname(cmd, cmdtab_seqcv), chn, note, parm));
voice = SYNTH_ALLOC(md, chn, note);
lockmgr(&scp->seq_lock, LK_RELEASE);
switch (cmd) {
case MIDI_NOTEON:
if (note < 128 || note == 255) {
#if 0
if (scp->music && chn == 9) {
note = 60;
}
#endif
if (scp->music) {
}
SYNTH_STARTNOTE(md, voice, note, parm);
}
break;
case MIDI_NOTEOFF:
SYNTH_KILLNOTE(md, voice, note, parm);
break;
case MIDI_KEY_PRESSURE:
SYNTH_AFTERTOUCH(md, voice, parm);
break;
default:
ret = 1;
SEQ_DEBUG(2, kprintf("seq_chnvoice event type %d not handled\n",
event[1]));
break;
}
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
return ret;
}
static int
seq_chncommon(struct seq_softc *scp, kobj_t md, u_char *event)
{
int ret;
u_short w14;
u_char cmd, chn, p1;
ret = 0;
cmd = event[2];
chn = event[3];
p1 = event[4];
w14 = *(u_short *)&event[6];
SEQ_DEBUG(5, kprintf("seq_chncommon: unit %d, dev %d, cmd %s, chn %d,"
" p1 %d, w14 %d.\n", scp->unit, event[1],
midi_cmdname(cmd, cmdtab_seqccmn), chn, p1, w14));
lockmgr(&scp->seq_lock, LK_RELEASE);
switch (cmd) {
case MIDI_PGM_CHANGE:
SEQ_DEBUG(4, kprintf("seq_chncommon pgmchn chn %d pg %d\n",
chn, p1));
SYNTH_SETINSTR(md, chn, p1);
break;
case MIDI_CTL_CHANGE:
SEQ_DEBUG(4, kprintf("seq_chncommon ctlch chn %d pg %d %d\n",
chn, p1, w14));
SYNTH_CONTROLLER(md, chn, p1, w14);
break;
case MIDI_PITCH_BEND:
if (scp->music) {
#if 0
lockmgr(&md->synth.vc_lock, LK_EXCLUSIVE);
md->synth.chn_info[chn].bender_value = w14;
if (md->midiunit >= 0) {
key = ((int)chn << 8);
for (i = 0; i < md->synth.alloc.max_voice; i++)
if ((md->synth.alloc.map[i] & 0xff00) == key) {
lockmgr(&md->synth.vc_lock, LK_RELEASE);
lockmgr(&scp->seq_lock, LK_RELEASE);
if (md->synth.bender(md, i, w14) == EAGAIN) {
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
return (QUEUEFULL);
}
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
}
} else {
lockmgr(&md->synth.vc_lock, LK_RELEASE);
lockmgr(&scp->seq_lock, LK_RELEASE);
if (md->synth.bender(md, chn, w14) == EAGAIN) {
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
return (QUEUEFULL);
}
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
}
#endif
} else
SYNTH_BENDER(md, chn, w14);
break;
default:
ret = 1;
SEQ_DEBUG(2,
kprintf("seq_chncommon event type %d not handled.\n",
event[1]));
break;
}
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
return ret;
}
static int
seq_timing(struct seq_softc *scp, u_char *event)
{
int param;
int ret;
ret = 0;
param = event[4] + (event[5] << 8) +
(event[6] << 16) + (event[7] << 24);
SEQ_DEBUG(5, kprintf("seq_timing: unit %d, cmd %d, param %d.\n",
scp->unit, event[1], param));
switch (event[1]) {
case TMR_WAIT_REL:
timer_wait(scp, param, 0);
break;
case TMR_WAIT_ABS:
timer_wait(scp, param, 1);
break;
case TMR_START:
timer_start(scp);
cv_broadcast(&scp->reset_cv);
break;
case TMR_STOP:
timer_stop(scp);
cv_broadcast(&scp->reset_cv);
break;
case TMR_CONTINUE:
timer_continue(scp);
cv_broadcast(&scp->reset_cv);
break;
case TMR_TEMPO:
if (param < 8)
param = 8;
if (param > 360)
param = 360;
SEQ_DEBUG(4, kprintf("Timer set tempo %d\n", param));
timer_setvals(scp, param, scp->timerbase);
break;
case TMR_TIMERBASE:
if (param < 1)
param = 1;
if (param > 1000)
param = 1000;
SEQ_DEBUG(4, kprintf("Timer set timerbase %d\n", param));
timer_setvals(scp, scp->tempo, param);
break;
case TMR_ECHO:
#if 0
if (scp->music)
seq_copytoinput(scp, event, 8);
else {
param = (param << 8 | SEQ_ECHO);
seq_copytoinput(scp, (u_char *)¶m, 4);
}
#else
seq_copytoinput(scp, event, 8);
#endif
break;
default:
SEQ_DEBUG(2, kprintf("seq_timing event type %d not handled.\n",
event[1]));
ret = 1;
break;
}
return ret;
}
static int
seq_local(struct seq_softc *scp, u_char *event)
{
int ret;
ret = 0;
KKASSERT(lockowned(&scp->seq_lock));
SEQ_DEBUG(5, kprintf("seq_local: unit %d, cmd %d\n", scp->unit,
event[1]));
switch (event[1]) {
default:
SEQ_DEBUG(1, kprintf("seq_local event type %d not handled\n",
event[1]));
ret = 1;
break;
}
return ret;
}
static int
seq_sysex(struct seq_softc *scp, kobj_t md, u_char *event)
{
int i, l;
KKASSERT(lockowned(&scp->seq_lock));
SEQ_DEBUG(5, kprintf("seq_sysex: unit %d device %d\n", scp->unit,
event[1]));
l = 0;
for (i = 0; i < 6 && event[i + 2] != 0xff; i++)
l = i + 1;
if (l > 0) {
lockmgr(&scp->seq_lock, LK_RELEASE);
if (SYNTH_SENDSYSEX(md, &event[2], l) == EAGAIN) {
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
return 1;
}
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
}
return 0;
}
static void
seq_reset(struct seq_softc *scp)
{
int chn, i;
kobj_t m;
KKASSERT(lockowned(&scp->seq_lock));
SEQ_DEBUG(5, kprintf("seq_reset: unit %d.\n", scp->unit));
scp->playing = 0;
cv_broadcast(&scp->state_cv);
cv_broadcast(&scp->out_cv);
cv_broadcast(&scp->reset_cv);
MIDIQ_CLEAR(scp->in_q);
MIDIQ_CLEAR(scp->out_q);
for (i = 0; i < scp->midi_number; i++) {
m = scp->midis[i];
lockmgr(&scp->seq_lock, LK_RELEASE);
SYNTH_RESET(m);
for (chn = 0; chn < 16; chn++) {
SYNTH_CONTROLLER(m, chn, 123, 0);
SYNTH_CONTROLLER(m, chn, 121, 0);
SYNTH_BENDER(m, chn, 1 << 13);
}
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
}
}
#define SEQ_SYNC_TIMEOUT 8
static int
seq_sync(struct seq_softc *scp)
{
int i, rl, sync[16], done;
KKASSERT(lockowned(&scp->seq_lock));
SEQ_DEBUG(4, kprintf("seq_sync: unit %d.\n", scp->unit));
while (!MIDIQ_EMPTY(scp->out_q)) {
if (!scp->playing) {
scp->playing = 1;
cv_broadcast(&scp->state_cv);
cv_broadcast(&scp->out_cv);
}
rl = MIDIQ_LEN(scp->out_q);
i = cv_timedwait_sig(&scp->out_cv,
&scp->seq_lock, SEQ_SYNC_TIMEOUT * hz);
if (i == EINTR || i == ERESTART) {
if (i == EINTR) {
scp->playing = 0;
cv_broadcast(&scp->out_cv);
}
return i;
}
if (i == EWOULDBLOCK && rl == MIDIQ_LEN(scp->out_q) &&
scp->waiting == 0) {
MIDIQ_CLEAR(scp->out_q);
scp->playing = 0;
cv_broadcast(&scp->state_cv);
cv_broadcast(&scp->out_cv);
cv_broadcast(&scp->reset_cv);
SEQ_DEBUG(1, kprintf("seq_sync queue stuck, aborting\n"));
return i;
}
}
scp->playing = 0;
lockmgr(&scp->seq_lock, LK_RELEASE);
for (i = 0; i < scp->midi_number; i++)
sync[i] = 1;
do {
done = 1;
for (i = 0; i < scp->midi_number; i++)
if (sync[i]) {
if (SYNTH_INSYNC(scp->midis[i]) == 0)
sync[i] = 0;
else
done = 0;
}
if (!done)
DELAY(5000);
} while (!done);
lockmgr(&scp->seq_lock, LK_EXCLUSIVE);
return 0;
}
char *
midi_cmdname(int cmd, midi_cmdtab *tab)
{
while (tab->name != NULL) {
if (cmd == tab->cmd)
return (tab->name);
tab++;
}
return ("unknown");
}