87a636ccb0
If you ever want to run midi(4) out of the giant lock, uncomment MIDI_OUTOFGIANT in midi.h. Confirmed to work for csamidi with WITNESS and INVARIANTS. - midi_info, midi_open and seq_info are now tailqs, allowing arbitrary numbers of devices to be configured. - Do not send an active sensing message to reset midi modules. - Clone /dev/sequencer*. /dev/sequencer0 and /dev/sequencer are generated upon initialization.
502 lines
13 KiB
C
502 lines
13 KiB
C
/*
|
|
* Copyright (C) 1999 Seigo Tanimura
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or
|
|
* without modification, are permitted provided that the following
|
|
* conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS
|
|
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* This file implements a midi event/message queue. A midi
|
|
* event/message queue holds midi events and messages to
|
|
* transmit to or received from a midi interface.
|
|
*/
|
|
|
|
#include <dev/sound/midi/midi.h>
|
|
|
|
/* Some macros to handle the queue. */
|
|
#define DATA_AVAIL(dbuf) ((dbuf)->rl)
|
|
#define SPACE_AVAIL(dbuf) ((dbuf)->fl)
|
|
|
|
static void queuerawdata(midi_dbuf *dbuf, char *data, int len);
|
|
static void queueuiodata(midi_dbuf *dbuf, struct uio *buf, int len);
|
|
static void dequeuerawdata(midi_dbuf *dbuf, char *data, int len);
|
|
static void undequeuerawdata(midi_dbuf *dbuf, char *data, int len);
|
|
static void copyrawdata(midi_dbuf *dbuf, char *data, int len);
|
|
static void dequeueuiodata(midi_dbuf *dbuf, struct uio *buf, int len);
|
|
|
|
/*
|
|
* Here are the functions to interact to the midi device drivers.
|
|
* These are called from midi device driver functions under sys/i386/isa/snd.
|
|
*/
|
|
|
|
int
|
|
midibuf_init(midi_dbuf *dbuf)
|
|
{
|
|
if (dbuf->buf != NULL)
|
|
free(dbuf->buf, M_DEVBUF);
|
|
dbuf->buf = malloc(MIDI_BUFFSIZE, M_DEVBUF, M_WAITOK | M_ZERO);
|
|
|
|
return (midibuf_clear(dbuf));
|
|
}
|
|
|
|
int
|
|
midibuf_destroy(midi_dbuf *dbuf)
|
|
{
|
|
if (dbuf->buf != NULL)
|
|
free(dbuf->buf, M_DEVBUF);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
midibuf_clear(midi_dbuf *dbuf)
|
|
{
|
|
bzero(dbuf->buf, MIDI_BUFFSIZE);
|
|
dbuf->bufsize = MIDI_BUFFSIZE;
|
|
dbuf->rp = dbuf->fp = 0;
|
|
dbuf->dl = 0;
|
|
dbuf->rl = 0;
|
|
dbuf->fl = dbuf->bufsize;
|
|
dbuf->int_count = 0;
|
|
dbuf->chan = 0;
|
|
/*dbuf->unit_size = 1;*/ /* The drivers are responsible. */
|
|
bzero(&dbuf->sel, sizeof(dbuf->sel));
|
|
dbuf->total = 0;
|
|
dbuf->prev_total = 0;
|
|
dbuf->blocksize = dbuf->bufsize / 4;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* The sequencer calls this function to queue data. */
|
|
int
|
|
midibuf_seqwrite(midi_dbuf *dbuf, u_char* data, int len, struct mtx *m)
|
|
{
|
|
int i, lwrt, lwritten;
|
|
|
|
/* Is this a real queue? */
|
|
if (dbuf == (midi_dbuf *)NULL)
|
|
return (0);
|
|
|
|
lwritten = 0;
|
|
/* Write down every single byte. */
|
|
while (len > 0) {
|
|
/* Find out the number of bytes to write. */
|
|
lwrt = SPACE_AVAIL(dbuf);
|
|
if (lwrt > len)
|
|
lwrt = len;
|
|
if (lwrt > 0) {
|
|
/* We can write some now. Queue the data. */
|
|
queuerawdata(dbuf, data, lwrt);
|
|
|
|
lwritten += lwrt;
|
|
len -= lwrt;
|
|
data += lwrt;
|
|
}
|
|
|
|
/* Have we got still more data to write? */
|
|
if (len > 0) {
|
|
/* Yes, sleep until we have enough space. */
|
|
i = msleep((void *)&dbuf->tsleep_out, m, PRIBIO | PCATCH, "mbsqwt", 0);
|
|
if (i == EINTR || i == ERESTART)
|
|
return (-i);
|
|
}
|
|
}
|
|
|
|
return (lwritten);
|
|
}
|
|
|
|
/* sndwrite calls this function to queue data. */
|
|
int
|
|
midibuf_uiowrite(midi_dbuf *dbuf, struct uio *buf, int len, struct mtx *m)
|
|
{
|
|
int i, lwrt, lwritten;
|
|
|
|
/* Is this a real queue? */
|
|
if (dbuf == (midi_dbuf *)NULL)
|
|
return (0);
|
|
|
|
lwritten = 0;
|
|
/* Write down every single byte. */
|
|
while (len > 0) {
|
|
/* Find out the number of bytes to write. */
|
|
lwrt = SPACE_AVAIL(dbuf);
|
|
if (lwrt > len)
|
|
lwrt = len;
|
|
if (lwrt > 0) {
|
|
/* We can write some now. Queue the data. */
|
|
queueuiodata(dbuf, buf, lwrt);
|
|
|
|
lwritten += lwrt;
|
|
len -= lwrt;
|
|
}
|
|
|
|
/* Have we got still more data to write? */
|
|
if (len > 0) {
|
|
/* Yes, sleep until we have enough space. */
|
|
i = msleep(&dbuf->tsleep_out, m, PRIBIO | PCATCH, "mbuiwt", 0);
|
|
if (i == EINTR || i == ERESTART)
|
|
return (-i);
|
|
}
|
|
}
|
|
|
|
return (lwritten);
|
|
}
|
|
|
|
int
|
|
midibuf_output_intr(midi_dbuf *dbuf, u_char *data, int len)
|
|
{
|
|
int lrd;
|
|
|
|
/* Is this a real queue? */
|
|
if (dbuf == (midi_dbuf *)NULL)
|
|
return (0);
|
|
|
|
/* Have we got any data in the queue? */
|
|
if ((lrd = DATA_AVAIL(dbuf)) == 0)
|
|
return (0);
|
|
|
|
/* Dequeue the data. */
|
|
if (lrd > len)
|
|
lrd = len;
|
|
dequeuerawdata(dbuf, data, lrd);
|
|
|
|
return (lrd);
|
|
}
|
|
|
|
int
|
|
midibuf_input_intr(midi_dbuf *dbuf, u_char *data, int len)
|
|
{
|
|
int lwritten;
|
|
|
|
/* Is this a real queue? */
|
|
if (dbuf == (midi_dbuf *)NULL)
|
|
return (0);
|
|
|
|
lwritten = 0;
|
|
|
|
/* Have we got any data to write? */
|
|
if (len == 0)
|
|
return (0);
|
|
/* Can we write now? */
|
|
if (SPACE_AVAIL(dbuf) < len)
|
|
return (-EAGAIN);
|
|
|
|
/* We can write some now. Queue the data. */
|
|
queuerawdata(dbuf, data, len);
|
|
lwritten = len;
|
|
|
|
/* Have we managed to write the whole data? */
|
|
if (lwritten < len)
|
|
printf("midibuf_input_intr: queue did not have enough space, discarded %d bytes out of %d bytes.\n", len - lwritten, len);
|
|
|
|
return (lwritten);
|
|
}
|
|
|
|
/* The sequencer calls this function to dequeue data. */
|
|
int
|
|
midibuf_seqread(midi_dbuf *dbuf, u_char* data, int len, struct mtx *m)
|
|
{
|
|
int i, lrd, lread;
|
|
|
|
/* Is this a real queue? */
|
|
if (dbuf == (midi_dbuf *)NULL)
|
|
return (0);
|
|
|
|
lread = 0;
|
|
/* Write down every single byte. */
|
|
while (len > 0) {
|
|
/* Have we got data to read? */
|
|
if ((lrd = DATA_AVAIL(dbuf)) == 0) {
|
|
/* No, sleep until we have data ready to read. */
|
|
i = msleep(&dbuf->tsleep_in, m, PRIBIO | PCATCH, "mbsqrd", 0);
|
|
if (i == EINTR || i == ERESTART)
|
|
return (-i);
|
|
if (i == EWOULDBLOCK)
|
|
continue;
|
|
/* Find out the number of bytes to read. */
|
|
lrd = DATA_AVAIL(dbuf);
|
|
}
|
|
|
|
if (lrd > len)
|
|
lrd = len;
|
|
if (lrd > 0) {
|
|
/* We can read some data now. Dequeue the data. */
|
|
dequeuerawdata(dbuf, data, lrd);
|
|
|
|
lread += lrd;
|
|
len -= lrd;
|
|
data += lrd;
|
|
}
|
|
}
|
|
|
|
return (lread);
|
|
}
|
|
|
|
/* The sequencer calls this function to undo dequeued data. */
|
|
int
|
|
midibuf_sequnread(midi_dbuf *dbuf, u_char* data, int len, struct mtx *m)
|
|
{
|
|
int i, lrd, lunread;
|
|
|
|
/* Is this a real queue? */
|
|
if (dbuf == (midi_dbuf *)NULL)
|
|
return (0);
|
|
|
|
lunread = 0;
|
|
/* Write down every single byte. */
|
|
while (len > 0) {
|
|
/* Have we got data to read? */
|
|
if ((lrd = SPACE_AVAIL(dbuf)) == 0) {
|
|
/* No, sleep until we have data ready to read. */
|
|
i = msleep(&dbuf->tsleep_in, m, PRIBIO | PCATCH, "mbsqur", 0);
|
|
if (i == EINTR || i == ERESTART)
|
|
return (-i);
|
|
if (i == EWOULDBLOCK)
|
|
continue;
|
|
/* Find out the number of bytes to unread. */
|
|
lrd = SPACE_AVAIL(dbuf);
|
|
}
|
|
|
|
if (lrd > len)
|
|
lrd = len;
|
|
if (lrd > 0) {
|
|
/* We can read some data now. Dequeue the data. */
|
|
undequeuerawdata(dbuf, data, lrd);
|
|
|
|
lunread += lrd;
|
|
len -= lrd;
|
|
data += lrd;
|
|
}
|
|
}
|
|
|
|
return (lunread);
|
|
}
|
|
|
|
/* The sequencer calls this function to copy data without dequeueing. */
|
|
int
|
|
midibuf_seqcopy(midi_dbuf *dbuf, u_char* data, int len, struct mtx *m)
|
|
{
|
|
int i, lrd, lread;
|
|
|
|
/* Is this a real queue? */
|
|
if (dbuf == (midi_dbuf *)NULL)
|
|
return (0);
|
|
|
|
lread = 0;
|
|
/* Write down every single byte. */
|
|
while (len > 0) {
|
|
/* Have we got data to read? */
|
|
if ((lrd = DATA_AVAIL(dbuf)) == 0) {
|
|
/* No, sleep until we have data ready to read. */
|
|
i = msleep(&dbuf->tsleep_in, m, PRIBIO | PCATCH, "mbsqrd", 0);
|
|
if (i == EINTR || i == ERESTART)
|
|
return (-i);
|
|
if (i == EWOULDBLOCK)
|
|
continue;
|
|
/* Find out the number of bytes to read. */
|
|
lrd = DATA_AVAIL(dbuf);
|
|
}
|
|
|
|
if (lrd > len)
|
|
lrd = len;
|
|
if (lrd > 0) {
|
|
/* We can read some data now. Copy the data. */
|
|
copyrawdata(dbuf, data, lrd);
|
|
|
|
lread += lrd;
|
|
len -= lrd;
|
|
data += lrd;
|
|
}
|
|
}
|
|
|
|
return (lread);
|
|
}
|
|
|
|
/* sndread calls this function to dequeue data. */
|
|
int
|
|
midibuf_uioread(midi_dbuf *dbuf, struct uio *buf, int len, struct mtx *m)
|
|
{
|
|
int i, lrd, lread;
|
|
|
|
/* Is this a real queue? */
|
|
if (dbuf == (midi_dbuf *)NULL)
|
|
return (0);
|
|
|
|
lread = 0;
|
|
while (len > 0 && lread == 0) {
|
|
/* Have we got data to read? */
|
|
if ((lrd = DATA_AVAIL(dbuf)) == 0) {
|
|
/* No, sleep until we have data ready to read. */
|
|
i = msleep(&dbuf->tsleep_in, m, PRIBIO | PCATCH, "mbuird", 0);
|
|
if (i == EINTR || i == ERESTART)
|
|
return (-i);
|
|
if (i == EWOULDBLOCK)
|
|
continue;
|
|
/* Find out the number of bytes to read. */
|
|
lrd = DATA_AVAIL(dbuf);
|
|
}
|
|
|
|
if (lrd > len)
|
|
lrd = len;
|
|
if (lrd > 0) {
|
|
/* We can read some data now. Dequeue the data. */
|
|
dequeueuiodata(dbuf, buf, lrd);
|
|
|
|
lread += lrd;
|
|
len -= lrd;
|
|
}
|
|
}
|
|
|
|
return (lread);
|
|
}
|
|
|
|
/*
|
|
* The functions below here are the libraries for the above ones.
|
|
*/
|
|
|
|
static void
|
|
queuerawdata(midi_dbuf *dbuf, char *data, int len)
|
|
{
|
|
/* dbuf->fp might wrap around dbuf->bufsize. */
|
|
if (dbuf->bufsize - dbuf->fp < len) {
|
|
/* The new data wraps, copy them twice. */
|
|
memcpy(dbuf->buf + dbuf->fp, data, dbuf->bufsize - dbuf->fp);
|
|
memcpy(dbuf->buf, data + dbuf->bufsize - dbuf->fp, len - (dbuf->bufsize - dbuf->fp));
|
|
} else
|
|
/* The new data do not wrap, once is enough. */
|
|
memcpy(dbuf->buf + dbuf->fp, data, len);
|
|
|
|
/* Adjust the pointer and the length counters. */
|
|
dbuf->fp = (dbuf->fp + len) % dbuf->bufsize;
|
|
dbuf->fl -= len;
|
|
dbuf->rl += len;
|
|
|
|
/* Wake up the processes sleeping on input data. */
|
|
wakeup(&dbuf->tsleep_in);
|
|
if (dbuf->sel.si_pid && dbuf->rl >= dbuf->blocksize)
|
|
selwakeup(&dbuf->sel);
|
|
}
|
|
|
|
static void
|
|
queueuiodata(midi_dbuf *dbuf, struct uio *buf, int len)
|
|
{
|
|
/* dbuf->fp might wrap around dbuf->bufsize. */
|
|
if (dbuf->bufsize - dbuf->fp < len) {
|
|
/* The new data wraps, copy them twice. */
|
|
uiomove((caddr_t)(dbuf->buf + dbuf->fp), dbuf->bufsize - dbuf->fp, buf);
|
|
uiomove((caddr_t)(dbuf->buf), len - (dbuf->bufsize - dbuf->fp), buf);
|
|
} else
|
|
/* The new data do not wrap, once is enough. */
|
|
uiomove((caddr_t)(dbuf->buf + dbuf->fp), len, buf);
|
|
|
|
/* Adjust the pointer and the length counters. */
|
|
dbuf->fp = (dbuf->fp + len) % dbuf->bufsize;
|
|
dbuf->fl -= len;
|
|
dbuf->rl += len;
|
|
|
|
/* Wake up the processes sleeping on queueing. */
|
|
wakeup(&dbuf->tsleep_in);
|
|
if (dbuf->sel.si_pid && dbuf->rl >= dbuf->blocksize)
|
|
selwakeup(&dbuf->sel);
|
|
}
|
|
|
|
static void
|
|
dequeuerawdata(midi_dbuf *dbuf, char *data, int len)
|
|
{
|
|
/* Copy the data. */
|
|
copyrawdata(dbuf, data, len);
|
|
|
|
/* Adjust the pointer and the length counters. */
|
|
dbuf->rp = (dbuf->rp + len) % dbuf->bufsize;
|
|
dbuf->rl -= len;
|
|
dbuf->fl += len;
|
|
|
|
/* Wake up the processes sleeping on queueing. */
|
|
wakeup(&dbuf->tsleep_out);
|
|
if (dbuf->sel.si_pid && dbuf->fl >= dbuf->blocksize)
|
|
selwakeup(&dbuf->sel);
|
|
}
|
|
|
|
/* Undo the last dequeue. */
|
|
static void
|
|
undequeuerawdata(midi_dbuf *dbuf, char *data, int len)
|
|
{
|
|
/* Copy the data. */
|
|
if (dbuf->rp < len) {
|
|
memcpy(dbuf->buf, data + (len - dbuf->rp), dbuf->rp);
|
|
memcpy(dbuf->buf + (dbuf->bufsize - (len - dbuf->rp)), data, len - dbuf->rp);
|
|
} else
|
|
memcpy(dbuf->buf + (dbuf->rp - len), data, len);
|
|
|
|
/* Adjust the pointer and the length counters. */
|
|
dbuf->rp = (dbuf->rp - len + dbuf->bufsize) % dbuf->bufsize;
|
|
dbuf->rl += len;
|
|
dbuf->fl -= len;
|
|
|
|
/* Wake up the processes sleeping on queueing. */
|
|
wakeup(&dbuf->tsleep_in);
|
|
if (dbuf->sel.si_pid && dbuf->rl >= dbuf->blocksize)
|
|
selwakeup(&dbuf->sel);
|
|
}
|
|
|
|
static void
|
|
copyrawdata(midi_dbuf *dbuf, char *data, int len)
|
|
{
|
|
/* dbuf->rp might wrap around dbuf->bufsize. */
|
|
if (dbuf->bufsize - dbuf->rp < len) {
|
|
/* The data to be read wraps, copy them twice. */
|
|
memcpy(data, dbuf->buf + dbuf->rp, dbuf->bufsize - dbuf->rp);
|
|
memcpy(data + dbuf->bufsize - dbuf->rp, dbuf->buf, len - (dbuf->bufsize - dbuf->rp));
|
|
} else
|
|
/* The new data do not wrap, once is enough. */
|
|
memcpy(data, dbuf->buf + dbuf->rp, len);
|
|
}
|
|
|
|
static void
|
|
dequeueuiodata(midi_dbuf *dbuf, struct uio *buf, int len)
|
|
{
|
|
/* dbuf->rp might wrap around dbuf->bufsize. */
|
|
if (dbuf->bufsize - dbuf->rp < len) {
|
|
/* The new data wraps, copy them twice. */
|
|
uiomove((caddr_t)(dbuf->buf + dbuf->rp), dbuf->bufsize - dbuf->rp, buf);
|
|
uiomove((caddr_t)(dbuf->buf), len - (dbuf->bufsize - dbuf->rp), buf);
|
|
} else
|
|
/* The new data do not wrap, once is enough. */
|
|
uiomove((caddr_t)(dbuf->buf + dbuf->rp), len, buf);
|
|
|
|
/* Adjust the pointer and the length counters. */
|
|
dbuf->rp = (dbuf->rp + len) % dbuf->bufsize;
|
|
dbuf->rl -= len;
|
|
dbuf->fl += len;
|
|
|
|
/* Wake up the processes sleeping on queueing. */
|
|
wakeup(&dbuf->tsleep_out);
|
|
if (dbuf->sel.si_pid && dbuf->fl >= dbuf->blocksize)
|
|
selwakeup(&dbuf->sel);
|
|
}
|