#include <sys/cdefs.h>
__FBSDID("$FreeBSD: head/sys/dev/sound/midi/midi.c 227309 2011-11-07 15:43:11Z ed $");
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/conf.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/fcntl.h>
#include <sys/uio.h>
#include <sys/poll.h>
#include <sys/sbuf.h>
#include <sys/kobj.h>
#include <sys/module.h>
#include <sys/device.h>
#ifdef HAVE_KERNEL_OPTION_HEADERS
#include "opt_snd.h"
#endif
#include <dev/sound/midi/midi.h>
#include "mpu_if.h"
#include <dev/sound/midi/midiq.h>
#include "synth_if.h"
MALLOC_DEFINE(M_MIDI, "midi buffers", "Midi data allocation area");
#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f))
#define MIDIMKMINOR(u, d, c) PCMMKMINOR(u, d, c)
#define MIDI_DEV_RAW 2
#define MIDI_DEV_MIDICTL 12
enum midi_states {
MIDI_IN_START, MIDI_IN_SYSEX, MIDI_IN_DATA
};
#include "mpu_if.h"
#define MIDI_NAMELEN 16
struct snd_midi {
KOBJ_FIELDS;
struct lock lock;
void *cookie;
int unit;
int channel;
int busy;
int flags;
char name[MIDI_NAMELEN];
struct lock qlock;
MIDIQ_HEAD(, char) inq, outq;
int rchan, wchan;
struct kqinfo rkq, wkq;
int hiwat;
enum midi_states inq_state;
int inq_status, inq_left;
struct proc *async;
struct cdev *dev;
struct synth_midi *synth;
int synth_flags;
TAILQ_ENTRY(snd_midi) link;
};
struct synth_midi {
KOBJ_FIELDS;
struct snd_midi *m;
};
static synth_open_t midisynth_open;
static synth_close_t midisynth_close;
static synth_writeraw_t midisynth_writeraw;
static synth_killnote_t midisynth_killnote;
static synth_startnote_t midisynth_startnote;
static synth_setinstr_t midisynth_setinstr;
static synth_alloc_t midisynth_alloc;
static synth_controller_t midisynth_controller;
static synth_bender_t midisynth_bender;
static kobj_method_t midisynth_methods[] = {
KOBJMETHOD(synth_open, midisynth_open),
KOBJMETHOD(synth_close, midisynth_close),
KOBJMETHOD(synth_writeraw, midisynth_writeraw),
KOBJMETHOD(synth_setinstr, midisynth_setinstr),
KOBJMETHOD(synth_startnote, midisynth_startnote),
KOBJMETHOD(synth_killnote, midisynth_killnote),
KOBJMETHOD(synth_alloc, midisynth_alloc),
KOBJMETHOD(synth_controller, midisynth_controller),
KOBJMETHOD(synth_bender, midisynth_bender),
KOBJMETHOD_END
};
DEFINE_CLASS(midisynth, midisynth_methods, 0);
TAILQ_HEAD(, snd_midi) midi_devs;
static struct lock midistat_lock;
static int midistat_isopen = 0;
static struct sbuf midistat_sbuf;
static struct cdev *midistat_dev;
static d_open_t midistat_open;
static d_close_t midistat_close;
static d_read_t midistat_read;
static void midi_filter_detach(struct knote *);
static int midi_filter_read(struct knote *, long);
static int midi_filter_write(struct knote *, long);
static struct dev_ops midistat_ops = {
{ "midistat", 0, D_MPSAFE },
.d_open = midistat_open,
.d_close = midistat_close,
.d_read = midistat_read,
};
static struct filterops midi_read_filterops =
{ FILTEROP_ISFD, NULL, midi_filter_detach, midi_filter_read };
static struct filterops midi_write_filterops =
{ FILTEROP_ISFD, NULL, midi_filter_detach, midi_filter_write };
static d_open_t midi_open;
static d_close_t midi_close;
static d_ioctl_t midi_ioctl;
static d_read_t midi_read;
static d_write_t midi_write;
static d_kqfilter_t midi_kqfilter;
static struct dev_ops midi_ops = {
{ "rmidi", 0, D_MPSAFE },
.d_open = midi_open,
.d_close = midi_close,
.d_read = midi_read,
.d_write = midi_write,
.d_ioctl = midi_ioctl,
.d_kqfilter = midi_kqfilter,
};
static int midi_destroy(struct snd_midi *, int);
static int midistat_prepare(struct sbuf * s);
static int midi_load(void);
static int midi_unload(void);
SYSCTL_NODE(_hw, OID_AUTO, midi, CTLFLAG_RD, 0, "Midi driver");
static SYSCTL_NODE(_hw_midi, OID_AUTO, stat, CTLFLAG_RD, 0, "Status device");
int midi_debug;
SYSCTL_INT(_hw_midi, OID_AUTO, debug, CTLFLAG_RW, &midi_debug, 0, "");
int midi_dumpraw;
SYSCTL_INT(_hw_midi, OID_AUTO, dumpraw, CTLFLAG_RW, &midi_dumpraw, 0, "");
int midi_instroff;
SYSCTL_INT(_hw_midi, OID_AUTO, instroff, CTLFLAG_RW, &midi_instroff, 0, "");
int midistat_verbose;
SYSCTL_INT(_hw_midi_stat, OID_AUTO, verbose, CTLFLAG_RW,
&midistat_verbose, 0, "");
#define MIDI_DEBUG(l,a) if(midi_debug>=l) a
struct snd_midi *
midi_init(kobj_class_t cls, int unit, int channel, void *cookie)
{
struct snd_midi *m;
int i;
int inqsize, outqsize;
MIDI_TYPE *buf;
MIDI_DEBUG(1, kprintf("midiinit: unit %d/%d.\n", unit, channel));
lockmgr(&midistat_lock, LK_EXCLUSIVE);
i = -1;
TAILQ_FOREACH(m, &midi_devs, link) {
lockmgr(&m->lock, LK_EXCLUSIVE);
if (unit != 0) {
if (m->unit == unit && m->channel == channel) {
lockmgr(&m->lock, LK_RELEASE);
goto err0;
}
} else {
if (m->unit > i)
i = m->unit;
}
lockmgr(&m->lock, LK_RELEASE);
}
if (unit == 0)
unit = i + 1;
MIDI_DEBUG(1, kprintf("midiinit #2: unit %d/%d.\n", unit, channel));
m = kmalloc(sizeof(*m), M_MIDI, M_WAITOK | M_ZERO);
m->synth = kmalloc(sizeof(*m->synth), M_MIDI, M_WAITOK | M_ZERO);
kobj_init((kobj_t)m->synth, &midisynth_class);
m->synth->m = m;
kobj_init((kobj_t)m, cls);
inqsize = MPU_INQSIZE(m, cookie);
outqsize = MPU_OUTQSIZE(m, cookie);
MIDI_DEBUG(1, kprintf("midiinit queues %d/%d.\n", inqsize, outqsize));
if (!inqsize && !outqsize)
goto err1;
lockinit(&m->lock, "raw midi", 0, LK_CANRECURSE);
lockinit(&m->qlock, "q raw midi", 0, LK_CANRECURSE);
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
if (inqsize)
buf = kmalloc(sizeof(MIDI_TYPE) * inqsize, M_MIDI, M_WAITOK);
else
buf = NULL;
MIDIQ_INIT(m->inq, buf, inqsize);
if (outqsize)
buf = kmalloc(sizeof(MIDI_TYPE) * outqsize, M_MIDI, M_WAITOK);
else
buf = NULL;
m->hiwat = outqsize / 2;
MIDIQ_INIT(m->outq, buf, outqsize);
if ((inqsize && !MIDIQ_BUF(m->inq)) ||
(outqsize && !MIDIQ_BUF(m->outq)))
goto err2;
m->busy = 0;
m->flags = 0;
m->unit = unit;
m->channel = channel;
m->cookie = cookie;
if (MPU_INIT(m, cookie))
goto err2;
lockmgr(&m->lock, LK_RELEASE);
lockmgr(&m->qlock, LK_RELEASE);
TAILQ_INSERT_TAIL(&midi_devs, m, link);
lockmgr(&midistat_lock, LK_RELEASE);
m->dev = make_dev(&midi_ops,
MIDIMKMINOR(unit, MIDI_DEV_RAW, channel),
UID_ROOT, GID_WHEEL, 0666, "midi%d.%d", unit, channel);
m->dev->si_drv1 = m;
return m;
err2:
lockuninit(&m->qlock);
lockuninit(&m->lock);
if (MIDIQ_BUF(m->inq))
kfree(MIDIQ_BUF(m->inq), M_MIDI);
if (MIDIQ_BUF(m->outq))
kfree(MIDIQ_BUF(m->outq), M_MIDI);
err1:
kfree(m, M_MIDI);
err0:
lockmgr(&midistat_lock, LK_RELEASE);
MIDI_DEBUG(1, kprintf("midi_init ended in error\n"));
return NULL;
}
int
midi_uninit(struct snd_midi *m)
{
int err;
err = ENXIO;
lockmgr(&midistat_lock, LK_EXCLUSIVE);
lockmgr(&m->lock, LK_EXCLUSIVE);
if (m->busy) {
if (!(m->rchan || m->wchan))
goto err;
if (m->rchan) {
wakeup(&m->rchan);
m->rchan = 0;
}
if (m->wchan) {
wakeup(&m->wchan);
m->wchan = 0;
}
}
err = midi_destroy(m, 0);
if (!err)
goto exit;
err: lockmgr(&m->lock, LK_RELEASE);
exit: lockmgr(&midistat_lock, LK_RELEASE);
return err;
}
#ifdef notdef
static int midi_lengths[] = {2, 2, 2, 2, 1, 1, 2, 0};
#endif
#define MIDI_LENGTH(d) (midi_lengths[((d) >> 4) & 7])
#define MIDI_ACK 0xfe
#define MIDI_IS_STATUS(d) ((d) >= 0x80)
#define MIDI_IS_COMMON(d) ((d) >= 0xf0)
#define MIDI_SYSEX_START 0xF0
#define MIDI_SYSEX_END 0xF7
int
midi_in(struct snd_midi *m, MIDI_TYPE *buf, int size)
{
int used;
MIDI_DEBUG(5, kprintf("midi_in: m=%p size=%d\n", m, size));
if (!(m->flags & M_RX))
return size;
used = 0;
lockmgr(&m->qlock, LK_EXCLUSIVE);
#if 0
if (!(m->flags & M_RX))
return size;
for (i = sig = 0; i < size; i++) {
data = buf[i];
enq = 0;
if (data == MIDI_ACK)
continue;
switch (m->inq_state) {
case MIDI_IN_START:
if (MIDI_IS_STATUS(data)) {
switch (data) {
case 0xf0:
m->inq_state = MIDI_IN_SYSEX;
break;
case 0xf1:
case 0xf3:
m->inq_state = MIDI_IN_DATA;
enq = 1;
m->inq_left = 1;
break;
case 0xf2:
m->inq_state = MIDI_IN_DATA;
enq = 1;
m->inq_left = 2;
break;
default:
if (MIDI_IS_COMMON(data)) {
enq = 1;
sig = 1;
} else {
m->inq_state = MIDI_IN_DATA;
enq = 1;
m->inq_status = data;
m->inq_left = MIDI_LENGTH(data);
}
break;
}
} else if (MIDI_IS_STATUS(m->inq_status)) {
m->inq_state = MIDI_IN_DATA;
if (!MIDIQ_FULL(m->inq)) {
used++;
MIDIQ_ENQ(m->inq, &m->inq_status, 1);
}
enq = 1;
m->inq_left = MIDI_LENGTH(m->inq_status) - 1;
}
break;
case MIDI_IN_DATA:
enq = 1;
if (--m->inq_left <= 0)
sig = 1;
break;
case MIDI_IN_SYSEX:
if (data == MIDI_SYSEX_END)
m->inq_state = MIDI_IN_START;
break;
}
if (enq)
if (!MIDIQ_FULL(m->inq)) {
MIDIQ_ENQ(m->inq, &data, 1);
used++;
}
}
if (sig) {
#endif
MIDI_DEBUG(6, kprintf("midi_in: len %jd avail %jd\n",
(intmax_t)MIDIQ_LEN(m->inq),
(intmax_t)MIDIQ_AVAIL(m->inq)));
if (MIDIQ_AVAIL(m->inq) > size) {
used = size;
MIDIQ_ENQ(m->inq, buf, size);
} else {
MIDI_DEBUG(4, kprintf("midi_in: Discarding data qu\n"));
lockmgr(&m->qlock, LK_RELEASE);
return 0;
}
if (m->rchan) {
wakeup(&m->rchan);
m->rchan = 0;
}
KNOTE(&m->rkq.ki_note, 0);
if (m->async) {
PHOLD(m->async);
ksignal(m->async, SIGIO);
PRELE(m->async);
}
#if 0
}
#endif
lockmgr(&m->qlock, LK_RELEASE);
return used;
}
int
midi_out(struct snd_midi *m, MIDI_TYPE *buf, int size)
{
int used;
if (!(m->flags & M_TXEN))
return 0;
MIDI_DEBUG(2, kprintf("midi_out: %p\n", m));
lockmgr(&m->qlock, LK_EXCLUSIVE);
used = MIN(size, MIDIQ_LEN(m->outq));
MIDI_DEBUG(3, kprintf("midi_out: used %d\n", used));
if (used)
MIDIQ_DEQ(m->outq, buf, used);
if (MIDIQ_EMPTY(m->outq)) {
m->flags &= ~M_TXEN;
MPU_CALLBACKP(m, m->cookie, m->flags);
}
if (used && MIDIQ_AVAIL(m->outq) > m->hiwat) {
if (m->wchan) {
wakeup(&m->wchan);
m->wchan = 0;
}
KNOTE(&m->wkq.ki_note, 0);
if (m->async) {
PHOLD(m->async);
ksignal(m->async, SIGIO);
PRELE(m->async);
}
}
lockmgr(&m->qlock, LK_RELEASE);
return used;
}
int
midi_open(struct dev_open_args *ap)
{
cdev_t i_dev = ap->a_head.a_dev;
int flags = ap->a_oflags;
struct snd_midi *m = i_dev->si_drv1;
int retval;
#if 0
MIDI_DEBUG(1, kprintf("midiopen %p %s %s\n", td,
flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
#endif
if (m == NULL)
return ENXIO;
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
retval = 0;
if (flags & FREAD) {
if (MIDIQ_SIZE(m->inq) == 0)
retval = ENXIO;
else if (m->flags & M_RX)
retval = EBUSY;
if (retval)
goto err;
}
if (flags & FWRITE) {
if (MIDIQ_SIZE(m->outq) == 0)
retval = ENXIO;
else if (m->flags & M_TX)
retval = EBUSY;
if (retval)
goto err;
}
m->busy++;
m->rchan = 0;
m->wchan = 0;
m->async = 0;
if (flags & FREAD) {
m->flags |= M_RX | M_RXEN;
MIDIQ_CLEAR(m->inq);
};
if (flags & FWRITE)
m->flags |= M_TX;
MPU_CALLBACK(m, m->cookie, m->flags);
MIDI_DEBUG(2, kprintf("midi_open: opened.\n"));
err: lockmgr(&m->qlock, LK_RELEASE);
lockmgr(&m->lock, LK_RELEASE);
return retval;
}
int
midi_close(struct dev_close_args *ap)
{
cdev_t i_dev = ap->a_head.a_dev;
int flags = ap->a_fflag;
struct snd_midi *m = i_dev->si_drv1;
int retval;
int oldflags;
#if 0
MIDI_DEBUG(1, kprintf("midi_close %p %s %s\n", td,
flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
#endif
if (m == NULL)
return ENXIO;
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
if ((flags & FREAD && !(m->flags & M_RX)) ||
(flags & FWRITE && !(m->flags & M_TX))) {
retval = ENXIO;
goto err;
}
m->busy--;
oldflags = m->flags;
if (flags & FREAD)
m->flags &= ~(M_RX | M_RXEN);
if (flags & FWRITE)
m->flags &= ~M_TX;
if ((m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN)))
MPU_CALLBACK(m, m->cookie, m->flags);
MIDI_DEBUG(1, kprintf("midi_close: closed, busy = %d.\n", m->busy));
lockmgr(&m->qlock, LK_RELEASE);
lockmgr(&m->lock, LK_RELEASE);
retval = 0;
err: return retval;
}
int
midi_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;
#define MIDI_RSIZE 32
struct snd_midi *m = i_dev->si_drv1;
int retval;
int used;
char buf[MIDI_RSIZE];
MIDI_DEBUG(5, kprintf("midiread: count=%lu\n",
(unsigned long)uio->uio_resid));
retval = EIO;
if (m == NULL)
goto err0;
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
if (!(m->flags & M_RX))
goto err1;
while (uio->uio_resid > 0) {
while (MIDIQ_EMPTY(m->inq)) {
retval = EWOULDBLOCK;
if (ioflag & O_NONBLOCK)
goto err1;
lockmgr(&m->lock, LK_RELEASE);
m->rchan = 1;
retval = lksleep(&m->rchan, &m->qlock,
PCATCH, "midi RX", 0);
if (retval == EINTR)
goto err0;
if (m != i_dev->si_drv1)
retval = ENXIO;
if (retval)
goto err0;
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
m->rchan = 0;
if (!m->busy)
goto err1;
}
MIDI_DEBUG(6, kprintf("midi_read start\n"));
used = MIN(MIDIQ_LEN(m->inq), uio->uio_resid);
used = MIN(used, MIDI_RSIZE);
MIDI_DEBUG(6, kprintf("midiread: uiomove cc=%d\n", used));
MIDIQ_DEQ(m->inq, buf, used);
retval = uiomove(buf, used, uio);
if (retval)
goto err1;
}
retval = 0;
err1: lockmgr(&m->qlock, LK_RELEASE);
lockmgr(&m->lock, LK_RELEASE);
err0: MIDI_DEBUG(4, kprintf("midi_read: ret %d\n", retval));
return retval;
}
int
midi_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;
#define MIDI_WSIZE 32
struct snd_midi *m = i_dev->si_drv1;
int retval;
int used;
char buf[MIDI_WSIZE];
MIDI_DEBUG(4, kprintf("midi_write\n"));
retval = 0;
if (m == NULL)
goto err0;
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
if (!(m->flags & M_TX))
goto err1;
while (uio->uio_resid > 0) {
while (MIDIQ_AVAIL(m->outq) == 0) {
retval = EWOULDBLOCK;
if (ioflag & O_NONBLOCK)
goto err1;
lockmgr(&m->lock, LK_RELEASE);
m->wchan = 1;
MIDI_DEBUG(3, kprintf("midi_write lksleep\n"));
retval = lksleep(&m->wchan, &m->qlock,
PCATCH, "midi TX", 0);
if (retval == EINTR)
goto err0;
if (m != i_dev->si_drv1)
retval = ENXIO;
if (retval)
goto err0;
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
m->wchan = 0;
if (!m->busy)
goto err1;
}
used = MIN(MIDIQ_AVAIL(m->outq), uio->uio_resid);
used = MIN(used, MIDI_WSIZE);
MIDI_DEBUG(5, kprintf("midiout: resid %zd len %jd avail %jd\n",
uio->uio_resid, (intmax_t)MIDIQ_LEN(m->outq),
(intmax_t)MIDIQ_AVAIL(m->outq)));
MIDI_DEBUG(5, kprintf("midi_write: uiomove cc=%d\n", used));
retval = uiomove(buf, used, uio);
if (retval)
goto err1;
MIDIQ_ENQ(m->outq, buf, used);
if (!(m->flags & M_TXEN)) {
m->flags |= M_TXEN;
MPU_CALLBACK(m, m->cookie, m->flags);
}
}
retval = 0;
err1: lockmgr(&m->qlock, LK_RELEASE);
lockmgr(&m->lock, LK_RELEASE);
err0: return retval;
}
int
midi_ioctl(struct dev_ioctl_args *ap)
{
return ENXIO;
}
int
midi_kqfilter(struct dev_kqfilter_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct knote *kn = ap->a_kn;
struct snd_midi *m;
struct klist *klist;
ap->a_result = 0;
m = dev->si_drv1;
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &midi_read_filterops;
kn->kn_hook = (caddr_t)m;
klist = &m->rkq.ki_note;
break;
case EVFILT_WRITE:
kn->kn_fop = &midi_write_filterops;
kn->kn_hook = (caddr_t)m;
klist = &m->wkq.ki_note;
break;
default:
ap->a_result = EOPNOTSUPP;
return (0);
}
knote_insert(klist, kn);
return(0);
}
static void
midi_filter_detach(struct knote *kn)
{
struct snd_midi *m = (struct snd_midi *)kn->kn_hook;
struct klist *rklist = &m->rkq.ki_note;
struct klist *wklist = &m->wkq.ki_note;
knote_remove(rklist, kn);
knote_remove(wklist, kn);
}
static int
midi_filter_read(struct knote *kn, long hint)
{
struct snd_midi *m = (struct snd_midi *)kn->kn_hook;
int ready = 0;
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
if (!MIDIQ_EMPTY(m->inq))
ready = 1;
lockmgr(&m->lock, LK_RELEASE);
lockmgr(&m->qlock, LK_RELEASE);
return (ready);
}
static int
midi_filter_write(struct knote *kn, long hint)
{
struct snd_midi *m = (struct snd_midi *)kn->kn_hook;
int ready = 0;
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
if (MIDIQ_AVAIL(m->outq) < m->hiwat)
ready = 1;
lockmgr(&m->lock, LK_RELEASE);
lockmgr(&m->qlock, LK_RELEASE);
return (ready);
}
static int
midistat_open(struct dev_open_args *ap)
{
int error;
MIDI_DEBUG(1, kprintf("midistat_open\n"));
lockmgr(&midistat_lock, LK_EXCLUSIVE);
if (midistat_isopen) {
lockmgr(&midistat_lock, LK_RELEASE);
return EBUSY;
}
midistat_isopen = 1;
if (sbuf_new(&midistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) {
error = ENXIO;
goto out;
}
error = (midistat_prepare(&midistat_sbuf) > 0) ? 0 : ENOMEM;
out:
if (error)
midistat_isopen = 0;
lockmgr(&midistat_lock, LK_RELEASE);
return error;
}
static int
midistat_close(struct dev_close_args *ap)
{
MIDI_DEBUG(1, kprintf("midistat_close\n"));
lockmgr(&midistat_lock, LK_EXCLUSIVE);
if (!midistat_isopen) {
lockmgr(&midistat_lock, LK_RELEASE);
return EBADF;
}
sbuf_delete(&midistat_sbuf);
midistat_isopen = 0;
lockmgr(&midistat_lock, LK_RELEASE);
return 0;
}
static int
midistat_read(struct dev_read_args *ap)
{
struct uio *uio = ap->a_uio;
int l, err;
MIDI_DEBUG(4, kprintf("midistat_read\n"));
lockmgr(&midistat_lock, LK_EXCLUSIVE);
if (!midistat_isopen) {
lockmgr(&midistat_lock, LK_RELEASE);
return EBADF;
}
l = min(uio->uio_resid, sbuf_len(&midistat_sbuf) - uio->uio_offset);
err = 0;
if (l > 0) {
err = uiomove(sbuf_data(&midistat_sbuf) + uio->uio_offset,
l, uio);
}
lockmgr(&midistat_lock, LK_RELEASE);
return err;
}
static int
midistat_prepare(struct sbuf *s)
{
struct snd_midi *m;
KKASSERT(lockowned(&midistat_lock));
sbuf_printf(s, "FreeBSD Midi Driver (midi2)\n");
if (TAILQ_EMPTY(&midi_devs)) {
sbuf_printf(s, "No devices installed.\n");
sbuf_finish(s);
return sbuf_len(s);
}
sbuf_printf(s, "Installed devices:\n");
TAILQ_FOREACH(m, &midi_devs, link) {
lockmgr(&m->lock, LK_EXCLUSIVE);
sbuf_printf(s, "%s [%d/%d:%s]", m->name, m->unit, m->channel,
MPU_PROVIDER(m, m->cookie));
sbuf_printf(s, "%s", MPU_DESCR(m, m->cookie, midistat_verbose));
sbuf_printf(s, "\n");
lockmgr(&m->lock, LK_RELEASE);
}
sbuf_finish(s);
return sbuf_len(s);
}
#ifdef notdef
static char *
midi_cmdname(int cmd)
{
static struct {
int cmd;
char *name;
} *tab, cmdtab_midiioctl[] = {
#define A(x) {x, ## x}
#undef A
{
-1, "unknown"
},
};
for (tab = cmdtab_midiioctl; tab->cmd != cmd && tab->cmd != -1; tab++);
return tab->name;
}
#endif
int
midisynth_open(void *n, void *arg, int flags)
{
struct snd_midi *m = ((struct synth_midi *)n)->m;
int retval;
MIDI_DEBUG(1, kprintf("midisynth_open %s %s\n",
flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : ""));
if (m == NULL)
return ENXIO;
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
retval = 0;
if (flags & FREAD) {
if (MIDIQ_SIZE(m->inq) == 0)
retval = ENXIO;
else if (m->flags & M_RX)
retval = EBUSY;
if (retval)
goto err;
}
if (flags & FWRITE) {
if (MIDIQ_SIZE(m->outq) == 0)
retval = ENXIO;
else if (m->flags & M_TX)
retval = EBUSY;
if (retval)
goto err;
}
m->busy++;
if (flags & FREAD) {
m->flags |= M_RX | M_RXEN;
MIDIQ_CLEAR(m->inq);
m->rchan = 0;
};
if (flags & FWRITE) {
m->flags |= M_TX;
m->wchan = 0;
}
m->synth_flags = flags & (FREAD | FWRITE);
MPU_CALLBACK(m, m->cookie, m->flags);
err: lockmgr(&m->qlock, LK_RELEASE);
lockmgr(&m->lock, LK_RELEASE);
MIDI_DEBUG(2, kprintf("midisynth_open: return %d.\n", retval));
return retval;
}
int
midisynth_close(void *n)
{
struct snd_midi *m = ((struct synth_midi *)n)->m;
int retval;
int oldflags;
MIDI_DEBUG(1, kprintf("midisynth_close %s %s\n",
m->synth_flags & FREAD ? "M_RX" : "",
m->synth_flags & FWRITE ? "M_TX" : ""));
if (m == NULL)
return ENXIO;
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
if ((m->synth_flags & FREAD && !(m->flags & M_RX)) ||
(m->synth_flags & FWRITE && !(m->flags & M_TX))) {
retval = ENXIO;
goto err;
}
m->busy--;
oldflags = m->flags;
if (m->synth_flags & FREAD)
m->flags &= ~(M_RX | M_RXEN);
if (m->synth_flags & FWRITE)
m->flags &= ~M_TX;
if ((m->flags & (M_TXEN | M_RXEN)) != (oldflags & (M_RXEN | M_TXEN)))
MPU_CALLBACK(m, m->cookie, m->flags);
MIDI_DEBUG(1, kprintf("midi_close: closed, busy = %d.\n", m->busy));
lockmgr(&m->qlock, LK_RELEASE);
lockmgr(&m->lock, LK_RELEASE);
retval = 0;
err: return retval;
}
int
midisynth_writeraw(void *n, uint8_t *buf, size_t len)
{
struct snd_midi *m = ((struct synth_midi *)n)->m;
int retval;
int used;
int i;
MIDI_DEBUG(4, kprintf("midisynth_writeraw\n"));
retval = 0;
if (m == NULL)
return ENXIO;
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
if (!(m->flags & M_TX))
goto err1;
if (midi_dumpraw)
kprintf("midi dump: ");
while (len > 0) {
while (MIDIQ_AVAIL(m->outq) == 0) {
if (!(m->flags & M_TXEN)) {
m->flags |= M_TXEN;
MPU_CALLBACK(m, m->cookie, m->flags);
}
lockmgr(&m->lock, LK_RELEASE);
m->wchan = 1;
MIDI_DEBUG(3, kprintf("midisynth_writeraw lksleep\n"));
retval = lksleep(&m->wchan, &m->qlock,
PCATCH, "midi TX", 0);
if (retval == EINTR)
goto err0;
if (retval)
goto err0;
lockmgr(&m->lock, LK_EXCLUSIVE);
lockmgr(&m->qlock, LK_EXCLUSIVE);
m->wchan = 0;
if (!m->busy)
goto err1;
}
used = MIN(MIDIQ_AVAIL(m->outq), len);
used = MIN(used, MIDI_WSIZE);
MIDI_DEBUG(5,
kprintf("midi_synth: resid %zu len %jd avail %jd\n",
len, (intmax_t)MIDIQ_LEN(m->outq),
(intmax_t)MIDIQ_AVAIL(m->outq)));
if (midi_dumpraw)
for (i = 0; i < used; i++)
kprintf("%x ", buf[i]);
MIDIQ_ENQ(m->outq, buf, used);
len -= used;
if (!(m->flags & M_TXEN)) {
m->flags |= M_TXEN;
MPU_CALLBACK(m, m->cookie, m->flags);
}
}
if (midi_dumpraw)
kprintf("\n");
retval = 0;
err1: lockmgr(&m->qlock, LK_RELEASE);
lockmgr(&m->lock, LK_RELEASE);
err0: return retval;
}
static int
midisynth_killnote(void *n, uint8_t chn, uint8_t note, uint8_t vel)
{
u_char c[3];
if (note > 127 || chn > 15)
return (EINVAL);
if (vel > 127)
vel = 127;
if (vel == 64) {
c[0] = 0x90 | (chn & 0x0f);
c[1] = (u_char)note;
c[2] = 0;
} else {
c[0] = 0x80 | (chn & 0x0f);
c[1] = (u_char)note;
c[2] = (u_char)vel;
}
return midisynth_writeraw(n, c, 3);
}
static int
midisynth_setinstr(void *n, uint8_t chn, uint16_t instr)
{
u_char c[2];
if (instr > 127 || chn > 15)
return EINVAL;
c[0] = 0xc0 | (chn & 0x0f);
c[1] = instr + midi_instroff;
return midisynth_writeraw(n, c, 2);
}
static int
midisynth_startnote(void *n, uint8_t chn, uint8_t note, uint8_t vel)
{
u_char c[3];
if (note > 127 || chn > 15)
return EINVAL;
if (vel > 127)
vel = 127;
c[0] = 0x90 | (chn & 0x0f);
c[1] = (u_char)note;
c[2] = (u_char)vel;
return midisynth_writeraw(n, c, 3);
}
static int
midisynth_alloc(void *n, uint8_t chan, uint8_t note)
{
return chan;
}
static int
midisynth_controller(void *n, uint8_t chn, uint8_t ctrlnum, uint16_t val)
{
u_char c[3];
if (ctrlnum > 127 || chn > 15)
return EINVAL;
c[0] = 0xb0 | (chn & 0x0f);
c[1] = ctrlnum;
c[2] = val;
return midisynth_writeraw(n, c, 3);
}
static int
midisynth_bender(void *n, uint8_t chn, uint16_t val)
{
u_char c[3];
if (val > 16383 || chn > 15)
return EINVAL;
c[0] = 0xe0 | (chn & 0x0f);
c[1] = (u_char)val & 0x7f;
c[2] = (u_char)(val >> 7) & 0x7f;
return midisynth_writeraw(n, c, 3);
}
static int
midi_destroy(struct snd_midi *m, int midiuninit)
{
KKASSERT(lockowned(&midistat_lock));
KKASSERT(lockowned(&m->lock));
MIDI_DEBUG(3, kprintf("midi_destroy\n"));
m->dev->si_drv1 = NULL;
lockmgr(&m->lock, LK_RELEASE);
destroy_dev(m->dev);
TAILQ_REMOVE(&midi_devs, m, link);
if (midiuninit)
MPU_UNINIT(m, m->cookie);
kfree(MIDIQ_BUF(m->inq), M_MIDI);
kfree(MIDIQ_BUF(m->outq), M_MIDI);
lockuninit(&m->qlock);
lockuninit(&m->lock);
kfree(m, M_MIDI);
return 0;
}
static int
midi_load(void)
{
lockinit(&midistat_lock, "midistat lock", 0, LK_CANRECURSE);
TAILQ_INIT(&midi_devs);
midistat_dev = make_dev(&midistat_ops,
MIDIMKMINOR(0, MIDI_DEV_MIDICTL, 0),
UID_ROOT, GID_WHEEL, 0666, "midistat");
return 0;
}
static int
midi_unload(void)
{
struct snd_midi *m;
int retval;
MIDI_DEBUG(1, kprintf("midi_unload()\n"));
retval = EBUSY;
lockmgr(&midistat_lock, LK_EXCLUSIVE);
if (midistat_isopen)
goto exit0;
TAILQ_FOREACH(m, &midi_devs, link) {
lockmgr(&m->lock, LK_EXCLUSIVE);
if (m->busy)
retval = EBUSY;
else
retval = midi_destroy(m, 1);
if (retval)
goto exit1;
}
lockmgr(&midistat_lock, LK_RELEASE);
destroy_dev(midistat_dev);
lockuninit(&midistat_lock);
return 0;
exit1:
lockmgr(&m->lock, LK_RELEASE);
exit0:
lockmgr(&midistat_lock, LK_RELEASE);
if (retval)
MIDI_DEBUG(2, kprintf("midi_unload: failed\n"));
return retval;
}
extern int seq_modevent(module_t mod, int type, void *data);
static int
midi_modevent(module_t mod, int type, void *data)
{
int retval;
retval = 0;
switch (type) {
case MOD_LOAD:
retval = midi_load();
#if 0
if (retval == 0)
retval = seq_modevent(mod, type, data);
#endif
break;
case MOD_UNLOAD:
retval = midi_unload();
#if 0
if (retval == 0)
retval = seq_modevent(mod, type, data);
#endif
break;
default:
break;
}
return retval;
}
kobj_t
midimapper_addseq(void *arg1, int *unit, void **cookie)
{
unit = 0;
return (kobj_t)arg1;
}
int
midimapper_open(void *arg1, void **cookie)
{
int retval = 0;
struct snd_midi *m;
lockmgr(&midistat_lock, LK_EXCLUSIVE);
TAILQ_FOREACH(m, &midi_devs, link) {
retval++;
}
lockmgr(&midistat_lock, LK_RELEASE);
return retval;
}
int
midimapper_close(void *arg1, void *cookie)
{
return 0;
}
kobj_t
midimapper_fetch_synth(void *arg, void *cookie, int unit)
{
struct snd_midi *m;
int retval = 0;
lockmgr(&midistat_lock, LK_EXCLUSIVE);
TAILQ_FOREACH(m, &midi_devs, link) {
if (unit == retval) {
lockmgr(&midistat_lock, LK_RELEASE);
return (kobj_t)m->synth;
}
retval++;
}
lockmgr(&midistat_lock, LK_RELEASE);
return NULL;
}
DEV_MODULE(midi, midi_modevent, NULL);
MODULE_VERSION(midi, 1);