File: [local] / sys / dev / midi.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:09:38 2008 UTC (16 years, 4 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: midi.c,v 1.16 2006/12/21 02:28:47 krw Exp $ */
/*
* Copyright (c) 2003, 2004 Alexandre Ratchov
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* TODO
* - put the sequencer stuff in sequencer.c and sequencervar.h
* there is no reason to have it here. The sequencer
* driver need only to open the midi hw_if thus it does not
* need this driver
*/
#include "midi.h"
#include "sequencer.h"
#if NMIDI > 0
#include <sys/param.h>
#include <sys/fcntl.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/exec.h>
#include <sys/conf.h>
#include <sys/lkm.h>
#include <sys/proc.h>
#include <sys/poll.h>
#include <sys/kernel.h>
#include <sys/timeout.h>
#include <sys/vnode.h>
#include <sys/signalvar.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <dev/midi_if.h>
#include <dev/audio_if.h>
#include <dev/midivar.h>
int midiopen(dev_t, int, int, struct proc *);
int midiclose(dev_t, int, int, struct proc *);
int midiread(dev_t, struct uio *, int);
int midiwrite(dev_t, struct uio *, int);
int midipoll(dev_t, int, struct proc *);
int midiioctl(dev_t, u_long, caddr_t, int, struct proc *);
int midiprobe(struct device *, void *, void *);
void midiattach(struct device *, struct device *, void *);
int mididetach(struct device *, int);
int midiprint(void *, const char *);
void midi_iintr(void *, int);
void midi_ointr(void *);
void midi_out_start(struct midi_softc *);
void midi_out_stop(struct midi_softc *);
void midi_out_do(struct midi_softc *);
void midi_attach(struct midi_softc *, struct device *);
#if NSEQUENCER > 0
int midi_unit_count(void);
struct midi_hw_if *midi_get_hwif(int);
void midi_toevent(struct midi_softc *, int);
int midi_writebytes(int, u_char *, int);
void midiseq_in(struct midi_dev *, u_char *, int);
#endif
struct cfattach midi_ca = {
sizeof(struct midi_softc), midiprobe, midiattach, mididetach
};
struct cfdriver midi_cd = {
NULL, "midi", DV_DULL
};
void
midi_iintr(void *addr, int data)
{
struct midi_softc *sc = (struct midi_softc *)addr;
struct midi_buffer *mb = &sc->inbuf;
if (sc->isdying || !sc->isopen || !(sc->flags & FREAD)) return;
#if NSEQUENCER > 0
if (sc->seqopen) {
midi_toevent(sc, data);
return;
}
#endif
if (MIDIBUF_ISFULL(mb))
return; /* discard data */
if (MIDIBUF_ISEMPTY(mb)) {
if (sc->rchan) {
sc->rchan = 0;
wakeup(&sc->rchan);
}
selwakeup(&sc->rsel);
if (sc->async)
psignal(sc->async, SIGIO);
}
MIDIBUF_WRITE(mb, data);
}
int
midiread(dev_t dev, struct uio *uio, int ioflag)
{
struct midi_softc *sc = MIDI_DEV2SC(dev);
struct midi_buffer *mb = &sc->inbuf;
unsigned count;
int s, error;
if (!(sc->flags & FREAD))
return ENXIO;
/* if there is no data then sleep (unless IO_NDELAY flag is set) */
s = splaudio();
while(MIDIBUF_ISEMPTY(mb)) {
if (sc->isdying) {
splx(s);
return EIO;
}
if (ioflag & IO_NDELAY) {
splx(s);
return EWOULDBLOCK;
}
sc->rchan = 1;
error = tsleep(&sc->rchan, PWAIT|PCATCH, "mid_rd", 0);
if (error) {
splx(s);
return error;
}
}
/* at this stage, there is at least 1 byte */
while (uio->uio_resid > 0 && mb->used > 0) {
count = MIDIBUF_SIZE - mb->start;
if (count > mb->used)
count = mb->used;
if (count > uio->uio_resid)
count = uio->uio_resid;
error = uiomove(mb->data + mb->start, count, uio);
if (error) {
splx(s);
return error;
}
MIDIBUF_REMOVE(mb, count);
}
splx(s);
return 0;
}
void
midi_ointr(void *addr)
{
struct midi_softc *sc = (struct midi_softc *)addr;
struct midi_buffer *mb;
int s;
if (sc->isopen && !sc->isdying) {
#ifdef MIDI_DEBUG
if (!sc->isbusy) {
printf("midi_ointr: output should be busy\n");
}
#endif
mb = &sc->outbuf;
s = splaudio();
if (mb->used == 0)
midi_out_stop(sc);
else
midi_out_do(sc); /* restart output */
splx(s);
}
}
void
midi_out_start(struct midi_softc *sc)
{
if (!sc->isbusy) {
sc->isbusy = 1;
midi_out_do(sc);
}
}
void
midi_out_stop(struct midi_softc *sc)
{
sc->isbusy = 0;
if (sc->wchan) {
sc->wchan = 0;
wakeup(&sc->wchan);
}
selwakeup(&sc->wsel);
if (sc->async)
psignal(sc->async, SIGIO);
}
/*
* drain output buffer, must be called with
* interrupts disabled
*/
void
midi_out_do(struct midi_softc *sc)
{
struct midi_buffer *mb = &sc->outbuf;
unsigned i, max;
int error;
/*
* If output interrupts are not supported then we write MIDI_MAXWRITE
* bytes instead of 1, and then we wait sc->wait
*/
max = sc->props & MIDI_PROP_OUT_INTR ? 1 : MIDI_MAXWRITE;
for (i = max; i != 0;) {
if (mb->used == 0)
break;
error = sc->hw_if->output(sc->hw_hdl, mb->data[mb->start]);
/*
* 0 means that data is being sent, an interrupt will
* be generated when the interface becomes ready again
*
* EINPROGRESS means that data has been queued, but
* will not be sent immediately and thus will not
* generate interrupt, in this case we can send
* another byte. The flush() method can be called
* to force the transfer.
*
* EAGAIN means that data cannot be queued or sent;
* because the interface isn't ready. An interrupt
* will be generated once the interface is ready again
*
* any other (fatal) error code means that data couldn't
* be sent and was lost, interrupt will not be generated
*/
if (error == EINPROGRESS) {
MIDIBUF_REMOVE(mb, 1);
if (MIDIBUF_ISEMPTY(mb)) {
if (sc->hw_if->flush != NULL)
sc->hw_if->flush(sc->hw_hdl);
midi_out_stop(sc);
return;
}
} else if (error == 0) {
MIDIBUF_REMOVE(mb, 1);
i--;
} else if (error == EAGAIN) {
break;
} else {
MIDIBUF_INIT(mb);
midi_out_stop(sc);
return;
}
}
if (!(sc->props & MIDI_PROP_OUT_INTR)) {
if (MIDIBUF_ISEMPTY(mb))
midi_out_stop(sc);
else
timeout_add(&sc->timeo, sc->wait);
}
}
int
midiwrite(dev_t dev, struct uio *uio, int ioflag)
{
struct midi_softc *sc = MIDI_DEV2SC(dev);
struct midi_buffer *mb = &sc->outbuf;
unsigned count;
int s, error;
if (!(sc->flags & FWRITE))
return ENXIO;
if (sc->isdying)
return EIO;
/*
* If IO_NDELAY flag is set then check if there is enough room
* in the buffer to store at least one byte. If not then dont
* start the write process.
*/
if ((ioflag & IO_NDELAY) && MIDIBUF_ISFULL(mb) &&
(uio->uio_resid > 0))
return EWOULDBLOCK;
while (uio->uio_resid > 0) {
s = splaudio();
while (MIDIBUF_ISFULL(mb)) {
if (ioflag & IO_NDELAY) {
/*
* At this stage at least one byte is already
* moved so we do not return EWOULDBLOCK
*/
splx(s);
return 0;
}
sc->wchan = 1;
error = tsleep(&sc->wchan, PWAIT|PCATCH, "mid_wr", 0);
if (error) {
splx(s);
return error;
}
if (sc->isdying) {
splx(s);
return EIO;
}
}
count = MIDIBUF_SIZE - MIDIBUF_END(mb);
if (count > MIDIBUF_AVAIL(mb))
count = MIDIBUF_AVAIL(mb);
if (count > uio->uio_resid)
count = uio->uio_resid;
error = uiomove(mb->data + MIDIBUF_END(mb), count, uio);
if (error) {
splx(s);
return error;
}
mb->used += count;
midi_out_start(sc);
splx(s);
}
return 0;
}
int
midipoll(dev_t dev, int events, struct proc *p)
{
struct midi_softc *sc = MIDI_DEV2SC(dev);
int s, revents;
if (sc->isdying)
return POLLERR;
revents = 0;
s = splaudio();
if (events & (POLLIN | POLLRDNORM)) {
if (!MIDIBUF_ISEMPTY(&sc->inbuf))
revents |= events & (POLLIN | POLLRDNORM);
}
if (events & (POLLOUT | POLLWRNORM)) {
if (!MIDIBUF_ISFULL(&sc->outbuf))
revents |= events & (POLLOUT | POLLWRNORM);
}
if (revents == 0) {
if (events & (POLLIN | POLLRDNORM))
selrecord(p, &sc->rsel);
if (events & (POLLOUT | POLLWRNORM))
selrecord(p, &sc->wsel);
}
splx(s);
return (revents);
}
int
midiioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
{
struct midi_softc *sc = MIDI_DEV2SC(dev);
if (sc->isdying) return EIO;
switch(cmd) {
case FIONBIO:
/* All handled in the upper FS layer */
break;
case FIOASYNC:
if (*(int *)addr) {
if (sc->async) return EBUSY;
sc->async = p;
} else
sc->async = 0;
break;
default:
return ENOTTY;
break;
}
return 0;
}
int
midiopen(dev_t dev, int flags, int mode, struct proc *p)
{
struct midi_softc *sc;
int err;
if (MIDI_UNIT(dev) >= midi_cd.cd_ndevs)
return ENXIO;
sc = MIDI_DEV2SC(dev);
if (sc == NULL) /* there may be more units than devices */
return ENXIO;
if (sc->isdying)
return EIO;
if (sc->isopen)
return EBUSY;
MIDIBUF_INIT(&sc->inbuf);
MIDIBUF_INIT(&sc->outbuf);
sc->isbusy = 0;
sc->rchan = sc->wchan = 0;
sc->async = 0;
sc->flags = flags;
err = sc->hw_if->open(sc->hw_hdl, flags, midi_iintr, midi_ointr, sc);
if (err)
return err;
sc->isopen = 1;
#if NSEQUENCER > 0
sc->seq_md = 0;
sc->seqopen = 0;
sc->evstatus = 0xff;
#endif
return 0;
}
int
midiclose(dev_t dev, int fflag, int devtype, struct proc *p)
{
struct midi_softc *sc = MIDI_DEV2SC(dev);
struct midi_buffer *mb;
int error;
int s;
mb = &sc->outbuf;
if (!sc->isdying) {
/* start draining output buffer */
s = splaudio();
if (!MIDIBUF_ISEMPTY(mb))
midi_out_start(sc);
while (sc->isbusy) {
sc->wchan = 1;
error = tsleep(&sc->wchan, PWAIT|PCATCH, "mid_dr", 0);
if (error || sc->isdying)
break;
}
splx(s);
}
/*
* some hw_if->close() reset immediately the midi uart
* which flushes the internal buffer of the uart device,
* so we may lose some (important) data. To avoid this, we sleep 2*wait,
* which gives the time to the uart to drain its internal buffers.
*
* Note: we'd better sleep in the corresponding hw_if->close()
*/
tsleep(&sc->wchan, PWAIT|PCATCH, "mid_cl", 2 * sc->wait);
sc->hw_if->close(sc->hw_hdl);
sc->isopen = 0;
return 0;
}
int
midiprobe(struct device *parent, void *match, void *aux)
{
struct audio_attach_args *sa = aux;
return (sa != NULL && (sa->type == AUDIODEV_TYPE_MIDI) ? 1 : 0);
}
void
midi_attach(struct midi_softc *sc, struct device *parent)
{
struct midi_info mi;
sc->isdying = 0;
sc->wait = (hz * MIDI_MAXWRITE) / MIDI_RATE;
if (sc->wait == 0)
sc->wait = 1;
sc->hw_if->getinfo(sc->hw_hdl, &mi);
sc->props = mi.props;
sc->isopen = 0;
timeout_set(&sc->timeo, midi_ointr, sc);
printf(": <%s>\n", mi.name);
}
void
midiattach(struct device *parent, struct device *self, void *aux)
{
struct midi_softc *sc = (struct midi_softc *)self;
struct audio_attach_args *sa = (struct audio_attach_args *)aux;
struct midi_hw_if *hwif = sa->hwif;
void *hdl = sa->hdl;
#ifdef DIAGNOSTIC
if (hwif == 0 ||
hwif->open == 0 ||
hwif->close == 0 ||
hwif->output == 0 ||
hwif->getinfo == 0) {
printf("midi: missing method\n");
return;
}
#endif
sc->hw_if = hwif;
sc->hw_hdl = hdl;
midi_attach(sc, parent);
}
int
mididetach(struct device *self, int flags)
{
struct midi_softc *sc = (struct midi_softc *)self;
int maj, mn;
sc->isdying = 1;
if (sc->wchan) {
sc->wchan = 0;
wakeup(&sc->wchan);
}
if (sc->rchan) {
sc->rchan = 0;
wakeup(&sc->rchan);
}
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++)
if (cdevsw[maj].d_open == midiopen)
break;
/* Nuke the vnodes for any open instances (calls close). */
mn = self->dv_unit;
vdevgone(maj, mn, mn, VCHR);
return 0;
}
int
midiprint(void *aux, const char *pnp)
{
if (pnp)
printf("midi at %s", pnp);
return (UNCONF);
}
void
midi_getinfo(dev_t dev, struct midi_info *mi)
{
struct midi_softc *sc = MIDI_DEV2SC(dev);
if (MIDI_UNIT(dev) >= midi_cd.cd_ndevs || sc == NULL || sc->isdying) {
mi->name = "unconfigured";
mi->props = 0;
return;
}
sc->hw_if->getinfo(sc->hw_hdl, mi);
}
struct device *
midi_attach_mi(struct midi_hw_if *hwif, void *hdl, struct device *dev)
{
struct audio_attach_args arg;
arg.type = AUDIODEV_TYPE_MIDI;
arg.hwif = hwif;
arg.hdl = hdl;
return config_found(dev, &arg, midiprint);
}
int
midi_unit_count(void)
{
return midi_cd.cd_ndevs;
}
#if NSEQUENCER > 0
#define MIDI_EVLEN(status) (midi_evlen[((status) >> 4) & 7])
unsigned midi_evlen[] = { 2, 2, 2, 2, 1, 1, 2 };
void
midi_toevent(struct midi_softc *sc, int data)
{
unsigned char mesg[3];
if (data >= 0xf8) { /* is it a realtime message ? */
switch(data) {
case 0xf8: /* midi timer tic */
case 0xfa: /* midi timer start */
case 0xfb: /* midi timer continue (after stop) */
case 0xfc: /* midi timer stop */
mesg[0] = data;
midiseq_in(sc->seq_md, mesg, 1);
break;
default:
break;
}
} else if (data >= 0x80) { /* is it a common or voice message ? */
sc->evstatus = data;
sc->evindex = 0;
} else { /* else it is a data byte */
/* strip common messages and bogus data */
if (sc->evstatus >= 0xf0 || sc->evstatus < 0x80)
return;
sc->evdata[sc->evindex++] = data;
if (sc->evindex == MIDI_EVLEN(sc->evstatus)) {
sc->evindex = 0;
mesg[0] = sc->evstatus;
mesg[1] = sc->evdata[0];
mesg[2] = sc->evdata[1];
midiseq_in(sc->seq_md, mesg, 1 + MIDI_EVLEN(sc->evstatus));
}
}
}
int
midi_writebytes(int unit, unsigned char *mesg, int mesglen)
{
struct midi_softc *sc = midi_cd.cd_devs[unit];
struct midi_buffer *mb = &sc->outbuf;
unsigned count;
int s;
s = splaudio();
if (mesglen > MIDIBUF_AVAIL(mb)) {
splx(s);
return EWOULDBLOCK;
}
while (mesglen > 0) {
count = MIDIBUF_SIZE - MIDIBUF_END(mb);
if (count > MIDIBUF_AVAIL(mb)) count = MIDIBUF_AVAIL(mb);
if (count > mesglen) count = mesglen;
bcopy(mesg, mb->data + MIDIBUF_END(mb), count);
mb->used += count;
mesg += count;
mesglen -= count;
midi_out_start(sc);
}
splx(s);
return 0;
}
#endif /* NSEQUENCER > 0 */
#endif /* NMIDI > 0 */