home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.ee.lbl.gov
/
2014.05.ftp.ee.lbl.gov.tar
/
ftp.ee.lbl.gov
/
bmd-1.0beta.tar.Z
/
bmd-1.0beta.tar
/
bmd-1.0beta
/
sundev
/
bmd.c
next >
Wrap
C/C++ Source or Header
|
1991-08-27
|
23KB
|
1,166 lines
/*
* Copyright (c) 1990,1991 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Lawrence Berkeley Laboratory,
* Berkeley, CA. The name of the University may not be used to
* endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "bmd.h"
#if NBMD > 0
#ifndef lint
static char rcsid[] =
"@(#) $Header: bmd.c,v 1.10 91/08/27 00:36:27 mccanne Exp $";
#endif
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/acct.h>
#include <sys/proc.h>
#include <sundev/midi.h>
#include <sundev/bmdvar.h>
#include <machine/midiclock.h>
#include <machine/intreg.h>
#ifndef NBMDFILES
#define NBMDFILES 4
#endif
#define NMDQ 512
#define BMD_MAXNBUF 128
#define BMD_MAXRBUF (BMD_MAXNBUF+8)
#define BMD_WHI 64
#define BMD_WLO 16
struct mdq *mdqalloc();
void mdqfreeq();
void mdqfree();
void mdqinit();
/*
* An interface structure for each hardware port.
*/
#define NPORTS 2
struct midi_softc midi_softc[NPORTS];
struct midid midid[NBMDFILES];
static struct midid *midisoft;
/*
* Forward declarations for the interrupt handlers.
*/
int midioutput();
void midiinput();
/*
* The length of fixed length midi messages.
* Also, an input code for the dfa. There are five classes:
*
* (0) data
* (1) one byte msgs
* (2) two byte msgs
* (3) three byte msgs
* (4) sysexs
*/
u_char midi_msglen[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x80 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x90 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xa0 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xb0 */
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 */
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 */
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 */
0xff, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xf0 */
};
int bmdsoftintr();
static void bmdwakeup();
static int bmdsleep();
void
bmdattach(unit)
int unit;
{
register int i;
register struct midi_softc *sc;
if (unit >= NBMD) {
printf("bmdattach: unit %d ignored\n", unit);
return;
}
printf("bmd%d: attached\n", unit);
sc = &midi_softc[unit];
bzero((char *)sc, sizeof(*sc));
sc->sc_p = &sc->sc_sp;
sc->sc_f = &sc->sc_sf;
sc->sc_unit = unit;
/*
* Return if not first time.
* Rest of initialization is one time only.
*/
if (unit > 0)
return;
/*
* Initialize free list.
*/
mdqinit();
/*
* Mark all descriptors free.
*/
for (i = 0; i < NBMDFILES; ++i) {
midid[i].md_nxtr = &midid[i];
midid[i].md_nxtw = &midid[i];
}
addintr(4, bmdsoftintr, "bmd", 0);
}
static int
port_attach(md, port)
struct midid *md;
int port;
{
int s;
register struct midi_softc *sc;
extern void *msl_init();
if ((unsigned)port >= NPORTS)
return EINVAL;
sc = &midi_softc[port];
s = splmidi();
if (md->md_flag & FWRITE) {
md->md_nxtw = sc->sc_writers;
sc->sc_writers = md;
}
md->md_sc = sc;
if (md->md_flag & FREAD) {
md->md_nxtr = sc->sc_readers;
sc->sc_readers = md;
}
if (sc->sc_refcnt++ == 0) {
splx(s);
sc->sc_msl = msl_init(port, (caddr_t)sc);
sc->sc_clkrate = midiclk_start();
sc->sc_stat = -1;
} else
splx(s);
md->md_tz = midiclk.mt_now;
return 0;
}
/*
* Open a midi file.
*/
int
bmdopen(dev, flag)
dev_t dev;
int flag;
{
register int unit;
register struct midid *md;
unit = minor(dev);
if ((unsigned)unit >= NBMDFILES)
return ENXIO;
md = &midid[unit];
if (md->md_nxtr != md)
return EBUSY;
bzero((char *)md, sizeof *md);
md->md_flag = flag;
md->md_filter = BMD_FILTER;
md->md_nxti = md;
md->md_wlo = BMD_WLO;
md->md_nbuf = 1;
return 0;
}
static void
bmdrmr(p, md)
register struct midid **p, *md;
{
for (; *p != md; p = &(*p)->md_nxtr)
if (*p == 0)
panic("bmdrmr");
*p = (*p)->md_nxtr;
}
static void
bmdrmw(p, md)
register struct midid **p, *md;
{
for (; *p != md; p = &(*p)->md_nxtw)
if (*p == 0)
panic("bmdrmw");
*p = (*p)->md_nxtw;
}
static void
bmdrmi(p, md)
register struct midid **p, *md;
{
for (; *p != md; p = &(*p)->md_nxti)
if (*p == 0)
panic("bmdrmr");
*p = (*p)->md_nxti;
}
/*
* Close a midi file.
*/
/* ARGSUSED */
bmdclose(dev, flag)
dev_t dev;
int flag;
{
register struct midi_softc *sc;
register struct midid *md;
register int s, drain = 0;
register int unit = minor(dev);
md = &midid[unit];
sc = md->md_sc;
if (sc == 0)
goto out;
s = splmidi();
if (md->md_flag & FREAD) {
mdqfreeq(md->md_rh, md->md_rt);
bmdrmr(&sc->sc_readers, md);
}
if (md->md_flag & FWRITE) {
/*
* Block until output drained. Delay wakeup until all
* output is gone by setting wlo to 0. But block only if
* we're not:
* 1. in non-blocking mode, or
* 2. paused.
*
* We set drain to true if we want to additionally wait
* for the serial driver to drain.
*/
md->md_wlo = 0;
if (!(md->md_nbio || midiclk.mt_pause)) {
drain = 1;
while (md->md_wcnt > 0)
if (bmdsleep(&md->md_wchan)) {
drain = 0;
break;
}
}
if (md->md_wh != 0)
mdqfreeq(md->md_wh, md->md_wt->mq_tail);
bmdrmw(&sc->sc_writers, md);
}
if (md->md_nxti != md) {
bmdrmi(&midisoft, md);
md->md_nxti = md;
}
/*
* Deallocate clock and serial line resources.
*/
if (--sc->sc_refcnt <= 0) {
midiclk_stop();
msl_disconnect(sc->sc_msl, drain);
}
splx(s);
/*
* Mark descriptor free.
*/
out:
md->md_nxtr = md;
}
/*
* Read data from the midi interface. If in non-blocking mode, return
* if no data is available. If in immediate mode, block until next byte
* arrives. (XXX Maybe we should block until next midi *message* arrives.)
* Otherwise, block until data arrives.
*/
bmdread(dev, uio)
dev_t dev;
register struct uio *uio;
{
register int s, error, len;
register struct midid *md = &midid[minor(dev)];
register struct midi_softc *sc = md->md_sc;
register struct mdq *q;
if (sc == 0)
return EIO;
s = splmidi();
/*
* Block until data arrives, if not already present.
*/
while (md->md_rcnt < md->md_nbuf) {
if (md->md_nbio) {
/* non-blocking */
splx(s);
return 0;
}
if (bmdsleep(&md->md_rchan)) {
splx(s);
return EINTR;
}
}
error = 0;
while ((q = md->md_rh) != 0) {
len = q->mq_len + sizeof(q->mq_time);
len = BMDALIGN(len);
if (len > uio->uio_resid)
break;
md->md_rh = q->mq_next;
if (md->md_rh == 0)
md->md_rt = 0;
--md->md_rcnt;
q->mq_time -= md->md_tz;
splx(s);
error = uiomove((caddr_t)&q->mq_time, len, UIO_READ, uio);
s = splmidi();
mdqfree(q);
if (error != 0)
break;
}
if (md->md_sigioproc != 0)
md->md_sigio.want = 1;
splx(s);
return error;
}
/*
* Read a sysex from uio into q and set q's length.
*/
static int
bmdsysex(q, uio)
struct mdq *q;
struct uio *uio;
{
register u_char *cp = q->mq_data, *ep;
register int n, error;
cp = &q->mq_data[4];
ep = &cp[sizeof(q->mq_data) - 8];
while (cp <= ep) {
if (uio->uio_resid < 8)
return EINVAL;
error = uiomove(cp, 8, UIO_WRITE, uio);
if (error != 0)
return error;
n = 8;
while (--n >= 0)
if (*cp++ == 0xf7) {
q->mq_len = cp - q->mq_data;
return 0;
}
}
return EINVAL;
}
static struct mdq *
mdqwrite(uio, errp)
register struct uio *uio;
register int *errp;
{
register int error, s;
register struct mdq *q;
s = splmidi();
q = mdqalloc();
splx(s);
if (q == 0) {
*errp = ENOBUFS;
return 0;
}
error = uiomove((caddr_t)&q->mq_time, 8, UIO_WRITE, uio);
if (error)
goto bad;
if (q->mq_data[0] == 0xf0 && (error = bmdsysex(q, uio)) != 0)
goto bad;
else
q->mq_len = midi_msglen[q->mq_data[0]];
q->mq_cp = q->mq_data;
q->mq_qlen = 1;
return q;
bad:
s = splmidi();
mdqfree(q);
splx(s);
*errp = error;
return 0;
}
static void
mdqmerge(md, head, tail)
register struct midid *md;
register struct mdq *head;
register struct mdq *tail;
{
register struct mdq **p, *n, *hh, *tt;
for (p = &md->md_wh; head != 0; head = n) {
while (*p != 0 && head->mq_time > (*p)->mq_time)
p = &(*p)->mq_tail->mq_next;
hh = *p;
if (hh == 0) {
*p = head;
md->md_wt = tail;
return;
}
n = head->mq_tail->mq_next;
if (head->mq_time == hh->mq_time) {
tt = hh->mq_tail;
hh->mq_qlen += head->mq_qlen;
head->mq_tail->mq_next = tt->mq_next;
tt->mq_next = head;
hh->mq_tail = head->mq_tail;
} else {
head->mq_tail->mq_next = hh;
*p = head;
}
}
if (tail->mq_time > md->md_wt->mq_time)
md->md_wt = tail;
}
/*
* Write to a midi file.
*/
bmdwrite(dev, uio)
dev_t dev;
register struct uio *uio;
{
register struct midid *md = &midid[minor(dev)];
register struct midi_softc *sc = md->md_sc;
register struct mdq *head, *tail, *q, *wt;
register int s, n;
int error;
if (sc == 0)
return EIO;
if (uio->uio_resid == 0)
return 0;
/*
* Build a list of mdqs (we assume timestamps are ordered in
* a given write call). If time of first mdq is later than oldest
* mdq already buffered (common case), splice list onto end.
* Otherwise, merge the two lists. Update the work time
* if necessary.
*/
tail = 0;
n = 0;
error = 0;
while (uio->uio_resid >= sizeof(struct midi_msg)) {
s = splmidi();
if (md->md_wcnt >= BMD_WHI) {
if (md->md_nbio) {
splx(s);
break;
}
while (md->md_wcnt > md->md_wlo) {
if (bmdsleep(&md->md_wchan)) {
splx(s);
error = EINTR;
goto out;
}
}
}
splx(s);
q = mdqwrite(uio, &error);
if (q == 0)
break;
q->mq_time += md->md_tz;
if (tail == 0) {
head = tail = q;
tail->mq_tail = tail;
continue;
}
tail->mq_tail->mq_next = q;
if (q->mq_time == tail->mq_time) {
++tail->mq_qlen;
tail->mq_tail = q;
} else {
n += tail->mq_qlen;
tail = q;
q->mq_tail = q;
}
}
out:
if (tail == 0)
return error;
n += tail->mq_qlen;
s = splmidi();
md->md_wcnt += n;
tail->mq_tail->mq_next = 0;
wt = md->md_wt;
if (wt == 0) {
md->md_wh = head;
md->md_wt = tail;
if (BMD_TLT(head->mq_time, midiclk.mt_work))
midiclk.mt_work = head->mq_time;
} else if (BMD_TGT(head->mq_time, wt->mq_time)) {
wt->mq_tail->mq_next = head;
md->md_wt = tail;
} else if (head->mq_time == wt->mq_time) {
wt->mq_tail->mq_next = head;
wt->mq_qlen += head->mq_qlen;
wt->mq_tail = head->mq_tail;
if (tail != head)
md->md_wt = tail;
} else {
/* XXX this shouldn't be done at splmidi */
mdqmerge(md, head, tail);
head = md->md_wh;
if (BMD_TLT(head->mq_time, midiclk.mt_work))
midiclk.mt_work = head->mq_time;
}
splx(s);
return 0;
}
/*
* Select support.
*/
int
bmdselect(dev, flag)
register dev_t dev;
int flag;
{
struct midid *md = &midid[minor(dev)];
register struct proc *p;
register int s;
if (md->md_sc == 0)
return EIO;
if (flag == FREAD) {
if (md->md_rcnt >= md->md_nbuf)
/* Data ready. */
return 1;
/*
* No data ready. If there's already a select() waiting on
* this minor device then this is a collision. This shouldn't
* happen because minors really should not be shared, but if
* a process forks while one of these is open, it is possible
* that both processes could select on the same descriptor.
*/
s = splmidi();
p = (struct proc *)md->md_rsel.want;
if (p != 0 && p->p_wchan == (caddr_t)&selwait)
md->md_rcol = 1;
else
md->md_rsel.want = (int)u.u_procp;
splx(s);
} else {
if (md->md_wcnt < BMD_WHI)
return 1;
s = splmidi();
p = (struct proc *)md->md_wsel.want;
if (p != 0 && p->p_wchan == (caddr_t)&selwait)
md->md_wcol = 1;
else
md->md_wsel.want = (int)u.u_procp;
splx(s);
}
return 0;
}
/*
* Flush the input buffer of md.
*/
bmd_iflush(md)
struct midid *md;
{
int s = splmidi();
struct mdq *q = md->md_rh;
if (q != 0) {
mdqfreeq(q, md->md_rt);
md->md_rh = 0;
md->md_rt = 0;
md->md_rcnt = 0;
}
splx(s);
}
/*
* Flush the output buffer of the interface that md is listening on.
* Called at "low" ipl.
*/
bmd_oflush(md)
struct midid *md;
{
register struct mdq *q, *n;
register int s;
s = splmidi();
q = md->md_wh;
md->md_wh = 0;
md->md_wt = 0;
md->md_wcnt = 0;
splx(s);
while (q != 0) {
s = splmidi();
n = q->mq_next;
mdqfree(q);
splx(s);
q = n;
}
/* msl_flush() */
}
int
bmd_rate(sc, newrate)
register struct midi_softc *sc;
register u_int newrate;
{
register int s;
/*
* Only allow rates from 320us (max midi rate) to 1s.
*/
if (newrate < 320 || newrate > 1000000)
return EINVAL;
s = splmidi();
sc->sc_clkrate = newrate;
midiclk_setrate(newrate);
splx(s);
return 0;
}
/*
* Process a midi ioctl.
*/
/* ARGSUSED */
bmdioctl(dev, cmd, addr, flag)
dev_t dev;
int cmd;
caddr_t addr;
int flag;
{
int error;
struct midid *md = &midid[minor(dev)];
if (cmd == BMDATTACH)
return port_attach(md, *(u_int *)addr);
if (md->md_sc == 0)
return EINVAL;
error = 0;
switch (cmd) {
case FIONREAD:
*(int *)addr = md->md_rcnt;
break;
case FIONBIO:
md->md_nbio = (*(int *)addr != 0) ? 1 : 0;
break;
case FIOASYNC:
if (*(int *)addr != 0) {
if (md->md_sigioproc == 0) {
md->md_sigioproc = u.u_procp;
md->md_sigio.want = 1;
} else
error = EINVAL;
} else {
md->md_sigioproc = 0;
md->md_sigio.want = 0;
}
break;
case BMDGTIME:
*(u_long *)addr = midiclk.mt_now - md->md_tz;
break;
case BMDSTIME:
midiclk_pause();
md->md_tz = midiclk.mt_now - *(u_long *)addr;
if (md->md_flag & FREAD)
bmd_iflush(md);
if (md->md_flag & FWRITE)
bmd_oflush(md);
midiclk_resume();
break;
case BMDFLUSH:
if (md->md_flag & FREAD)
bmd_iflush(md);
if (md->md_flag & FWRITE)
bmd_oflush(md);
break;
case BMDRATE:
if (*(u_int *)addr == 0)
*(u_int *)addr = md->md_sc->sc_clkrate;
else
error = bmd_rate(md->md_sc, *(u_int *)addr);
break;
case BMDFILTER:
md->md_filter = *(u_long *)addr;
bmd_iflush(md);
break;
case BMDPAUSE:
if (!md->md_pause) {
md->md_pause = 1;
midiclk_pause();
} else
error = EINVAL;
break;
case BMDRESUME:
if (md->md_pause) {
md->md_pause = 0;
midiclk_resume();
} else
error = EINVAL;
break;
case BMDNWRITE:
*(int *)addr = BMD_WHI - md->md_wcnt;
break;
case BMDECHO:
if (*(u_int *)addr & 0xf > 15)
md->md_echo = 0;
else {
md->md_echan = *(u_int *)addr & 0xf;
md->md_echo = 1;
}
break;
case BMDNBUF:
if (*(u_int *)addr > BMD_MAXNBUF)
*(u_int *)addr = BMD_MAXNBUF;
md->md_nbuf = *(u_int *)addr;
break;
case BMDWLO:
if (*(u_int *)addr < BMD_WLO)
*(u_int *)addr = BMD_WLO;
else if (*(u_int *)addr > BMD_WHI / 2)
*(u_int *)addr = BMD_WHI / 2;
md->md_wlo = *(u_int *)addr;
break;
default:
error = EINVAL;
break;
}
return error;
}
static struct mdq *
mdqcopy(q)
register struct mdq *q;
{
register struct mdq *p;
p = mdqalloc();
if (p != 0) {
p->mq_time = q->mq_time;
p->mq_len = q->mq_len;
bcopy((char *)q->mq_data, (char *)p->mq_data, p->mq_len);
}
return p;
}
static void
bmd_echo(sc, q, chan)
register struct midi_softc *sc;
register struct mdq *q;
register int chan;
{
register struct mdq *p;
if (q->mq_data[0] >= 0xf0)
return;
p = mdqcopy(q);
if (p == 0)
return;
p->mq_data[0] = (p->mq_data[0] & 0xf0) | chan;
/*
* Put straight onto pending queue.
*/
p->mq_cp = p->mq_data;
msl_output(sc->sc_msl, p, p);
}
static int
bmdfilter(filter, c)
int filter;
int c;
{
if (filter & (1 << (c >> 4))) {
if ((c & 0xf0) != 0xf0)
return 1;
return (filter & (0x10000 << (c & 0xf)));
}
return 0;
}
/*
* This doesn't quite work. We rely on eox to terminate sysex.
*/
static struct mdq *
bmdparse(sc, c)
register struct midi_softc *sc;
register int c;
{
register int s;
register struct mdq *p;
register struct mdq *f;
/*
* Use the f buffer for real time bytes, which don't
* affect any aspect of the stream.
*/
if (c >= 0xf8) {
p = sc->sc_f;
p->mq_data[0] = c;
p->mq_len = 1;
p->mq_time = midiclk.mt_now;
return p;
}
p = sc->sc_p;
if (c < 0x80) {
/*
* A data byte. If we don't have a running status,
* ignore the byte. If we need to timestamp the
* arriving byte, do it.
*/
s = sc->sc_stat;
if (s < 0)
return 0;
else if (s > 0) {
p->mq_time = midiclk.mt_now;
p->mq_data[0] = s;
p->mq_len = 1;
sc->sc_stat = 0;
} else
s = p->mq_data[0];
if (p->mq_len < sizeof(p->mq_data)) {
p->mq_data[p->mq_len++] = c;
if (p->mq_len >= midi_msglen[s]) {
sc->sc_stat = s;
return p;
}
}
return 0;
} else if (c == 0xf7) {
sc->sc_stat = -1;
if (p->mq_data[0] != 0xf0)
return 0;
if (p->mq_len < sizeof(p->mq_data))
p->mq_data[p->mq_len++] = c;
return p;
} else if (p->mq_data[0] == 0xf0 && sc->sc_stat == 0) {
/*
* Terminating a sysex with a status byte.
* Terminate the sysex (if room allows), and set
* up the next command. If we are terminating
* with a one byte status message, it is dropped.
*/
if (p->mq_len < sizeof(p->mq_data))
p->mq_data[p->mq_len++] = 0xf7;
f = sc->sc_f;
sc->sc_f = p;
sc->sc_p = f;
f->mq_time = midiclk.mt_now;
f->mq_data[0] = c;
f->mq_len = 1;
sc->sc_stat = 0;
return p;
} else {
p->mq_time = midiclk.mt_now;
p->mq_data[0] = c;
p->mq_len = 1;
sc->sc_stat = 0;
return (midi_msglen[c] == 1) ? p : 0;
}
/* NOTREACHED */
}
/*
* Process byte received by serial driver.
* Called at splmidi.
*/
void
bmdinput(csc, c)
caddr_t csc;
register int c;
{
register struct midi_softc *sc = (struct midi_softc *)csc;
register struct midid *md;
register struct mdq *p, *q;
q = bmdparse(sc, c);
if (q == 0)
return;
for (md = sc->sc_readers; md != 0; md = md->md_nxtr) {
if (bmdfilter(md->md_filter, q->mq_data[0]))
continue;
if (md->md_echo)
bmd_echo(sc, q, md->md_echan);
if (md->md_rcnt >= BMD_MAXRBUF) {
++md->md_idrops;
continue;
}
p = mdqcopy(q);
if (p == 0) {
++md->md_idrops;
continue;
}
if (md->md_rt == 0)
md->md_rh = md->md_rt = p;
else {
md->md_rt->mq_next = p;
md->md_rt = p;
}
p->mq_next = 0;
if (++md->md_rcnt >= md->md_nbuf) {
if (md->md_rchan.want)
bmdwakeup(md, &md->md_rchan);
if (md->md_sigio.want)
bmdwakeup(md, &md->md_sigio);
if (md->md_rsel.want)
bmdwakeup(md, &md->md_rsel);
}
}
}
/*
* Called by level 14 interrupt handler. The trap handler noted
* that we have work to do, so do it, and schedule the next work interval.
*/
int
bmdupdate()
{
register u_long t = midiclk.mt_now;
register u_long work = ~0;
register struct midid *md;
register struct mdq *head, *tail, *next;
register int i;
register struct midi_softc *sc = midi_softc;
/*
* Bring each port up to current time.
*/
for (i = NPORTS; --i >= 0; ++sc) {
for (md = sc->sc_writers; md != 0; md = md->md_nxtw) {
head = md->md_wh;
if (head != 0) {
if (BMD_TGT(head->mq_time, t)) {
if (BMD_TLT(head->mq_time, work))
work = head->mq_time;
continue;
}
tail = head->mq_tail;
next = tail->mq_next;
md->md_wcnt -= head->mq_qlen;
msl_output(sc->sc_msl, head, tail);
md->md_wh = next;
if (next == 0)
md->md_wt = 0;
else if (BMD_TLT(next->mq_time, work))
work = next->mq_time;
/*
* Wake up anything waiting for output
* to happen.
*/
if (md->md_wcnt <= md->md_wlo) {
if (md->md_wchan.want)
bmdwakeup(md, &md->md_wchan);
if (md->md_wsel.want)
bmdwakeup(md, &md->md_wsel);
}
}
}
}
midiclk.mt_work = work;
}
/*
* Schedule a software interrupt for a bmd descriptor. We must do various
* tasks at ipl lower than splmidi() because system data structures (i.e.,
* the process queues) are serialized at such a lower level.
* md is the descriptor and p is a pointer to a flag in the descriptor
* that indicates the action.
* If md points to itself (via md_nxti) then it is not in midisoft
* and must be added.
*/
static void
bmdwakeup(md, p)
struct midid *md;
struct sleepchan *p;
{
if (md->md_nxti == md) {
md->md_nxti = midisoft;
midisoft = md;
}
p->have = p->want;
p->want = 0;
set_intreg(IR_SOFT_INT4, 1);
}
static int
bmdsleep(p)
register struct sleepchan *p;
{
register int st;
p->want = 1;
st = sleep((caddr_t)p, (PZERO+1)|PCATCH);
p->want = 0;
return st;
}
int
bmdsoftintr()
{
register int s, st;
register struct midid *md;
s = splmidi();
st = (int)midisoft;
while ((md = midisoft) != 0) {
midisoft = md->md_nxti;
md->md_nxti = md;
splx(s);
if (md->md_sigio.have) {
psignal(md->md_sigioproc, SIGIO);
md->md_sigio.have = 0;
}
if (md->md_rsel.have) {
selwakeup((struct proc *)md->md_rsel.have,
md->md_rcol);
md->md_rsel.have = 0;
md->md_rcol = 0;
}
if (md->md_wsel.have) {
selwakeup((struct proc *)md->md_wsel.have,
md->md_wcol);
md->md_wsel.have = 0;
md->md_wcol = 0;
}
if (md->md_wchan.have) {
wakeup((caddr_t)&md->md_wchan);
md->md_wchan.have = 0;
}
if (md->md_rchan.have) {
wakeup((caddr_t)&md->md_rchan);
md->md_rchan.have = 0;
}
(void)splmidi();
}
splx(s);
return st;
}
/*
* Message allocator.
* XXX Should be replaced by kernel malloc.
* Maintain free list, returning to malloc pool after last close.
*/
static struct mdq mpool[NMDQ];
static struct mdq *mdqfreelist;
struct mdq *
mdqalloc()
{
register struct mdq *q;
q = mdqfreelist;
if (q != 0)
mdqfreelist = q->mq_next;
return q;
}
void
mdqinit()
{
register int i;
register struct mdq *m;
mdqfreelist = m = mpool;
for (i = NMDQ; --i > 0; ++m)
m->mq_next = m + 1;
m->mq_next = 0;
}
void
mdqfree(q)
register struct mdq *q;
{
q->mq_next = mdqfreelist;
mdqfreelist = q;
}
void
mdqfreeq(head, tail)
register struct mdq *head;
register struct mdq *tail;
{
if (head != 0) {
tail->mq_next = mdqfreelist;
mdqfreelist = head;
}
}
#endif