From 6c94880f12b914ad3317d68d4d3ec7f4b13c0b7b Mon Sep 17 00:00:00 2001 From: Sascha Wildner Date: Wed, 30 Sep 2015 19:21:39 +0200 Subject: [PATCH] Revert "sound: Drop midi support" This reverts commit 5bea67f41d941ba13d0b25782f6a3b9a9460e98e. --- sys/conf/files | 7 + sys/conf/kmod.mk | 2 + sys/dev/sound/driver/cmi/Makefile | 1 + sys/dev/sound/driver/emu10k1/Makefile | 1 + sys/dev/sound/driver/emu10kx/Makefile | 3 +- sys/dev/sound/midi/midi.c | 1531 ++++++++++++++++++++++++ sys/dev/sound/midi/midi.h | 57 + sys/dev/sound/midi/midiq.h | 106 ++ sys/dev/sound/midi/mpu401.c | 298 +++++ sys/dev/sound/midi/mpu401.h | 41 + sys/dev/sound/midi/mpu_if.m | 74 ++ sys/dev/sound/midi/mpufoi_if.m | 50 + sys/dev/sound/midi/sequencer.c | 2095 +++++++++++++++++++++++++++++++++ sys/dev/sound/midi/sequencer.h | 90 ++ sys/dev/sound/midi/synth_if.m | 313 +++++ sys/dev/sound/pci/cmi.c | 93 ++ sys/dev/sound/pci/emu10k1.c | 72 ++ sys/dev/sound/pci/emu10kx-midi.c | 253 ++++ sys/dev/sound/sound/Makefile | 4 + 19 files changed, 5090 insertions(+), 1 deletion(-) create mode 100644 sys/dev/sound/midi/midi.c create mode 100644 sys/dev/sound/midi/midi.h create mode 100644 sys/dev/sound/midi/midiq.h create mode 100644 sys/dev/sound/midi/mpu401.c create mode 100644 sys/dev/sound/midi/mpu401.h create mode 100644 sys/dev/sound/midi/mpu_if.m create mode 100644 sys/dev/sound/midi/mpufoi_if.m create mode 100644 sys/dev/sound/midi/sequencer.c create mode 100644 sys/dev/sound/midi/sequencer.h create mode 100644 sys/dev/sound/midi/synth_if.m create mode 100644 sys/dev/sound/pci/emu10kx-midi.c diff --git a/sys/conf/files b/sys/conf/files index c9d9e3838b..37daad2ddd 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1823,6 +1823,7 @@ dev/sound/pci/cs4281.c optional snd_cs4281 pci dev/sound/pci/emu10k1.c optional snd_emu10k1 pci dev/sound/pci/emu10kx.c optional snd_emu10kx pci dev/sound/pci/emu10kx-pcm.c optional snd_emu10kx pci +dev/sound/pci/emu10kx-midi.c optional snd_emu10kx pci dev/sound/pci/envy24.c optional snd_envy24 pci dev/sound/pci/envy24ht.c optional snd_envy24ht pci dev/sound/pci/es137x.c optional snd_es137x pci @@ -1874,6 +1875,12 @@ dev/sound/pcm/mixer_if.m optional sound dev/sound/pcm/sndstat.c optional sound dev/sound/pcm/sound.c optional sound dev/sound/pcm/vchan.c optional sound +dev/sound/midi/midi.c optional sound +dev/sound/midi/mpu401.c optional sound +dev/sound/midi/mpu_if.m optional sound +dev/sound/midi/mpufoi_if.m optional sound +dev/sound/midi/sequencer.c optional sound +dev/sound/midi/synth_if.m optional sound # # These files in libkern/ are those needed by all architectures. Some # of the files in libkern/ are only needed on some architectures. diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk index aeb7cba042..1f3b5a11a2 100644 --- a/sys/conf/kmod.mk +++ b/sys/conf/kmod.mk @@ -313,6 +313,8 @@ MFILES?= kern/bus_if.m kern/device_if.m bus/iicbus/iicbb_if.m \ dev/sound/pci/hda/hdac_if.m \ dev/sound/pcm/ac97_if.m dev/sound/pcm/channel_if.m \ dev/sound/pcm/feeder_if.m dev/sound/pcm/mixer_if.m \ + dev/sound/midi/mpu_if.m dev/sound/midi/mpufoi_if.m \ + dev/sound/midi/synth_if.m \ libiconv/iconv_converter_if.m dev/agp/agp_if.m opencrypto/cryptodev_if.m \ bus/mmc/mmcbus_if.m bus/mmc/mmcbr_if.m \ dev/virtual/virtio/virtio/virtio_bus_if.m \ diff --git a/sys/dev/sound/driver/cmi/Makefile b/sys/dev/sound/driver/cmi/Makefile index 87ae668170..0d15990e27 100644 --- a/sys/dev/sound/driver/cmi/Makefile +++ b/sys/dev/sound/driver/cmi/Makefile @@ -4,6 +4,7 @@ KMOD= snd_cmi SRCS= device_if.h bus_if.h pci_if.h +SRCS+= mpufoi_if.h SRCS+= cmi.c .include diff --git a/sys/dev/sound/driver/emu10k1/Makefile b/sys/dev/sound/driver/emu10k1/Makefile index 093942760b..4a2de37427 100644 --- a/sys/dev/sound/driver/emu10k1/Makefile +++ b/sys/dev/sound/driver/emu10k1/Makefile @@ -4,6 +4,7 @@ KMOD= snd_emu10k1 SRCS= device_if.h bus_if.h pci_if.h +SRCS+= mpufoi_if.h SRCS+= emu10k1.c .include diff --git a/sys/dev/sound/driver/emu10kx/Makefile b/sys/dev/sound/driver/emu10kx/Makefile index 627a2f372b..566f8eced0 100644 --- a/sys/dev/sound/driver/emu10kx/Makefile +++ b/sys/dev/sound/driver/emu10kx/Makefile @@ -4,9 +4,10 @@ KMOD= snd_emu10kx SRCS= device_if.h bus_if.h pci_if.h -SRCS+= channel_if.h ac97_if.h mixer_if.h +SRCS+= channel_if.h ac97_if.h mixer_if.h mpufoi_if.h # Master, PCM and MIDI devices SRCS+= emu10kx.c SRCS+= emu10kx-pcm.c +SRCS+= emu10kx-midi.c .include diff --git a/sys/dev/sound/midi/midi.c b/sys/dev/sound/midi/midi.c new file mode 100644 index 0000000000..ace7dfc4db --- /dev/null +++ b/sys/dev/sound/midi/midi.c @@ -0,0 +1,1531 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (augustss@netbsd.org). + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + /* + * Parts of this file started out as NetBSD: midi.c 1.31 + * They are mostly gone. Still the most obvious will be the state + * machine midi_in + */ + +#include +__FBSDID("$FreeBSD: head/sys/dev/sound/midi/midi.c 227309 2011-11-07 15:43:11Z ed $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + +#include +#include "mpu_if.h" + +#include +#include "synth_if.h" +MALLOC_DEFINE(M_MIDI, "midi buffers", "Midi data allocation area"); + +#ifndef KOBJMETHOD_END +#define KOBJMETHOD_END { NULL, NULL } +#endif + +#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 +}; + +/* + * The MPU interface current has init() uninit() inqsize(( outqsize() + * callback() : fiddle with the tx|rx status. + */ + +#include "mpu_if.h" + +/* + * /dev/rmidi Structure definitions + */ + +#define MIDI_NAMELEN 16 +struct snd_midi { + KOBJ_FIELDS; + struct mtx lock; /* Protects all but queues */ + void *cookie; + + int unit; /* Should only be used in midistat */ + int channel; /* Should only be used in midistat */ + + int busy; + int flags; /* File flags */ + char name[MIDI_NAMELEN]; + struct mtx qlock; /* Protects inq, outq and flags */ + MIDIQ_HEAD(, char) inq, outq; + int rchan, wchan; + struct selinfo rsel, wsel; + int hiwat; /* QLEN(outq)>High-water -> disable + * writes from userland */ + enum midi_states inq_state; + int inq_status, inq_left; /* Variables for the state machine in + * Midi_in, this is to provide that + * signals only get issued only + * complete command packets. */ + 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); + +/* + * Module Exports & Interface + * + * struct midi_chan *midi_init(MPU_CLASS cls, int unit, int chan) int + * midi_uninit(struct snd_midi *) 0 == no error EBUSY or other error int + * Midi_in(struct midi_chan *, char *buf, int count) int Midi_out(struct + * midi_chan *, char *buf, int count) + * + * midi_{in,out} return actual size transfered + * + */ + + +/* + * midi_devs tailq, holder of all rmidi instances protected by midistat_lock + */ + +TAILQ_HEAD(, snd_midi) midi_devs; + +/* + * /dev/midistat variables and declarations, protected by midistat_lock + */ + +static struct mtx midistat_lock; +static int midistat_isopen = 0; +static struct sbuf midistat_sbuf; +static int midistat_bufptr; +static struct cdev *midistat_dev; + +/* + * /dev/midistat dev_t declarations + */ + +static d_open_t midistat_open; +static d_close_t midistat_close; +static d_read_t midistat_read; + +static struct cdevsw midistat_cdevsw = { + .d_version = D_VERSION, + .d_open = midistat_open, + .d_close = midistat_close, + .d_read = midistat_read, + .d_name = "midistat", +}; + + +/* + * /dev/rmidi dev_t declarations, struct variable access is protected by + * locks contained within the structure. + */ + +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_poll_t midi_poll; + +static struct cdevsw midi_cdevsw = { + .d_version = D_VERSION, + .d_open = midi_open, + .d_close = midi_close, + .d_read = midi_read, + .d_write = midi_write, + .d_ioctl = midi_ioctl, + .d_poll = midi_poll, + .d_name = "rmidi", +}; + +/* + * Prototypes of library functions + */ + +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); + +/* + * Misc declr. + */ +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; +/* XXX: should this be moved into debug.midi? */ +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 +/* + * CODE START + */ + +/* + * Register a new rmidi device. cls midi_if interface unit == 0 means + * auto-assign new unit number unit != 0 already assigned a unit number, eg. + * not the first channel provided by this device. channel, sub-unit + * cookie is passed back on MPU calls Typical device drivers will call with + * unit=0, channel=1..(number of channels) and cookie=soft_c and won't care + * what unit number is used. + * + * It is an error to call midi_init with an already used unit/channel combo. + * + * Returns NULL on error + * + */ +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, printf("midiinit: unit %d/%d.\n", unit, channel)); + mtx_lock(&midistat_lock); + /* + * Protect against call with existing unit/channel or auto-allocate a + * new unit number. + */ + i = -1; + TAILQ_FOREACH(m, &midi_devs, link) { + mtx_lock(&m->lock); + if (unit != 0) { + if (m->unit == unit && m->channel == channel) { + mtx_unlock(&m->lock); + goto err0; + } + } else { + /* + * Find a better unit number + */ + if (m->unit > i) + i = m->unit; + } + mtx_unlock(&m->lock); + } + + if (unit == 0) + unit = i + 1; + + MIDI_DEBUG(1, printf("midiinit #2: unit %d/%d.\n", unit, channel)); + m = malloc(sizeof(*m), M_MIDI, M_NOWAIT | M_ZERO); + if (m == NULL) + goto err0; + + m->synth = malloc(sizeof(*m->synth), M_MIDI, M_NOWAIT | 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, printf("midiinit queues %d/%d.\n", inqsize, outqsize)); + if (!inqsize && !outqsize) + goto err1; + + mtx_init(&m->lock, "raw midi", NULL, 0); + mtx_init(&m->qlock, "q raw midi", NULL, 0); + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + if (inqsize) + buf = malloc(sizeof(MIDI_TYPE) * inqsize, M_MIDI, M_NOWAIT); + else + buf = NULL; + + MIDIQ_INIT(m->inq, buf, inqsize); + + if (outqsize) + buf = malloc(sizeof(MIDI_TYPE) * outqsize, M_MIDI, M_NOWAIT); + 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; + + mtx_unlock(&m->lock); + mtx_unlock(&m->qlock); + + TAILQ_INSERT_TAIL(&midi_devs, m, link); + + mtx_unlock(&midistat_lock); + + m->dev = make_dev(&midi_cdevsw, + MIDIMKMINOR(unit, MIDI_DEV_RAW, channel), + UID_ROOT, GID_WHEEL, 0666, "midi%d.%d", unit, channel); + m->dev->si_drv1 = m; + + return m; + +err2: mtx_destroy(&m->qlock); + mtx_destroy(&m->lock); + + if (MIDIQ_BUF(m->inq)) + free(MIDIQ_BUF(m->inq), M_MIDI); + if (MIDIQ_BUF(m->outq)) + free(MIDIQ_BUF(m->outq), M_MIDI); +err1: free(m, M_MIDI); +err0: mtx_unlock(&midistat_lock); + MIDI_DEBUG(1, printf("midi_init ended in error\n")); + return NULL; +} + +/* + * midi_uninit does not call MIDI_UNINIT, as since this is the implementors + * entry point. midi_unint if fact, does not send any methods. A call to + * midi_uninit is a defacto promise that you won't manipulate ch anymore + * + */ + +int +midi_uninit(struct snd_midi *m) +{ + int err; + + err = ENXIO; + mtx_lock(&midistat_lock); + mtx_lock(&m->lock); + 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: mtx_unlock(&m->lock); +exit: mtx_unlock(&midistat_lock); + return err; +} + +/* + * midi_in: process all data until the queue is full, then discards the rest. + * Since midi_in is a state machine, data discards can cause it to get out of + * whack. Process as much as possible. It calls, wakeup, selnotify and + * psignal at most once. + */ + +#ifdef notdef +static int midi_lengths[] = {2, 2, 2, 2, 1, 1, 2, 0}; + +#endif /* notdef */ +/* Number of bytes in a MIDI command */ +#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 i, sig, enq; */ + int used; + + /* MIDI_TYPE data; */ + MIDI_DEBUG(5, printf("midi_in: m=%p size=%d\n", m, size)); + +/* + * XXX: locking flub + */ + if (!(m->flags & M_RX)) + return size; + + used = 0; + + mtx_lock(&m->qlock); +#if 0 + /* + * Don't bother queuing if not in read mode. Discard everything and + * return size so the caller doesn't freak out. + */ + + 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: /* Sysex */ + m->inq_state = MIDI_IN_SYSEX; + break; + case 0xf1: /* MTC quarter frame */ + case 0xf3: /* Song select */ + m->inq_state = MIDI_IN_DATA; + enq = 1; + m->inq_left = 1; + break; + case 0xf2: /* Song position pointer */ + 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; + /* + * End of case MIDI_IN_START: + */ + + case MIDI_IN_DATA: + enq = 1; + if (--m->inq_left <= 0) + sig = 1;/* deliver data */ + 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++; + } + /* + * End of the state machines main "for loop" + */ + } + if (sig) { +#endif + MIDI_DEBUG(6, printf("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, printf("midi_in: Discarding data qu\n")); + mtx_unlock(&m->qlock); + return 0; + } + if (m->rchan) { + wakeup(&m->rchan); + m->rchan = 0; + } + selwakeup(&m->rsel); + if (m->async) { + PROC_LOCK(m->async); + kern_psignal(m->async, SIGIO); + PROC_UNLOCK(m->async); + } +#if 0 + } +#endif + mtx_unlock(&m->qlock); + return used; +} + +/* + * midi_out: The only clearer of the M_TXEN flag. + */ +int +midi_out(struct snd_midi *m, MIDI_TYPE *buf, int size) +{ + int used; + +/* + * XXX: locking flub + */ + if (!(m->flags & M_TXEN)) + return 0; + + MIDI_DEBUG(2, printf("midi_out: %p\n", m)); + mtx_lock(&m->qlock); + used = MIN(size, MIDIQ_LEN(m->outq)); + MIDI_DEBUG(3, printf("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; + } + selwakeup(&m->wsel); + if (m->async) { + PROC_LOCK(m->async); + kern_psignal(m->async, SIGIO); + PROC_UNLOCK(m->async); + } + } + mtx_unlock(&m->qlock); + return used; +} + + +/* + * /dev/rmidi#.# device access functions + */ +int +midi_open(struct cdev *i_dev, int flags, int mode, struct thread *td) +{ + struct snd_midi *m = i_dev->si_drv1; + int retval; + + MIDI_DEBUG(1, printf("midiopen %p %s %s\n", td, + flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : "")); + if (m == NULL) + return ENXIO; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + 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; + /* + * Only clear the inq, the outq might still have data to drain + * from a previous session + */ + MIDIQ_CLEAR(m->inq); + }; + + if (flags & FWRITE) + m->flags |= M_TX; + + MPU_CALLBACK(m, m->cookie, m->flags); + + MIDI_DEBUG(2, printf("midi_open: opened.\n")); + +err: mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); + return retval; +} + +int +midi_close(struct cdev *i_dev, int flags, int mode, struct thread *td) +{ + struct snd_midi *m = i_dev->si_drv1; + int retval; + int oldflags; + + MIDI_DEBUG(1, printf("midi_close %p %s %s\n", td, + flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : "")); + + if (m == NULL) + return ENXIO; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + 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, printf("midi_close: closed, busy = %d.\n", m->busy)); + + mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); + retval = 0; +err: return retval; +} + +/* + * TODO: midi_read, per oss programmer's guide pg. 42 should return as soon + * as data is available. + */ +int +midi_read(struct cdev *i_dev, struct uio *uio, int ioflag) +{ +#define MIDI_RSIZE 32 + struct snd_midi *m = i_dev->si_drv1; + int retval; + int used; + char buf[MIDI_RSIZE]; + + MIDI_DEBUG(5, printf("midiread: count=%lu\n", + (unsigned long)uio->uio_resid)); + + retval = EIO; + + if (m == NULL) + goto err0; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + 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; + mtx_unlock(&m->lock); + m->rchan = 1; + retval = msleep(&m->rchan, &m->qlock, + PCATCH | PDROP, "midi RX", 0); + /* + * We slept, maybe things have changed since last + * dying check + */ + if (retval == EINTR) + goto err0; + if (m != i_dev->si_drv1) + retval = ENXIO; + /* if (retval && retval != ERESTART) */ + if (retval) + goto err0; + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + m->rchan = 0; + if (!m->busy) + goto err1; + } + MIDI_DEBUG(6, printf("midi_read start\n")); + /* + * At this point, it is certain that m->inq has data + */ + + used = MIN(MIDIQ_LEN(m->inq), uio->uio_resid); + used = MIN(used, MIDI_RSIZE); + + MIDI_DEBUG(6, printf("midiread: uiomove cc=%d\n", used)); + MIDIQ_DEQ(m->inq, buf, used); + retval = uiomove(buf, used, uio); + if (retval) + goto err1; + } + + /* + * If we Made it here then transfer is good + */ + retval = 0; +err1: mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); +err0: MIDI_DEBUG(4, printf("midi_read: ret %d\n", retval)); + return retval; +} + +/* + * midi_write: The only setter of M_TXEN + */ + +int +midi_write(struct cdev *i_dev, struct uio *uio, int ioflag) +{ +#define MIDI_WSIZE 32 + struct snd_midi *m = i_dev->si_drv1; + int retval; + int used; + char buf[MIDI_WSIZE]; + + + MIDI_DEBUG(4, printf("midi_write\n")); + retval = 0; + if (m == NULL) + goto err0; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + 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; + mtx_unlock(&m->lock); + m->wchan = 1; + MIDI_DEBUG(3, printf("midi_write msleep\n")); + retval = msleep(&m->wchan, &m->qlock, + PCATCH | PDROP, "midi TX", 0); + /* + * We slept, maybe things have changed since last + * dying check + */ + if (retval == EINTR) + goto err0; + if (m != i_dev->si_drv1) + retval = ENXIO; + if (retval) + goto err0; + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + m->wchan = 0; + if (!m->busy) + goto err1; + } + + /* + * We are certain than data can be placed on the queue + */ + + used = MIN(MIDIQ_AVAIL(m->outq), uio->uio_resid); + used = MIN(used, MIDI_WSIZE); + MIDI_DEBUG(5, printf("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, printf("midi_write: uiomove cc=%d\n", used)); + retval = uiomove(buf, used, uio); + if (retval) + goto err1; + MIDIQ_ENQ(m->outq, buf, used); + /* + * Inform the bottom half that data can be written + */ + if (!(m->flags & M_TXEN)) { + m->flags |= M_TXEN; + MPU_CALLBACK(m, m->cookie, m->flags); + } + } + /* + * If we Made it here then transfer is good + */ + retval = 0; +err1: mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); +err0: return retval; +} + +int +midi_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, + struct thread *td) +{ + return ENXIO; +} + +int +midi_poll(struct cdev *i_dev, int events, struct thread *td) +{ + struct snd_midi *m = i_dev->si_drv1; + int revents; + + if (m == NULL) + return 0; + + revents = 0; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + if (events & (POLLIN | POLLRDNORM)) + if (!MIDIQ_EMPTY(m->inq)) + events |= events & (POLLIN | POLLRDNORM); + + if (events & (POLLOUT | POLLWRNORM)) + if (MIDIQ_AVAIL(m->outq) < m->hiwat) + events |= events & (POLLOUT | POLLWRNORM); + + if (revents == 0) { + if (events & (POLLIN | POLLRDNORM)) + selrecord(td, &m->rsel); + + if (events & (POLLOUT | POLLWRNORM)) + selrecord(td, &m->wsel); + } + mtx_unlock(&m->lock); + mtx_unlock(&m->qlock); + + return (revents); +} + +/* + * /dev/midistat device functions + * + */ +static int +midistat_open(struct cdev *i_dev, int flags, int mode, struct thread *td) +{ + int error; + + MIDI_DEBUG(1, printf("midistat_open\n")); + mtx_lock(&midistat_lock); + + if (midistat_isopen) { + mtx_unlock(&midistat_lock); + return EBUSY; + } + midistat_isopen = 1; + mtx_unlock(&midistat_lock); + + if (sbuf_new(&midistat_sbuf, NULL, 4096, SBUF_AUTOEXTEND) == NULL) { + error = ENXIO; + mtx_lock(&midistat_lock); + goto out; + } + mtx_lock(&midistat_lock); + midistat_bufptr = 0; + error = (midistat_prepare(&midistat_sbuf) > 0) ? 0 : ENOMEM; + +out: if (error) + midistat_isopen = 0; + mtx_unlock(&midistat_lock); + return error; +} + +static int +midistat_close(struct cdev *i_dev, int flags, int mode, struct thread *td) +{ + MIDI_DEBUG(1, printf("midistat_close\n")); + mtx_lock(&midistat_lock); + if (!midistat_isopen) { + mtx_unlock(&midistat_lock); + return EBADF; + } + sbuf_delete(&midistat_sbuf); + midistat_isopen = 0; + + mtx_unlock(&midistat_lock); + return 0; +} + +static int +midistat_read(struct cdev *i_dev, struct uio *buf, int flag) +{ + int l, err; + + MIDI_DEBUG(4, printf("midistat_read\n")); + mtx_lock(&midistat_lock); + if (!midistat_isopen) { + mtx_unlock(&midistat_lock); + return EBADF; + } + l = min(buf->uio_resid, sbuf_len(&midistat_sbuf) - midistat_bufptr); + err = 0; + if (l > 0) { + mtx_unlock(&midistat_lock); + err = uiomove(sbuf_data(&midistat_sbuf) + midistat_bufptr, l, + buf); + mtx_lock(&midistat_lock); + } else + l = 0; + midistat_bufptr += l; + mtx_unlock(&midistat_lock); + return err; +} + +/* + * Module library functions + */ + +static int +midistat_prepare(struct sbuf *s) +{ + struct snd_midi *m; + + mtx_assert(&midistat_lock, MA_OWNED); + + 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) { + mtx_lock(&m->lock); + 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"); + mtx_unlock(&m->lock); + } + + sbuf_finish(s); + return sbuf_len(s); +} + +#ifdef notdef +/* + * Convert IOCTL command to string for debugging + */ + +static char * +midi_cmdname(int cmd) +{ + static struct { + int cmd; + char *name; + } *tab, cmdtab_midiioctl[] = { +#define A(x) {x, ## x} + /* + * Once we have some real IOCTLs define, the following will + * be relavant. + * + * A(SNDCTL_MIDI_PRETIME), A(SNDCTL_MIDI_MPUMODE), + * A(SNDCTL_MIDI_MPUCMD), A(SNDCTL_SYNTH_INFO), + * A(SNDCTL_MIDI_INFO), A(SNDCTL_SYNTH_MEMAVL), + * A(SNDCTL_FM_LOAD_INSTR), A(SNDCTL_FM_4OP_ENABLE), + * A(MIOSPASSTHRU), A(MIOGPASSTHRU), A(AIONWRITE), + * A(AIOGSIZE), A(AIOSSIZE), A(AIOGFMT), A(AIOSFMT), + * A(AIOGMIX), A(AIOSMIX), A(AIOSTOP), A(AIOSYNC), + * A(AIOGCAP), + */ +#undef A + { + -1, "unknown" + }, + }; + + for (tab = cmdtab_midiioctl; tab->cmd != cmd && tab->cmd != -1; tab++); + return tab->name; +} + +#endif /* notdef */ + +/* + * midisynth + */ + + +int +midisynth_open(void *n, void *arg, int flags) +{ + struct snd_midi *m = ((struct synth_midi *)n)->m; + int retval; + + MIDI_DEBUG(1, printf("midisynth_open %s %s\n", + flags & FREAD ? "M_RX" : "", flags & FWRITE ? "M_TX" : "")); + + if (m == NULL) + return ENXIO; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + 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++; + + /* + * TODO: Consider m->async = 0; + */ + + if (flags & FREAD) { + m->flags |= M_RX | M_RXEN; + /* + * Only clear the inq, the outq might still have data to drain + * from a previous session + */ + 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: mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); + MIDI_DEBUG(2, printf("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, printf("midisynth_close %s %s\n", + m->synth_flags & FREAD ? "M_RX" : "", + m->synth_flags & FWRITE ? "M_TX" : "")); + + if (m == NULL) + return ENXIO; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + 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, printf("midi_close: closed, busy = %d.\n", m->busy)); + + mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); + retval = 0; +err: return retval; +} + +/* + * Always blocking. + */ + +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, printf("midisynth_writeraw\n")); + + retval = 0; + + if (m == NULL) + return ENXIO; + + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + + if (!(m->flags & M_TX)) + goto err1; + + if (midi_dumpraw) + printf("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); + } + mtx_unlock(&m->lock); + m->wchan = 1; + MIDI_DEBUG(3, printf("midisynth_writeraw msleep\n")); + retval = msleep(&m->wchan, &m->qlock, + PCATCH | PDROP, "midi TX", 0); + /* + * We slept, maybe things have changed since last + * dying check + */ + if (retval == EINTR) + goto err0; + + if (retval) + goto err0; + mtx_lock(&m->lock); + mtx_lock(&m->qlock); + m->wchan = 0; + if (!m->busy) + goto err1; + } + + /* + * We are certain than data can be placed on the queue + */ + + used = MIN(MIDIQ_AVAIL(m->outq), len); + used = MIN(used, MIDI_WSIZE); + MIDI_DEBUG(5, + printf("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++) + printf("%x ", buf[i]); + + MIDIQ_ENQ(m->outq, buf, used); + len -= used; + + /* + * Inform the bottom half that data can be written + */ + if (!(m->flags & M_TXEN)) { + m->flags |= M_TXEN; + MPU_CALLBACK(m, m->cookie, m->flags); + } + } + /* + * If we Made it here then transfer is good + */ + if (midi_dumpraw) + printf("\n"); + + retval = 0; +err1: mtx_unlock(&m->qlock); + mtx_unlock(&m->lock); +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); /* Note on. */ + c[1] = (u_char)note; + c[2] = 0; + } else { + c[0] = 0x80 | (chn & 0x0f); /* Note off. */ + 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); /* Progamme change. */ + 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); /* Note on. */ + 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); /* Control Message. */ + 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); /* Pitch bend. */ + c[1] = (u_char)val & 0x7f; + c[2] = (u_char)(val >> 7) & 0x7f; + + return midisynth_writeraw(n, c, 3); +} + +/* + * Single point of midi destructions. + */ +static int +midi_destroy(struct snd_midi *m, int midiuninit) +{ + + mtx_assert(&midistat_lock, MA_OWNED); + mtx_assert(&m->lock, MA_OWNED); + + MIDI_DEBUG(3, printf("midi_destroy\n")); + m->dev->si_drv1 = NULL; + mtx_unlock(&m->lock); /* XXX */ + destroy_dev(m->dev); + TAILQ_REMOVE(&midi_devs, m, link); + if (midiuninit) + MPU_UNINIT(m, m->cookie); + free(MIDIQ_BUF(m->inq), M_MIDI); + free(MIDIQ_BUF(m->outq), M_MIDI); + mtx_destroy(&m->qlock); + mtx_destroy(&m->lock); + free(m, M_MIDI); + return 0; +} + +/* + * Load and unload functions, creates the /dev/midistat device + */ + +static int +midi_load() +{ + mtx_init(&midistat_lock, "midistat lock", NULL, 0); + TAILQ_INIT(&midi_devs); /* Initialize the queue. */ + + midistat_dev = make_dev(&midistat_cdevsw, + MIDIMKMINOR(0, MIDI_DEV_MIDICTL, 0), + UID_ROOT, GID_WHEEL, 0666, "midistat"); + + return 0; +} + +static int +midi_unload() +{ + struct snd_midi *m; + int retval; + + MIDI_DEBUG(1, printf("midi_unload()\n")); + retval = EBUSY; + mtx_lock(&midistat_lock); + if (midistat_isopen) + goto exit0; + + TAILQ_FOREACH(m, &midi_devs, link) { + mtx_lock(&m->lock); + if (m->busy) + retval = EBUSY; + else + retval = midi_destroy(m, 1); + if (retval) + goto exit1; + } + + mtx_unlock(&midistat_lock); /* XXX */ + + destroy_dev(midistat_dev); + /* + * Made it here then unload is complete + */ + mtx_destroy(&midistat_lock); + return 0; + +exit1: + mtx_unlock(&m->lock); +exit0: + mtx_unlock(&midistat_lock); + if (retval) + MIDI_DEBUG(2, printf("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; + + mtx_lock(&midistat_lock); + + TAILQ_FOREACH(m, &midi_devs, link) { + retval++; + } + + mtx_unlock(&midistat_lock); + 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; + + mtx_lock(&midistat_lock); + + TAILQ_FOREACH(m, &midi_devs, link) { + if (unit == retval) { + mtx_unlock(&midistat_lock); + return (kobj_t)m->synth; + } + retval++; + } + + mtx_unlock(&midistat_lock); + return NULL; +} + +DEV_MODULE(midi, midi_modevent, NULL); +MODULE_VERSION(midi, 1); diff --git a/sys/dev/sound/midi/midi.h b/sys/dev/sound/midi/midi.h new file mode 100644 index 0000000000..3a13db27b5 --- /dev/null +++ b/sys/dev/sound/midi/midi.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * 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: head/sys/dev/sound/midi/midi.h 166971 2007-02-25 13:51:52Z netchild $ + */ + +#ifndef MIDI_H +#define MIDI_H + +#include +#include + +MALLOC_DECLARE(M_MIDI); + +#define M_RX 0x01 +#define M_TX 0x02 +#define M_RXEN 0x04 +#define M_TXEN 0x08 + +#define MIDI_TYPE unsigned char + +struct snd_midi; + +struct snd_midi * +midi_init(kobj_class_t _mpu_cls, int _unit, int _channel, void *cookie); +int midi_uninit(struct snd_midi *_m); +int midi_out(struct snd_midi *_m, MIDI_TYPE *_buf, int _size); +int midi_in(struct snd_midi *_m, MIDI_TYPE *_buf, int _size); + +kobj_t midimapper_addseq(void *arg1, int *unit, void **cookie); +int midimapper_open(void *arg1, void **cookie); +int midimapper_close(void *arg1, void *cookie); +kobj_t midimapper_fetch_synth(void *arg, void *cookie, int unit); + +#endif diff --git a/sys/dev/sound/midi/midiq.h b/sys/dev/sound/midi/midiq.h new file mode 100644 index 0000000000..14fb25da1a --- /dev/null +++ b/sys/dev/sound/midi/midiq.h @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * 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: head/sys/dev/sound/midi/midiq.h 166971 2007-02-25 13:51:52Z netchild $ + */ + +#ifndef MIDIQ_H +#define MIDIQ_H + +#define MIDIQ_MOVE(a,b,c) bcopy(b,a,c) + +#define MIDIQ_HEAD(name, type) \ +struct name { \ + int h, t, s; \ + type * b; \ +} + +#define MIDIQ_INIT(head, buf, size) do { \ + (head).h=(head).t=0; \ + (head).s=size; \ + (head).b=buf; \ +} while (0) + +#define MIDIQ_EMPTY(head) ((head).h == (head).t ) + +#define MIDIQ_LENBASE(head) ((head).h - (head).t < 0 ? \ + (head).h - (head).t + (head).s : \ + (head).h - (head).t) + +#define MIDIQ_FULL(head) ((head).h == -1) +#define MIDIQ_AVAIL(head) (MIDIQ_FULL(head) ? 0 : (head).s - MIDIQ_LENBASE(head)) +#define MIDIQ_LEN(head) ((head).s - MIDIQ_AVAIL(head)) +#define MIDIQ_DEBUG 0 +/* + * No protection against overflow, underflow + */ +#define MIDIQ_ENQ(head, buf, size) do { \ + if(MIDIQ_DEBUG)\ + printf("#1 %p %p bytes copied %jd tran req s %d h %d t %d\n", \ + &(head).b[(head).h], (buf), \ + (intmax_t)(sizeof(*(head).b) * \ + MIN( (size), (head).s - (head).h) ), \ + (size), (head).h, (head).t); \ + MIDIQ_MOVE(&(head).b[(head).h], (buf), sizeof(*(head).b) * MIN((size), (head).s - (head).h)); \ + if( (head).s - (head).h < (size) ) { \ + if(MIDIQ_DEBUG) \ + printf("#2 %p %p bytes copied %jd\n", (head).b, (buf) + (head).s - (head).h, (intmax_t)sizeof(*(head).b) * ((size) - (head).s + (head).h) ); \ + MIDIQ_MOVE((head).b, (buf) + (head).s - (head).h, sizeof(*(head).b) * ((size) - (head).s + (head).h) ); \ + } \ + (head).h+=(size); \ + (head).h%=(head).s; \ + if(MIDIQ_EMPTY(head)) (head).h=-1; \ + if(MIDIQ_DEBUG)\ + printf("#E h %d t %d\n", (head).h, (head).t); \ +} while (0) + +#define MIDIQ_DEQ_I(head, buf, size, move, update) do { \ + if(MIDIQ_FULL(head)) (head).h=(head).t; \ + if(MIDIQ_DEBUG)\ + printf("#1 %p %p bytes copied %jd tran req s %d h %d t %d\n", &(head).b[(head).t], (buf), (intmax_t)sizeof(*(head).b) * MIN((size), (head).s - (head).t), (size), (head).h, (head).t); \ + if (move) MIDIQ_MOVE((buf), &(head).b[(head).t], sizeof(*(head).b) * MIN((size), (head).s - (head).t)); \ + if( (head).s - (head).t < (size) ) { \ + if(MIDIQ_DEBUG) \ + printf("#2 %p %p bytes copied %jd\n", (head).b, (buf) + (head).s - (head).t, (intmax_t)sizeof(*(head).b) * ((size) - (head).s + (head).t) ); \ + if (move) MIDIQ_MOVE((buf) + (head).s - (head).t, (head).b, sizeof(*(head).b) * ((size) - (head).s + (head).t) ); \ + } \ + if (update) { \ + (head).t+=(size); \ + (head).t%=(head).s; \ + } else { \ + if (MIDIQ_EMPTY(head)) (head).h=-1; \ + } \ + if(MIDIQ_DEBUG)\ + printf("#E h %d t %d\n", (head).h, (head).t); \ +} while (0) + +#define MIDIQ_SIZE(head) ((head).s) +#define MIDIQ_CLEAR(head) ((head).h = (head).t = 0) +#define MIDIQ_BUF(head) ((head).b) +#define MIDIQ_DEQ(head, buf, size) MIDIQ_DEQ_I(head, buf, size, 1, 1) +#define MIDIQ_PEEK(head, buf, size) MIDIQ_DEQ_I(head, buf, size, 1, 0) +#define MIDIQ_POP(head, size) MIDIQ_DEQ_I(head, &head, size, 0, 1) + +#endif diff --git a/sys/dev/sound/midi/mpu401.c b/sys/dev/sound/midi/mpu401.c new file mode 100644 index 0000000000..e567d15d2e --- /dev/null +++ b/sys/dev/sound/midi/mpu401.c @@ -0,0 +1,298 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * 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. + */ + +#include +__FBSDID("$FreeBSD: head/sys/dev/sound/midi/mpu401.c 193979 2009-06-11 09:06:09Z ariff $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* to get driver_intr_t */ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + +#include +#include + +#include "mpu_if.h" +#include "mpufoi_if.h" + +#ifndef KOBJMETHOD_END +#define KOBJMETHOD_END { NULL, NULL } +#endif + +#define MPU_DATAPORT 0 +#define MPU_CMDPORT 1 +#define MPU_STATPORT 1 +#define MPU_RESET 0xff +#define MPU_UART 0x3f +#define MPU_ACK 0xfe +#define MPU_STATMASK 0xc0 +#define MPU_OUTPUTBUSY 0x40 +#define MPU_INPUTBUSY 0x80 +#define MPU_TRYDATA 50 +#define MPU_DELAY 2500 + +#define CMD(m,d) MPUFOI_WRITE(m, m->cookie, MPU_CMDPORT,d) +#define STATUS(m) MPUFOI_READ(m, m->cookie, MPU_STATPORT) +#define READ(m) MPUFOI_READ(m, m->cookie, MPU_DATAPORT) +#define WRITE(m,d) MPUFOI_WRITE(m, m->cookie, MPU_DATAPORT,d) + +struct mpu401 { + KOBJ_FIELDS; + struct snd_midi *mid; + int flags; + driver_intr_t *si; + void *cookie; + struct callout timer; +}; + +static void mpu401_timeout(void *m); +static mpu401_intr_t mpu401_intr; + +static int mpu401_minit(struct snd_midi *, void *); +static int mpu401_muninit(struct snd_midi *, void *); +static int mpu401_minqsize(struct snd_midi *, void *); +static int mpu401_moutqsize(struct snd_midi *, void *); +static void mpu401_mcallback(struct snd_midi *, void *, int); +static void mpu401_mcallbackp(struct snd_midi *, void *, int); +static const char *mpu401_mdescr(struct snd_midi *, void *, int); +static const char *mpu401_mprovider(struct snd_midi *, void *); + +static kobj_method_t mpu401_methods[] = { + KOBJMETHOD(mpu_init, mpu401_minit), + KOBJMETHOD(mpu_uninit, mpu401_muninit), + KOBJMETHOD(mpu_inqsize, mpu401_minqsize), + KOBJMETHOD(mpu_outqsize, mpu401_moutqsize), + KOBJMETHOD(mpu_callback, mpu401_mcallback), + KOBJMETHOD(mpu_callbackp, mpu401_mcallbackp), + KOBJMETHOD(mpu_descr, mpu401_mdescr), + KOBJMETHOD(mpu_provider, mpu401_mprovider), + KOBJMETHOD_END +}; + +DEFINE_CLASS(mpu401, mpu401_methods, 0); + +void +mpu401_timeout(void *a) +{ + struct mpu401 *m = (struct mpu401 *)a; + + if (m->si) + (m->si)(m->cookie); + +} +static int +mpu401_intr(struct mpu401 *m) +{ +#define MPU_INTR_BUF 16 + MIDI_TYPE b[MPU_INTR_BUF]; + int i; + int s; + +/* + printf("mpu401_intr\n"); +*/ +#define RXRDY(m) ( (STATUS(m) & MPU_INPUTBUSY) == 0) +#define TXRDY(m) ( (STATUS(m) & MPU_OUTPUTBUSY) == 0) +#if 0 +#define D(x,l) printf("mpu401_intr %d %x %s %s\n",l, x, x&MPU_INPUTBUSY?"RX":"", x&MPU_OUTPUTBUSY?"TX":"") +#else +#define D(x,l) +#endif + i = 0; + s = STATUS(m); + D(s, 1); + while ((s & MPU_INPUTBUSY) == 0 && i < MPU_INTR_BUF) { + b[i] = READ(m); +/* + printf("mpu401_intr in i %d d %d\n", i, b[i]); +*/ + i++; + s = STATUS(m); + } + if (i) + midi_in(m->mid, b, i); + i = 0; + while (!(s & MPU_OUTPUTBUSY) && i < MPU_INTR_BUF) { + if (midi_out(m->mid, b, 1)) { +/* + printf("mpu401_intr out i %d d %d\n", i, b[0]); +*/ + + WRITE(m, *b); + } else { +/* + printf("mpu401_intr write: no output\n"); +*/ + return 0; + } + i++; + /* DELAY(100); */ + s = STATUS(m); + } + + if ((m->flags & M_TXEN) && (m->si)) { + callout_reset(&m->timer, 1, mpu401_timeout, m); + } + return (m->flags & M_TXEN) == M_TXEN; +} + +struct mpu401 * +mpu401_init(kobj_class_t cls, void *cookie, driver_intr_t softintr, + mpu401_intr_t ** cb) +{ + struct mpu401 *m; + + *cb = NULL; + m = malloc(sizeof(*m), M_MIDI, M_NOWAIT | M_ZERO); + + if (!m) + return NULL; + + kobj_init((kobj_t)m, cls); + + callout_init(&m->timer, CALLOUT_MPSAFE); + + m->si = softintr; + m->cookie = cookie; + m->flags = 0; + + m->mid = midi_init(&mpu401_class, 0, 0, m); + if (!m->mid) + goto err; + *cb = mpu401_intr; + return m; +err: + printf("mpu401_init error\n"); + free(m, M_MIDI); + return NULL; +} + +int +mpu401_uninit(struct mpu401 *m) +{ + int retval; + + CMD(m, MPU_RESET); + retval = midi_uninit(m->mid); + if (retval) + return retval; + free(m, M_MIDI); + return 0; +} + +static int +mpu401_minit(struct snd_midi *sm, void *arg) +{ + struct mpu401 *m = arg; + int i; + + CMD(m, MPU_RESET); + CMD(m, MPU_UART); + return 0; + i = 0; + while (++i < 2000) { + if (RXRDY(m)) + if (READ(m) == MPU_ACK) + break; + } + + if (i < 2000) { + CMD(m, MPU_UART); + return 0; + } + printf("mpu401_minit failed active sensing\n"); + return 1; +} + + +int +mpu401_muninit(struct snd_midi *sm, void *arg) +{ + struct mpu401 *m = arg; + + return MPUFOI_UNINIT(m, m->cookie); +} + +int +mpu401_minqsize(struct snd_midi *sm, void *arg) +{ + return 128; +} + +int +mpu401_moutqsize(struct snd_midi *sm, void *arg) +{ + return 128; +} + +static void +mpu401_mcallback(struct snd_midi *sm, void *arg, int flags) +{ + struct mpu401 *m = arg; +#if 0 + printf("mpu401_callback %s %s %s %s\n", + flags & M_RX ? "M_RX" : "", + flags & M_TX ? "M_TX" : "", + flags & M_RXEN ? "M_RXEN" : "", + flags & M_TXEN ? "M_TXEN" : ""); +#endif + if (flags & M_TXEN && m->si) { + callout_reset(&m->timer, 1, mpu401_timeout, m); + } + m->flags = flags; +} + +static void +mpu401_mcallbackp(struct snd_midi *sm, void *arg, int flags) +{ +/* printf("mpu401_callbackp\n"); */ + mpu401_mcallback(sm, arg, flags); +} + +static const char * +mpu401_mdescr(struct snd_midi *sm, void *arg, int verbosity) +{ + + return "descr mpu401"; +} + +static const char * +mpu401_mprovider(struct snd_midi *m, void *arg) +{ + return "provider mpu401"; +} diff --git a/sys/dev/sound/midi/mpu401.h b/sys/dev/sound/midi/mpu401.h new file mode 100644 index 0000000000..acede4367e --- /dev/null +++ b/sys/dev/sound/midi/mpu401.h @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * 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: head/sys/dev/sound/midi/mpu401.h 166971 2007-02-25 13:51:52Z netchild $ + */ + +#ifndef MPU401_H +#define MPU401_H + +struct mpu401; + +typedef int mpu401_intr_t(struct mpu401 *_obj); + +extern struct mpu401 * +mpu401_init(kobj_class_t _cls, void *cookie, driver_intr_t *_softintr, + mpu401_intr_t ** _cb); +extern int mpu401_uninit(struct mpu401 *_obj); + +#endif diff --git a/sys/dev/sound/midi/mpu_if.m b/sys/dev/sound/midi/mpu_if.m new file mode 100644 index 0000000000..979aa6b523 --- /dev/null +++ b/sys/dev/sound/midi/mpu_if.m @@ -0,0 +1,74 @@ +#- +# Copyright (c) 2003 Mathew Kanner +# 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: head/sys/dev/sound/midi/mpu_if.m 166971 2007-02-25 13:51:52Z netchild $ +# + +#include + +INTERFACE mpu; + +METHOD int inqsize { + struct snd_midi *_kobj; + void *_cookie; +}; + +METHOD int outqsize { + struct snd_midi *_kobj; + void *_cookie; +}; + +METHOD int init { + struct snd_midi *_kobj; + void *_cookie; +}; + +METHOD void callbackp { + struct snd_midi *_kobj; + void *_cookie; + int _flags; +}; + +METHOD void callback { + struct snd_midi *_kobj; + void *_cookie; + int _flags; +}; + +METHOD const char * provider { + struct snd_midi *_kobj; + void *_cookie; +}; + +METHOD const char * descr { + struct snd_midi *_kobj; + void *_cookie; + int _verbosity; +}; + +METHOD int uninit { + struct snd_midi *_kobj; + void *_cookie; +}; diff --git a/sys/dev/sound/midi/mpufoi_if.m b/sys/dev/sound/midi/mpufoi_if.m new file mode 100644 index 0000000000..727bc1e726 --- /dev/null +++ b/sys/dev/sound/midi/mpufoi_if.m @@ -0,0 +1,50 @@ +#- +# Copyright (c) 2003 Mathew Kanner +# 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: head/sys/dev/sound/midi/mpufoi_if.m 166971 2007-02-25 13:51:52Z netchild $ +# + +#include +#include + +INTERFACE mpufoi; + +METHOD unsigned char read { + struct mpu401 *_kobj; + void *_cookie; + int _reg; +}; + +METHOD void write { + struct mpu401 *_kobj; + void *_cookie; + int _reg; + unsigned char _d; +}; + +METHOD int uninit { + struct mpu401 *_kobj; + void *_cookie; +}; diff --git a/sys/dev/sound/midi/sequencer.c b/sys/dev/sound/midi/sequencer.c new file mode 100644 index 0000000000..3747a268f3 --- /dev/null +++ b/sys/dev/sound/midi/sequencer.c @@ -0,0 +1,2095 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * Copyright (c) 1993 Hannu Savolainen + * 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. + */ + +/* + * The sequencer personality manager. + */ + +#include +__FBSDID("$FreeBSD: head/sys/dev/sound/midi/sequencer.c 274035 2014-11-03 11:11:45Z bapt $"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include /* for DATA_SET */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for DELAY */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + +#include +#include +#include "synth_if.h" + +#include + +#define TMR_TIMERBASE 13 + +#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM + * synthesizer and MIDI output) */ +#define SND_DEV_MUSIC 8 /* /dev/music, level 2 interface */ + +/* Length of a sequencer event. */ +#define EV_SZ 8 +#define IEV_SZ 8 + +/* Lookup modes */ +#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) + +/* These are the entries to the sequencer driver. */ +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_poll_t seq_poll; + +static struct cdevsw seq_cdevsw = { + .d_version = D_VERSION, + .d_open = seq_open, + .d_close = seq_close, + .d_read = seq_read, + .d_write = seq_write, + .d_ioctl = seq_ioctl, + .d_poll = seq_poll, + .d_name = "sequencer", +}; + +struct seq_softc { + KOBJ_FIELDS; + + struct mtx 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; + /* Flags (protected by flag_mtx of mididev_info) */ + int fflags; /* Access mode */ + int music; + + int out_water; /* Sequence output threshould */ + snd_sync_parm sync_parm; /* AIOSYNC parameter set */ + struct thread *sync_thread; /* AIOSYNCing thread */ + struct selinfo in_sel, out_sel; + 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; +}; + +/* + * Module specific stuff, including how many sequecers + * we currently own. + */ + +SYSCTL_NODE(_hw_midi, OID_AUTO, seq, CTLFLAG_RD, 0, "Midi sequencer"); + +int seq_debug; +/* XXX: should this be moved into debug.midi? */ +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 const char *mpu401_mprovider(kobj_t obj, struct mpu401 *m); + */ + +static kobj_method_t seq_methods[] = { + /* KOBJMETHOD(mpu_provider,mpu401_mprovider), */ + KOBJMETHOD_END +}; + +DEFINE_CLASS(sequencer, seq_methods, 0); + +/* The followings are the local function. */ +static int seq_convertold(u_char *event, u_char *out); + +/* + * static void seq_midiinput(struct seq_softc * scp, void *md); + */ +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 mtx seqinfo_mtx; +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, printf("Timer wait when timer isn't running\n")); + /* + * The old sequencer used timeouts that only increased + * the timer when the timer was running. + * Hence the sequencer would stick (?) if the + * timer was disabled. + */ + 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 + printf("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, + printf("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 + printf("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, printf("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 /= t->tempo; */ + i /= 1000000ull; + + ret = i; + /* + * printf("timer_now: %llu %d\n", i, ret); + */ + + return ret; +} + +static void +seq_eventthread(void *arg) +{ + struct seq_softc *scp = arg; + char event[EV_SZ]; + + mtx_lock(&scp->seq_lock); + SEQ_DEBUG(2, printf("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); + selwakeup(&scp->out_sel); + } + seq_processevent(scp, event); + } + +done: + cv_broadcast(&scp->th_cv); + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(2, printf("seq_eventthread finished\n")); + kproc_exit(0); +} + +/* + * seq_processevent: This maybe called by the event thread or the IOCTL + * handler for queued and out of band events respectively. + */ +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, printf("seq_processevent not known %d\n", + event[0])); + } else if (seq_fetch_mid(scp, event[1], &m) != 0) { + ret = 1; + SEQ_DEBUG(2, printf("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: + mtx_unlock(&scp->seq_lock); + ret = SYNTH_WRITERAW(m, &event[2], 1); + mtx_lock(&scp->seq_lock); + break; + } + return ret; +} + +static int +seq_addunit(void) +{ + struct seq_softc *scp; + int ret; + u_char *buf; + + /* Allocate the softc. */ + ret = ENOMEM; + scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT | M_ZERO); + if (scp == NULL) { + SEQ_DEBUG(1, printf("seq_addunit: softc allocation failed.\n")); + goto err; + } + kobj_init((kobj_t)scp, &sequencer_class); + + buf = malloc(sizeof(*buf) * EV_SZ * 1024, M_TEMP, M_NOWAIT | M_ZERO); + if (buf == NULL) + goto err; + MIDIQ_INIT(scp->in_q, buf, EV_SZ * 1024); + buf = malloc(sizeof(*buf) * EV_SZ * 1024, M_TEMP, M_NOWAIT | M_ZERO); + if (buf == NULL) + goto err; + MIDIQ_INIT(scp->out_q, buf, EV_SZ * 1024); + ret = EINVAL; + + scp->midis = malloc(sizeof(kobj_t) * 32, M_TEMP, M_NOWAIT | M_ZERO); + scp->midi_flags = malloc(sizeof(*scp->midi_flags) * 32, M_TEMP, + M_NOWAIT | M_ZERO); + + if (scp->midis == NULL || scp->midi_flags == NULL) + goto err; + + scp->flags = 0; + + mtx_init(&scp->seq_lock, "seqflq", NULL, 0); + 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"); + + /* + * Init the damn timer + */ + + scp->mapper = midimapper_addseq(scp, &scp->unit, &scp->mapper_cookie); + if (scp->mapper == NULL) + goto err; + + scp->seqdev = make_dev(&seq_cdevsw, + MIDIMKMINOR(scp->unit, SND_DEV_SEQ, 0), UID_ROOT, + GID_WHEEL, 0666, "sequencer%d", scp->unit); + + scp->musicdev = make_dev(&seq_cdevsw, + 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; + /* + * TODO: Add to list of sequencers this module provides + */ + + ret = + kproc_create + (seq_eventthread, scp, NULL, RFHIGHPID, 0, + "sequencer %02d", scp->unit); + + if (ret) + goto err; + + scp->seqdev->si_drv1 = scp->musicdev->si_drv1 = scp; + + SEQ_DEBUG(2, printf("sequencer %d created scp %p\n", scp->unit, scp)); + + ret = 0; + + mtx_lock(&seqinfo_mtx); + seqs[nseq++] = scp; + mtx_unlock(&seqinfo_mtx); + + goto ok; + +err: + if (scp != NULL) { + if (scp->seqdev != NULL) + destroy_dev(scp->seqdev); + if (scp->musicdev != NULL) + destroy_dev(scp->musicdev); + /* + * TODO: Destroy mutex and cv + */ + if (scp->midis != NULL) + free(scp->midis, M_TEMP); + if (scp->midi_flags != NULL) + free(scp->midi_flags, M_TEMP); + if (scp->out_q.b) + free(scp->out_q.b, M_TEMP); + if (scp->in_q.b) + free(scp->in_q.b, M_TEMP); + free(scp, M_DEVBUF); + } +ok: + return ret; +} + +static int +seq_delunit(int unit) +{ + struct seq_softc *scp = seqs[unit]; + int i; + + //SEQ_DEBUG(4, printf("seq_delunit: %d\n", unit)); + SEQ_DEBUG(1, printf("seq_delunit: 1 \n")); + mtx_lock(&scp->seq_lock); + + scp->playing = 0; + scp->done = 1; + cv_broadcast(&scp->out_cv); + cv_broadcast(&scp->state_cv); + cv_broadcast(&scp->reset_cv); + SEQ_DEBUG(1, printf("seq_delunit: 2 \n")); + cv_wait(&scp->th_cv, &scp->seq_lock); + SEQ_DEBUG(1, printf("seq_delunit: 3.0 \n")); + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(1, printf("seq_delunit: 3.1 \n")); + + cv_destroy(&scp->state_cv); + SEQ_DEBUG(1, printf("seq_delunit: 4 \n")); + cv_destroy(&scp->empty_cv); + SEQ_DEBUG(1, printf("seq_delunit: 5 \n")); + cv_destroy(&scp->reset_cv); + SEQ_DEBUG(1, printf("seq_delunit: 6 \n")); + cv_destroy(&scp->out_cv); + SEQ_DEBUG(1, printf("seq_delunit: 7 \n")); + cv_destroy(&scp->in_cv); + SEQ_DEBUG(1, printf("seq_delunit: 8 \n")); + cv_destroy(&scp->th_cv); + + SEQ_DEBUG(1, printf("seq_delunit: 10 \n")); + if (scp->seqdev) + destroy_dev(scp->seqdev); + SEQ_DEBUG(1, printf("seq_delunit: 11 \n")); + if (scp->musicdev) + destroy_dev(scp->musicdev); + SEQ_DEBUG(1, printf("seq_delunit: 12 \n")); + scp->seqdev = scp->musicdev = NULL; + if (scp->midis != NULL) + free(scp->midis, M_TEMP); + SEQ_DEBUG(1, printf("seq_delunit: 13 \n")); + if (scp->midi_flags != NULL) + free(scp->midi_flags, M_TEMP); + SEQ_DEBUG(1, printf("seq_delunit: 14 \n")); + free(scp->out_q.b, M_TEMP); + SEQ_DEBUG(1, printf("seq_delunit: 15 \n")); + free(scp->in_q.b, M_TEMP); + + SEQ_DEBUG(1, printf("seq_delunit: 16 \n")); + + mtx_destroy(&scp->seq_lock); + SEQ_DEBUG(1, printf("seq_delunit: 17 \n")); + free(scp, M_DEVBUF); + + mtx_lock(&seqinfo_mtx); + for (i = unit; i < (nseq - 1); i++) + seqs[i] = seqs[i + 1]; + nseq--; + mtx_unlock(&seqinfo_mtx); + + return 0; +} + +int +seq_modevent(module_t mod, int type, void *data) +{ + int retval, r; + + retval = 0; + + switch (type) { + case MOD_LOAD: + mtx_init(&seqinfo_mtx, "seqmod", NULL, 0); + retval = seq_addunit(); + break; + + case MOD_UNLOAD: + while (nseq) { + r = seq_delunit(nseq - 1); + if (r) { + retval = r; + break; + } + } + if (nseq == 0) { + retval = 0; + mtx_destroy(&seqinfo_mtx); + } + 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 cdev *i_dev, int flags, int mode, struct thread *td) +{ + struct seq_softc *scp = i_dev->si_drv1; + int i; + + if (scp == NULL) + return ENXIO; + + SEQ_DEBUG(3, printf("seq_open: scp %p unit %d, flags 0x%x.\n", + scp, scp->unit, flags)); + + /* + * Mark this device busy. + */ + + mtx_lock(&scp->seq_lock); + if (scp->busy) { + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(2, printf("seq_open: unit %d is busy.\n", scp->unit)); + return EBUSY; + } + scp->fflags = flags; + /* + if ((scp->fflags & O_NONBLOCK) != 0) + scp->flags |= SEQ_F_NBIO; + */ + scp->music = MIDIDEV(i_dev) == SND_DEV_MUSIC; + + /* + * Enumerate the available midi devices + */ + scp->midi_number = 0; + scp->maxunits = midimapper_open(scp->mapper, &scp->mapper_cookie); + + if (scp->maxunits == 0) + SEQ_DEBUG(2, printf("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); + /* + * actually, if we're in rdonly mode, we should start the timer + */ + /* + * TODO: Handle recording now + */ + + scp->out_water = MIDIQ_SIZE(scp->out_q) / 2; + + scp->busy = 1; + mtx_unlock(&scp->seq_lock); + + SEQ_DEBUG(2, printf("seq_open: opened, mode %s.\n", + scp->music ? "music" : "sequencer")); + SEQ_DEBUG(2, + printf("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, printf(" midi %d %p\n", i, scp->midis[i])); + + return 0; +} + +/* + * seq_close + */ +int +seq_close(struct cdev *i_dev, int flags, int mode, struct thread *td) +{ + int i; + struct seq_softc *scp = i_dev->si_drv1; + int ret; + + if (scp == NULL) + return ENXIO; + + SEQ_DEBUG(2, printf("seq_close: unit %d.\n", scp->unit)); + + mtx_lock(&scp->seq_lock); + + 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, printf("seq_close: closed ret = %d.\n", ret)); + mtx_unlock(&scp->seq_lock); + return ret; +} + +int +seq_read(struct cdev *i_dev, struct uio *uio, int 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, printf("seq_read: unit %d, resid %zd.\n", + scp->unit, uio->uio_resid)); + + mtx_lock(&scp->seq_lock); + if ((scp->fflags & FREAD) == 0) { + SEQ_DEBUG(2, printf("seq_read: unit %d is not for reading.\n", + scp->unit)); + retval = EIO; + goto err1; + } + /* + * Begin recording. + */ + /* + * if ((scp->flags & SEQ_F_READING) == 0) + */ + /* + * TODO, start recording if not alread + */ + + /* + * I think the semantics are to return as soon + * as possible. + * Second thought, it doens't seem like midimoutain + * expects that at all. + * TODO: Look up in some sort of spec + */ + + while (uio->uio_resid > 0) { + while (MIDIQ_EMPTY(scp->in_q)) { + retval = EWOULDBLOCK; + /* + * I wish I knew which one to care about + */ + + 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, printf("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: + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(6, printf("seq_read: ret %d, resid %zd.\n", + retval, uio->uio_resid)); + + return retval; +} + +int +seq_write(struct cdev *i_dev, struct uio *uio, int 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, printf("seq_write: unit %d, resid %zd.\n", + scp->unit, uio->uio_resid)); + + if (scp == NULL) + return ENXIO; + + mtx_lock(&scp->seq_lock); + + if ((scp->fflags & FWRITE) == 0) { + SEQ_DEBUG(2, printf("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, printf("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); + /* + * We slept, maybe things have changed since last + * dying check + */ + if (retval == EINTR) + goto err0; +#if 0 + /* + * Useless test + */ + if (scp != i_dev->si_drv1) + retval = ENXIO; +#endif + } + + used = MIN(uio->uio_resid, 4); + + SEQ_DEBUG(8, printf("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, printf("seq_write: unit %d, event %s.\n", + scp->unit, midi_cmdname(ev_code, cmdtab_seqevent))); + + /* Have a look at the event code. */ + if (ev_code == SEQ_FULLSIZE) { + + /* + * TODO: restore code for SEQ_FULLSIZE + */ +#if 0 + /* + * A long event, these are the patches/samples for a + * synthesizer. + */ + midiunit = *(u_short *)&event[2]; + mtx_lock(&sd->seq_lock); + ret = lookup_mididev(scp, midiunit, LOOKUP_OPEN, &md); + mtx_unlock(&sd->seq_lock); + if (ret != 0) + return (ret); + + SEQ_DEBUG(printf("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 + /* + * For now, just flush the darn buffer + */ + SEQ_DEBUG(2, + printf("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) { + + /* + * Some sort of an extended event. The size is eight + * bytes. scoop extra info. + */ + if (scp->music && ev_code == SEQ_EXTENDED) { + SEQ_DEBUG(2, printf("seq_write: invalid level two event %x.\n", ev_code)); + goto err0; + } + if (uiomove((caddr_t)&event[4], 4, uio)) { + SEQ_DEBUG(2, + printf("seq_write: user memory mangled?\n")); + goto err0; + } + } else { + /* + * Size four event. + */ + if (scp->music) { + SEQ_DEBUG(2, printf("seq_write: four byte event in music mode.\n")); + goto err0; + } + } + if (ev_code == SEQ_MIDIPUTC) { + /* + * TODO: event[2] is unit number to receive char. + * Range check it. + */ + } + if (scp->music) { +#ifdef not_ever_ever + if (event[0] == EV_TIMING && + (event[1] == TMR_START || event[1] == TMR_STOP)) { + /* + * For now, try to make midimoutain work by + * forcing these events to be processed + * immediatly. + */ + 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, + printf("seq_write done: leftover buffer length %zd retval %d\n", + uio->uio_resid, retval)); + mtx_unlock(&scp->seq_lock); + return retval; +} + +int +seq_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, + struct thread *td) +{ + 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; + + /* + * struct snd_size *sndsize; + */ + + if (scp == NULL) + return ENXIO; + + SEQ_DEBUG(6, printf("seq_ioctl: unit %d, cmd %s.\n", + scp->unit, midi_cmdname(cmd, cmdtab_seqioctl))); + + ret = 0; + + switch (cmd) { + case SNDCTL_SEQ_GETTIME: + /* + * ioctl needed by libtse + */ + mtx_lock(&scp->seq_lock); + *(int *)arg = timer_now(scp); + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(6, printf("seq_ioctl: gettime %d.\n", *(int *)arg)); + ret = 0; + break; + case SNDCTL_TMR_METRONOME: + /* fallthrough */ + case SNDCTL_TMR_SOURCE: + /* + * Not implemented + */ + 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; + mtx_lock(&scp->seq_lock); + if (!scp->music) { + ret = EINVAL; + mtx_unlock(&scp->seq_lock); + break; + } + seq_processevent(scp, event); + mtx_unlock(&scp->seq_lock); + break; + case SNDCTL_TMR_SELECT: + SEQ_DEBUG(2, + printf("seq_ioctl: SNDCTL_TMR_SELECT not supported\n")); + ret = EINVAL; + break; + case SNDCTL_SEQ_SYNC: + if (mode == O_RDONLY) { + ret = 0; + break; + } + mtx_lock(&scp->seq_lock); + ret = seq_sync(scp); + mtx_unlock(&scp->seq_lock); + break; + case SNDCTL_SEQ_PANIC: + /* fallthrough */ + case SNDCTL_SEQ_RESET: + /* + * SNDCTL_SEQ_PANIC == SNDCTL_SEQ_RESET + */ + mtx_lock(&scp->seq_lock); + seq_reset(scp); + mtx_unlock(&scp->seq_lock); + ret = 0; + break; + case SNDCTL_SEQ_TESTMIDI: + mtx_lock(&scp->seq_lock); + /* + * TODO: SNDCTL_SEQ_TESTMIDI now means "can I write to the + * device?". + */ + mtx_unlock(&scp->seq_lock); + break; +#if 0 + case SNDCTL_SEQ_GETINCOUNT: + if (mode == O_WRONLY) + *(int *)arg = 0; + else { + mtx_lock(&scp->seq_lock); + *(int *)arg = scp->in_q.rl; + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(printf("seq_ioctl: incount %d.\n", + *(int *)arg)); + } + ret = 0; + break; + case SNDCTL_SEQ_GETOUTCOUNT: + if (mode == O_RDONLY) + *(int *)arg = 0; + else { + mtx_lock(&scp->seq_lock); + *(int *)arg = scp->out_q.fl; + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(printf("seq_ioctl: outcount %d.\n", + *(int *)arg)); + } + ret = 0; + break; +#endif + case SNDCTL_SEQ_CTRLRATE: + if (*(int *)arg != 0) { + ret = EINVAL; + break; + } + mtx_lock(&scp->seq_lock); + *(int *)arg = scp->timerbase; + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(3, printf("seq_ioctl: ctrlrate %d.\n", *(int *)arg)); + ret = 0; + break; + /* + * TODO: ioctl SNDCTL_SEQ_RESETSAMPLES + */ +#if 0 + case SNDCTL_SEQ_RESETSAMPLES: + mtx_lock(&scp->seq_lock); + ret = lookup_mididev(scp, *(int *)arg, LOOKUP_OPEN, &md); + mtx_unlock(&scp->seq_lock); + 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: + mtx_lock(&scp->seq_lock); + *(int *)arg = scp->midi_number; + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(3, printf("seq_ioctl: synths %d.\n", *(int *)arg)); + ret = 0; + break; + case SNDCTL_SEQ_NRMIDIS: + mtx_lock(&scp->seq_lock); + if (scp->music) + *(int *)arg = 0; + else { + /* + * TODO: count the numbder of devices that can WRITERAW + */ + *(int *)arg = scp->midi_number; + } + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(3, printf("seq_ioctl: midis %d.\n", *(int *)arg)); + ret = 0; + break; + /* + * TODO: ioctl SNDCTL_SYNTH_MEMAVL + */ +#if 0 + case SNDCTL_SYNTH_MEMAVL: + mtx_lock(&scp->seq_lock); + ret = lookup_mididev(scp, *(int *)arg, LOOKUP_OPEN, &md); + mtx_unlock(&scp->seq_lock); + 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]; + + mtx_lock(&scp->seq_lock); + if (scp->music) + ret = seq_processevent(scp, event); + else { + if (seq_convertold(event, newevent) > 0) + ret = seq_processevent(scp, newevent); + else + ret = EINVAL; + } + mtx_unlock(&scp->seq_lock); + break; + case SNDCTL_SYNTH_INFO: + synthinfo = (struct synth_info *)arg; + midiunit = synthinfo->device; + mtx_lock(&scp->seq_lock); + 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; + mtx_unlock(&scp->seq_lock); + break; + case SNDCTL_MIDI_INFO: + midiinfo = (struct midi_info *)arg; + midiunit = midiinfo->device; + mtx_lock(&scp->seq_lock); + 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]; + /* + * TODO: What devtype? + */ + midiinfo->dev_type = 0x01; + ret = 0; + } else + ret = EINVAL; + mtx_unlock(&scp->seq_lock); + break; + case SNDCTL_SEQ_THRESHOLD: + mtx_lock(&scp->seq_lock); + RANGE(*(int *)arg, 1, MIDIQ_SIZE(scp->out_q) - 1); + scp->out_water = *(int *)arg; + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(3, printf("seq_ioctl: water %d.\n", *(int *)arg)); + ret = 0; + break; + case SNDCTL_MIDI_PRETIME: + tmp = *(int *)arg; + if (tmp < 0) + tmp = 0; + mtx_lock(&scp->seq_lock); + scp->pre_event_timeout = (hz * tmp) / 10; + *(int *)arg = scp->pre_event_timeout; + mtx_unlock(&scp->seq_lock); + SEQ_DEBUG(3, printf("seq_ioctl: pretime %d.\n", *(int *)arg)); + ret = 0; + break; + case SNDCTL_FM_4OP_ENABLE: + case SNDCTL_PMGR_IFACE: + case SNDCTL_PMGR_ACCESS: + /* + * Patch manager and fm are ded, ded, ded. + */ + /* fallthrough */ + default: + /* + * TODO: Consider ioctl default case. + * Old code used to + * if ((scp->fflags & O_ACCMODE) == FREAD) { + * ret = EIO; + * break; + * } + * Then pass on the ioctl to device 0 + */ + SEQ_DEBUG(2, + printf("seq_ioctl: unsupported IOCTL %ld.\n", cmd)); + ret = EINVAL; + break; + } + + return ret; +} + +int +seq_poll(struct cdev *i_dev, int events, struct thread *td) +{ + int ret, lim; + struct seq_softc *scp = i_dev->si_drv1; + + SEQ_DEBUG(3, printf("seq_poll: unit %d.\n", scp->unit)); + SEQ_DEBUG(1, printf("seq_poll: unit %d.\n", scp->unit)); + + mtx_lock(&scp->seq_lock); + + ret = 0; + + /* Look up the apropriate queue and select it. */ + if ((events & (POLLOUT | POLLWRNORM)) != 0) { + /* Start playing. */ + scp->playing = 1; + cv_broadcast(&scp->state_cv); + cv_broadcast(&scp->out_cv); + + lim = scp->out_water; + + if (MIDIQ_AVAIL(scp->out_q) < lim) + /* No enough space, record select. */ + selrecord(td, &scp->out_sel); + else + /* We can write now. */ + ret |= events & (POLLOUT | POLLWRNORM); + } + if ((events & (POLLIN | POLLRDNORM)) != 0) { + /* TODO: Start recording. */ + + /* Find out the boundary. */ + lim = 1; + if (MIDIQ_LEN(scp->in_q) < lim) + /* No data ready, record select. */ + selrecord(td, &scp->in_sel); + else + /* We can read now. */ + ret |= events & (POLLIN | POLLRDNORM); + } + mtx_unlock(&scp->seq_lock); + + return (ret); +} + +#if 0 +static void +sein_qtr(void *p, void /* mididev_info */ *md) +{ + struct seq_softc *scp; + + scp = (struct seq_softc *)p; + + mtx_lock(&scp->seq_lock); + + /* Restart playing if we have the data to output. */ + if (scp->queueout_pending) + seq_callback(scp, SEQ_CB_START | SEQ_CB_WR); + /* Check the midi device if we are reading. */ + if ((scp->flags & SEQ_F_READING) != 0) + seq_midiinput(scp, md); + + mtx_unlock(&scp->seq_lock); +} + +#endif +/* + * seq_convertold + * Was the old playevent. Use this to convert and old + * style /dev/sequencer event to a /dev/music event + */ +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: + /* + * TODO: Debug statement + */ + 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; + + /* + * wait delay = (event[2] << 16) + (event[3] << 8) + event[4] + */ + + 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; +/* + out[0] = EV_TIMING; + out[1] = dev; + out[2] = MIDI_PGM_CHANGE; + out[3] = chn; + out[4] = note; + out[5] = vel; + SEQ_DEBUG(4,printf("seq_playevent: synctimer\n")); + break; +*/ + + case SEQ_MIDIPUTC: + SEQ_DEBUG(4, + printf("seq_playevent: put data 0x%02x, unit %d.\n", + event[1], event[2])); + /* + * Pass through to the midi device. + * device = event[2] + * data = event[1] + */ + out[0] = SEQ_MIDIPUTC; + out[1] = dev; + out[2] = chn; + used += 4; + break; +#ifdef notyet + case SEQ_ECHO: + /* + * This isn't handled here yet because I don't know if I can + * just use four bytes events. There might be consequences + * in the _read routing + */ + 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: + /* + * SYNTH_AFTERTOUCH(md, event[3], event[4]) + */ + case SEQ_BALANCE: + /* + * SYNTH_PANNING(md, event[3], (char)event[4]) + */ + case SEQ_CONTROLLER: + /* + * SYNTH_CONTROLLER(md, event[3], event[4], *(short *)&event[5]) + */ + case SEQ_VOLMODE: + /* + * SYNTH_VOLUMEMETHOD(md, event[3]) + */ + default: + SEQ_DEBUG(2, + printf("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, printf("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, + printf("seq_convertold: event type %d not handled %d %d %d\n", + event[0], event[1], event[2], event[3])); + break; + } + return used; +} + +/* + * Writting to the sequencer buffer never blocks and drops + * input which cannot be queued + */ +void +seq_copytoinput(struct seq_softc *scp, u_char *event, int len) +{ + + mtx_assert(&scp->seq_lock, MA_OWNED); + + if (MIDIQ_AVAIL(scp->in_q) < len) { + /* + * ENOROOM? EINPUTDROPPED? ETOUGHLUCK? + */ + SEQ_DEBUG(2, printf("seq_copytoinput: queue full\n")); + } else { + MIDIQ_ENQ(scp->in_q, event, len); + selwakeup(&scp->in_sel); + 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]; + + mtx_assert(&scp->seq_lock, MA_OWNED); + + SEQ_DEBUG(5, printf("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); + + mtx_unlock(&scp->seq_lock); + + switch (cmd) { + case MIDI_NOTEON: + if (note < 128 || note == 255) { +#if 0 + if (scp->music && chn == 9) { + /* + * This channel is a percussion. The note + * number is the patch number. + */ + /* + mtx_unlock(&scp->seq_lock); + if (SYNTH_SETINSTR(md, voice, 128 + note) + == EAGAIN) { + mtx_lock(&scp->seq_lock); + return (QUEUEFULL); + } + mtx_lock(&scp->seq_lock); + */ + note = 60; /* Middle C. */ + } +#endif + if (scp->music) { + /* + mtx_unlock(&scp->seq_lock); + if (SYNTH_SETUPVOICE(md, voice, chn) + == EAGAIN) { + mtx_lock(&scp->seq_lock); + return (QUEUEFULL); + } + mtx_lock(&scp->seq_lock); + */ + } + 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, printf("seq_chnvoice event type %d not handled\n", + event[1])); + break; + } + + mtx_lock(&scp->seq_lock); + 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, printf("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)); + mtx_unlock(&scp->seq_lock); + switch (cmd) { + case MIDI_PGM_CHANGE: + SEQ_DEBUG(4, printf("seq_chncommon pgmchn chn %d pg %d\n", + chn, p1)); + SYNTH_SETINSTR(md, chn, p1); + break; + case MIDI_CTL_CHANGE: + SEQ_DEBUG(4, printf("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) { + /* + * TODO: MIDI_PITCH_BEND + */ +#if 0 + mtx_lock(&md->synth.vc_mtx); + md->synth.chn_info[chn].bender_value = w14; + if (md->midiunit >= 0) { + /* + * Handle all of the notes playing on this + * channel. + */ + key = ((int)chn << 8); + for (i = 0; i < md->synth.alloc.max_voice; i++) + if ((md->synth.alloc.map[i] & 0xff00) == key) { + mtx_unlock(&md->synth.vc_mtx); + mtx_unlock(&scp->seq_lock); + if (md->synth.bender(md, i, w14) == EAGAIN) { + mtx_lock(&scp->seq_lock); + return (QUEUEFULL); + } + mtx_lock(&scp->seq_lock); + } + } else { + mtx_unlock(&md->synth.vc_mtx); + mtx_unlock(&scp->seq_lock); + if (md->synth.bender(md, chn, w14) == EAGAIN) { + mtx_lock(&scp->seq_lock); + return (QUEUEFULL); + } + mtx_lock(&scp->seq_lock); + } +#endif + } else + SYNTH_BENDER(md, chn, w14); + break; + default: + ret = 1; + SEQ_DEBUG(2, + printf("seq_chncommon event type %d not handled.\n", + event[1])); + break; + + } + mtx_lock(&scp->seq_lock); + 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, printf("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); + /* + * The following cv_broadcast isn't needed since we only + * wait for 0->1 transitions. It probably won't hurt + */ + 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, printf("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, printf("Timer set timerbase %d\n", param)); + timer_setvals(scp, scp->tempo, param); + break; + case TMR_ECHO: + /* + * TODO: Consider making 4-byte events for /dev/sequencer + * PRO: Maybe needed by legacy apps + * CON: soundcard.h has been warning for a while many years + * to expect 8 byte events. + */ +#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, printf("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; + mtx_assert(&scp->seq_lock, MA_OWNED); + + SEQ_DEBUG(5, printf("seq_local: unit %d, cmd %d\n", scp->unit, + event[1])); + switch (event[1]) { + default: + SEQ_DEBUG(1, printf("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; + + mtx_assert(&scp->seq_lock, MA_OWNED); + SEQ_DEBUG(5, printf("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) { + mtx_unlock(&scp->seq_lock); + if (SYNTH_SENDSYSEX(md, &event[2], l) == EAGAIN) { + mtx_lock(&scp->seq_lock); + return 1; + } + mtx_lock(&scp->seq_lock); + } + return 0; +} + +/* + * Reset no longer closes the raw devices nor seq_sync's + * Callers are IOCTL and seq_close + */ +static void +seq_reset(struct seq_softc *scp) +{ + int chn, i; + kobj_t m; + + mtx_assert(&scp->seq_lock, MA_OWNED); + + SEQ_DEBUG(5, printf("seq_reset: unit %d.\n", scp->unit)); + + /* + * Stop reading and writing. + */ + + /* scp->recording = 0; */ + scp->playing = 0; + cv_broadcast(&scp->state_cv); + cv_broadcast(&scp->out_cv); + cv_broadcast(&scp->reset_cv); + + /* + * For now, don't reset the timers. + */ + MIDIQ_CLEAR(scp->in_q); + MIDIQ_CLEAR(scp->out_q); + + for (i = 0; i < scp->midi_number; i++) { + m = scp->midis[i]; + mtx_unlock(&scp->seq_lock); + 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); + } + mtx_lock(&scp->seq_lock); + } +} + +/* + * seq_sync + * *really* flush the output queue + * flush the event queue, then flush the synthsisers. + * Callers are IOCTL and close + */ + +#define SEQ_SYNC_TIMEOUT 8 +static int +seq_sync(struct seq_softc *scp) +{ + int i, rl, sync[16], done; + + mtx_assert(&scp->seq_lock, MA_OWNED); + + SEQ_DEBUG(4, printf("seq_sync: unit %d.\n", scp->unit)); + + /* + * Wait until output queue is empty. Check every so often to see if + * the queue is moving along. If it isn't just abort. + */ + 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) { + /* + * XXX: I don't know why we stop playing + */ + scp->playing = 0; + cv_broadcast(&scp->out_cv); + } + return i; + } + if (i == EWOULDBLOCK && rl == MIDIQ_LEN(scp->out_q) && + scp->waiting == 0) { + /* + * A queue seems to be stuck up. Give up and clear + * queues. + */ + MIDIQ_CLEAR(scp->out_q); + scp->playing = 0; + cv_broadcast(&scp->state_cv); + cv_broadcast(&scp->out_cv); + cv_broadcast(&scp->reset_cv); + + /* + * TODO: Consider if the raw devices need to be flushed + */ + + SEQ_DEBUG(1, printf("seq_sync queue stuck, aborting\n")); + + return i; + } + } + + scp->playing = 0; + /* + * Since syncing a midi device might block, unlock scp->seq_lock. + */ + + mtx_unlock(&scp->seq_lock); + 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); + + mtx_lock(&scp->seq_lock); + return 0; +} + +char * +midi_cmdname(int cmd, midi_cmdtab *tab) +{ + while (tab->name != NULL) { + if (cmd == tab->cmd) + return (tab->name); + tab++; + } + + return ("unknown"); +} diff --git a/sys/dev/sound/midi/sequencer.h b/sys/dev/sound/midi/sequencer.h new file mode 100644 index 0000000000..797e9da06d --- /dev/null +++ b/sys/dev/sound/midi/sequencer.h @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2003 Mathew Kanner + * 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: head/sys/dev/sound/midi/sequencer.h 193640 2009-06-07 19:12:08Z ariff $ + */ + +/* + * Include file for the midi sequence driver. + */ + +#ifndef _SEQUENCER_H_ +#define _SEQUENCER_H_ + + +#define NSEQ_MAX 16 + +/* + * many variables should be reduced to a range. Here define a macro + */ + +#define RANGE(var, low, high) (var) = \ +((var)<(low)?(low) : (var)>(high)?(high) : (var)) + +#ifdef _KERNEL + +void seq_timer(void *arg); + +SYSCTL_DECL(_hw_midi_seq); + +extern int seq_debug; + +#define SEQ_DEBUG(y, x) \ + do { \ + if (seq_debug >= y) { \ + (x); \ + } \ + } while (0) + +SYSCTL_DECL(_hw_midi); + +#endif /* _KERNEL */ + +#define SYNTHPROP_MIDI 1 +#define SYNTHPROP_SYNTH 2 +#define SYNTHPROP_RX 4 +#define SYNTHPROP_TX 8 + +struct _midi_cmdtab { + int cmd; + char *name; +}; +typedef struct _midi_cmdtab midi_cmdtab; +extern midi_cmdtab cmdtab_seqevent[]; +extern midi_cmdtab cmdtab_seqioctl[]; +extern midi_cmdtab cmdtab_timer[]; +extern midi_cmdtab cmdtab_seqcv[]; +extern midi_cmdtab cmdtab_seqccmn[]; + +char *midi_cmdname(int cmd, midi_cmdtab * tab); + +enum { + MORE, + TIMERARMED, + QUEUEFULL +}; + +#endif diff --git a/sys/dev/sound/midi/synth_if.m b/sys/dev/sound/midi/synth_if.m new file mode 100644 index 0000000000..faef209c1f --- /dev/null +++ b/sys/dev/sound/midi/synth_if.m @@ -0,0 +1,313 @@ +#- +# Copyright (c) 2003 Mathew Kanner +# 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: head/sys/dev/sound/midi/synth_if.m 166971 2007-02-25 13:51:52Z netchild $ +# + +INTERFACE synth; + +#include + +CODE { + +synth_killnote_t nokillnote; +synth_startnote_t nostartnote; +synth_setinstr_t nosetinstr; +synth_hwcontrol_t nohwcontrol; +synth_aftertouch_t noaftertouch; +synth_panning_t nopanning; +synth_controller_t nocontroller; +synth_volumemethod_t novolumemethod; +synth_bender_t nobender; +synth_setupvoice_t nosetupvoice; +synth_sendsysex_t nosendsysex; +synth_allocvoice_t noallocvoice; +synth_writeraw_t nowriteraw; +synth_reset_t noreset; +synth_shortname_t noshortname; +synth_open_t noopen; +synth_close_t noclose; +synth_query_t noquery; +synth_insync_t noinsync; +synth_alloc_t noalloc; + + int + nokillnote(void *_kobj, uint8_t _chn, uint8_t _note, uint8_t _vel) + { + printf("nokillnote\n"); + return 0; + } + + int + noopen(void *_kobj, void *_arg, int mode) + { + printf("noopen\n"); + return 0; + } + + int + noquery(void *_kboj) + { + printf("noquery\n"); + return 0; + } + + int + nostartnote(void *_kb, uint8_t _voice, uint8_t _note, uint8_t _parm) + { + printf("nostartnote\n"); + return 0; + } + + int + nosetinstr(void *_kb, uint8_t _chn, uint16_t _patchno) + { + printf("nosetinstr\n"); + return 0; + } + + int + nohwcontrol(void *_kb, uint8_t *_event) + { + printf("nohwcontrol\n"); + return 0; + } + + int + noaftertouch ( void /* X */ * _kobj, uint8_t _x1, uint8_t _x2) + { + printf("noaftertouch\n"); + return 0; + } + + int + nopanning ( void /* X */ * _kobj, uint8_t _x1, uint8_t _x2) + { + printf("nopanning\n"); + return 0; + } + + int + nocontroller ( void /* X */ * _kobj, uint8_t _x1, uint8_t _x2, uint16_t _x3) + { + printf("nocontroller\n"); + return 0; + } + + int + novolumemethod ( + void /* X */ * _kobj, + uint8_t _x1) + { + printf("novolumemethod\n"); + return 0; + } + + int + nobender ( void /* X */ * _kobj, uint8_t _voice, uint16_t _bend) + { + printf("nobender\n"); + return 0; + } + + int + nosetupvoice ( void /* X */ * _kobj, uint8_t _voice, uint8_t _chn) + { + + printf("nosetupvoice\n"); + return 0; + } + + int + nosendsysex ( void /* X */ * _kobj, void * _buf, size_t _len) + { + printf("nosendsysex\n"); + return 0; + } + + int + noallocvoice ( void /* X */ * _kobj, uint8_t _chn, uint8_t _note, void *_x) + { + printf("noallocvoice\n"); + return 0; + } + + int + nowriteraw ( void /* X */ * _kobjt, uint8_t * _buf, size_t _len) + { + printf("nowriteraw\n"); + return 1; + } + + int + noreset ( void /* X */ * _kobjt) + { + + printf("noreset\n"); + return 0; + } + + char * + noshortname (void /* X */ * _kobjt) + { + printf("noshortname\n"); + return "noshortname"; + } + + int + noclose ( void /* X */ * _kobjt) + { + + printf("noclose\n"); + return 0; + } + + int + noinsync (void /* X */ * _kobjt) + { + + printf("noinsync\n"); + return 0; + } + + int + noalloc ( void /* x */ * _kbojt, uint8_t _chn, uint8_t _note) + { + printf("noalloc\n"); + return 0; + } +} + +METHOD int killnote { + void /* X */ *_kobj; + uint8_t _chan; + uint8_t _note; + uint8_t _vel; +} DEFAULT nokillnote; + +METHOD int startnote { + void /* X */ *_kobj; + uint8_t _voice; + uint8_t _note; + uint8_t _parm; +} DEFAULT nostartnote; + +METHOD int setinstr { + void /* X */ *_kobj; + uint8_t _chn; + uint16_t _patchno; +} DEFAULT nosetinstr; + +METHOD int hwcontrol { + void /* X */ *_kobj; + uint8_t *_event; +} DEFAULT nohwcontrol; + +METHOD int aftertouch { + void /* X */ *_kobj; + uint8_t _x1; + uint8_t _x2; +} DEFAULT noaftertouch; + +METHOD int panning { + void /* X */ *_kobj; + uint8_t _x1; + uint8_t _x2; +} DEFAULT nopanning; + +METHOD int controller { + void /* X */ *_kobj; + uint8_t _x1; + uint8_t _x2; + uint16_t _x3; +} DEFAULT nocontroller; + +METHOD int volumemethod { + void /* X */ *_kobj; + uint8_t _x1; +} DEFAULT novolumemethod; + +METHOD int bender { + void /* X */ *_kobj; + uint8_t _voice; + uint16_t _bend; +} DEFAULT nobender; + +METHOD int setupvoice { + void /* X */ *_kobj; + uint8_t _voice; + uint8_t _chn; +} DEFAULT nosetupvoice; + +METHOD int sendsysex { + void /* X */ *_kobj; + void *_buf; + size_t _len; +} DEFAULT nosendsysex; + +METHOD int allocvoice { + void /* X */ *_kobj; + uint8_t _chn; + uint8_t _note; + void *_x; +} DEFAULT noallocvoice; + +METHOD int writeraw { + void /* X */ *_kobjt; + uint8_t *_buf; + size_t _len; +} DEFAULT nowriteraw; + +METHOD int reset { + void /* X */ *_kobjt; +} DEFAULT noreset; + +METHOD char * shortname { + void /* X */ *_kobjt; +} DEFAULT noshortname; + +METHOD int open { + void /* X */ *_kobjt; + void *_sythn; + int _mode; +} DEFAULT noopen; + +METHOD int close { + void /* X */ *_kobjt; +} DEFAULT noclose; + +METHOD int query { + void /* X */ *_kobjt; +} DEFAULT noquery; + +METHOD int insync { + void /* X */ *_kobjt; +} DEFAULT noinsync; + +METHOD int alloc { + void /* x */ *_kbojt; + uint8_t _chn; + uint8_t _note; +} DEFAULT noalloc; diff --git a/sys/dev/sound/pci/cmi.c b/sys/dev/sound/pci/cmi.c index b770595c22..a2d82fdc65 100644 --- a/sys/dev/sound/pci/cmi.c +++ b/sys/dev/sound/pci/cmi.c @@ -54,8 +54,10 @@ #include #include +#include #include "mixer_if.h" +#include "mpufoi_if.h" SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/cmi.c 254263 2013-08-12 23:30:01Z scottl $"); @@ -119,6 +121,13 @@ struct sc_info { int spdif_enabled; unsigned int bufsz; struct sc_chinfo pch, rch; + + struct mpu401 *mpu; + mpu401_intr_t *mpu_intr; + struct resource *mpu_reg; + int mpu_regid; + bus_space_tag_t mpu_bt; + bus_space_handle_t mpu_bh; }; /* Channel caps */ @@ -563,6 +572,9 @@ cmi_intr(void *data) } } + if(sc->mpu_intr) { + (sc->mpu_intr)(sc->mpu); + } snd_mtxunlock(sc->lock); return; } @@ -765,6 +777,75 @@ static kobj_method_t cmi_mixer_methods[] = { }; MIXER_DECLARE(cmi_mixer); +/* + * mpu401 functions + */ + +static unsigned char +cmi_mread(struct mpu401 *arg, void *sc, int reg) +{ + unsigned int d; + + d = bus_space_read_1(0,0, 0x330 + reg); + /* printf("cmi_mread: reg %x %x\n",reg, d); + */ + return d; +} + +static void +cmi_mwrite(struct mpu401 *arg, void *sc, int reg, unsigned char b) +{ + + bus_space_write_1(0,0,0x330 + reg , b); +} + +static int +cmi_muninit(struct mpu401 *arg, void *cookie) +{ + struct sc_info *sc = cookie; + + snd_mtxlock(sc->lock); + sc->mpu_intr = 0; + sc->mpu = 0; + snd_mtxunlock(sc->lock); + + return 0; +} + +static kobj_method_t cmi_mpu_methods[] = { + KOBJMETHOD(mpufoi_read, cmi_mread), + KOBJMETHOD(mpufoi_write, cmi_mwrite), + KOBJMETHOD(mpufoi_uninit, cmi_muninit), + KOBJMETHOD_END +}; + +static DEFINE_CLASS(cmi_mpu, cmi_mpu_methods, 0); + +static void +cmi_midiattach(struct sc_info *sc) { +/* + const struct { + int port,bits; + } *p, ports[] = { + {0x330,0}, + {0x320,1}, + {0x310,2}, + {0x300,3}, + {0,0} } ; + Notes, CMPCI_REG_VMPUSEL sets the io port for the mpu. Does + anyone know how to bus_space tag? +*/ + cmi_clr4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_UART_ENABLE); + cmi_clr4(sc, CMPCI_REG_LEGACY_CTRL, + CMPCI_REG_VMPUSEL_MASK << CMPCI_REG_VMPUSEL_SHIFT); + cmi_set4(sc, CMPCI_REG_LEGACY_CTRL, + 0 << CMPCI_REG_VMPUSEL_SHIFT ); + cmi_set4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_UART_ENABLE); + sc->mpu = mpu401_init(&cmi_mpu_class, sc, cmi_intr, &sc->mpu_intr); +} + + + /* ------------------------------------------------------------------------- */ /* Power and reset */ @@ -821,6 +902,9 @@ cmi_uninit(struct sc_info *sc) cmi_clr4(sc, CMPCI_REG_FUNC_0, CMPCI_REG_CH0_ENABLE | CMPCI_REG_CH1_ENABLE); cmi_clr4(sc, CMPCI_REG_FUNC_1, CMPCI_REG_UART_ENABLE); + + if( sc->mpu ) + sc->mpu_intr = 0; } /* ------------------------------------------------------------------------- */ @@ -870,6 +954,9 @@ cmi_attach(device_t dev) sc->st = rman_get_bustag(sc->reg); sc->sh = rman_get_bushandle(sc->reg); + if (0) + cmi_midiattach(sc); + sc->irqid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, RF_ACTIVE | RF_SHAREABLE); @@ -948,7 +1035,12 @@ cmi_detach(device_t dev) bus_dma_tag_destroy(sc->parent_dmat); bus_teardown_intr(dev, sc->irq, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); + if(sc->mpu) + mpu401_uninit(sc->mpu); bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg); + if (sc->mpu_reg) + bus_release_resource(dev, SYS_RES_IOPORT, sc->mpu_regid, sc->mpu_reg); + snd_mtxfree(sc->lock); kfree(sc, M_DEVBUF); @@ -1019,4 +1111,5 @@ static driver_t cmi_driver = { DRIVER_MODULE(snd_cmi, pci, cmi_driver, pcm_devclass, NULL, NULL); MODULE_DEPEND(snd_cmi, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_DEPEND(snd_cmi, midi, 1,1,1); MODULE_VERSION(snd_cmi, 1); diff --git a/sys/dev/sound/pci/emu10k1.c b/sys/dev/sound/pci/emu10k1.c index 7b8912b84b..362b71a84b 100644 --- a/sys/dev/sound/pci/emu10k1.c +++ b/sys/dev/sound/pci/emu10k1.c @@ -38,6 +38,9 @@ #include #include +#include +#include "mpufoi_if.h" + SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/emu10k1.c 267581 2014-06-17 16:07:57Z jhb $"); /* -------------------------------------------------------------------- */ @@ -225,6 +228,9 @@ struct sc_info { struct emu_voice voice[64]; struct sc_pchinfo pch[EMU_MAX_CHANS]; struct sc_rchinfo rch[3]; + struct mpu401 *mpu; + mpu401_intr_t *mpu_intr; + int mputx; }; /* -------------------------------------------------------------------- */ @@ -1150,6 +1156,63 @@ static kobj_method_t emurchan_methods[] = { }; CHANNEL_DECLARE(emurchan); +static unsigned char +emu_mread(struct mpu401 *arg, void *sc, int reg) +{ + unsigned int d; + + d = emu_rd((struct sc_info *)sc, 0x18 + reg, 1); + return d; +} + +static void +emu_mwrite(struct mpu401 *arg, void *sc, int reg, unsigned char b) +{ + + emu_wr((struct sc_info *)sc, 0x18 + reg, b, 1); +} + +static int +emu_muninit(struct mpu401 *arg, void *cookie) +{ + struct sc_info *sc = cookie; + + snd_mtxlock(sc->lock); + sc->mpu_intr = 0; + snd_mtxunlock(sc->lock); + + return 0; +} + +static kobj_method_t emu_mpu_methods[] = { + KOBJMETHOD(mpufoi_read, emu_mread), + KOBJMETHOD(mpufoi_write, emu_mwrite), + KOBJMETHOD(mpufoi_uninit, emu_muninit), + KOBJMETHOD_END +}; + +static DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0); + +static void +emu_intr2(void *p) +{ + struct sc_info *sc = (struct sc_info *)p; + + if (sc->mpu_intr) + (sc->mpu_intr)(sc->mpu); +} + +static void +emu_midiattach(struct sc_info *sc) +{ + int i; + + i = emu_rd(sc, EMU_INTE, 4); + i |= EMU_INTE_MIDIRXENABLE; + emu_wr(sc, EMU_INTE, i, 4); + + sc->mpu = mpu401_init(&emu_mpu_class, sc, emu_intr2, &sc->mpu_intr); +} /* -------------------------------------------------------------------- */ /* The interrupt handler */ @@ -1192,6 +1255,11 @@ emu_intr(void *data) #endif } + if (stat & EMU_IPR_MIDIRECVBUFE) + if (sc->mpu_intr) { + (sc->mpu_intr)(sc->mpu); + ack |= EMU_IPR_MIDIRECVBUFE | EMU_IPR_MIDITRANSBUFE; + } if (stat & ~ack) device_printf(sc->dev, "dodgy irq: %x (harmless)\n", stat & ~ack); @@ -1966,6 +2034,8 @@ emu_uninit(struct sc_info *sc) emu_free(sc, sc->mem.ptb_pages, sc->mem.ptb_map); emu_free(sc, sc->mem.silent_page, sc->mem.silent_map); + if(sc->mpu) + mpu401_uninit(sc->mpu); return 0; } @@ -2051,6 +2121,8 @@ emu_pci_attach(device_t dev) gotmic = (ac97_getcaps(codec) & AC97_CAP_MICCHANNEL) ? 1 : 0; if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; + emu_midiattach(sc); + i = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, RF_ACTIVE | RF_SHAREABLE); diff --git a/sys/dev/sound/pci/emu10kx-midi.c b/sys/dev/sound/pci/emu10kx-midi.c new file mode 100644 index 0000000000..51232146a8 --- /dev/null +++ b/sys/dev/sound/pci/emu10kx-midi.c @@ -0,0 +1,253 @@ +/*- + * Copyright (c) 1999 Seigo Tanimura + * Copyright (c) 2003 Mathew Kanner + * Copyright (c) 2003-2006 Yuriy Tsibizov + * 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: head/sys/dev/sound/pci/emu10kx-midi.c 246128 2013-01-30 18:01:20Z sbz $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + +#include +#include + +#include +#include +#include "mpufoi_if.h" + +#include +#include + +struct emu_midi_softc { + struct mtx mtx; + device_t dev; + struct mpu401 *mpu; + mpu401_intr_t *mpu_intr; + struct emu_sc_info *card; + int port; /* I/O port or I/O ptr reg */ + int is_emu10k1; + int fflags; /* File flags */ + int ihandle; /* interrupt manager handle */ +}; + +static uint32_t emu_midi_card_intr(void *p, uint32_t arg); +static devclass_t emu_midi_devclass; + +static unsigned char +emu_mread(struct mpu401 *arg __unused, void *cookie, int reg) +{ + struct emu_midi_softc *sc = cookie; + unsigned int d; + + d = 0; + if (sc->is_emu10k1) + d = emu_rd(sc->card, 0x18 + reg, 1); + else + d = emu_rdptr(sc->card, 0, sc->port + reg); + + return (d); +} + +static void +emu_mwrite(struct mpu401 *arg __unused, void *cookie, int reg, unsigned char b) +{ + struct emu_midi_softc *sc = cookie; + + if (sc->is_emu10k1) + emu_wr(sc->card, 0x18 + reg, b, 1); + else + emu_wrptr(sc->card, 0, sc->port + reg, b); +} + +static int +emu_muninit(struct mpu401 *arg __unused, void *cookie) +{ + struct emu_midi_softc *sc = cookie; + + mtx_lock(&sc->mtx); + sc->mpu_intr = NULL; + mtx_unlock(&sc->mtx); + + return (0); +} + +static kobj_method_t emu_mpu_methods[] = { + KOBJMETHOD(mpufoi_read, emu_mread), + KOBJMETHOD(mpufoi_write, emu_mwrite), + KOBJMETHOD(mpufoi_uninit, emu_muninit), + KOBJMETHOD_END +}; +static DEFINE_CLASS(emu_mpu, emu_mpu_methods, 0); + +static uint32_t +emu_midi_card_intr(void *p, uint32_t intr_status) +{ + struct emu_midi_softc *sc = (struct emu_midi_softc *)p; + if (sc->mpu_intr) + (sc->mpu_intr) (sc->mpu); + if (sc->mpu_intr == NULL) { + /* We should read MIDI event to unlock card after + * interrupt. XXX - check, why this happens. */ + if (bootverbose) + device_printf(sc->dev, "midi interrupt %08x without interrupt handler, force mread!\n", intr_status); + (void)emu_mread((void *)(NULL), sc, 0); + } + return (intr_status); /* Acknowledge everything */ +} + +static void +emu_midi_intr(void *p) +{ + (void)emu_midi_card_intr(p, 0); +} + +static int +emu_midi_probe(device_t dev) +{ + struct emu_midi_softc *scp; + uintptr_t func, r, is_emu10k1; + + r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func); + if (func != SCF_MIDI) + return (ENXIO); + + scp = device_get_softc(dev); + bzero(scp, sizeof(*scp)); + r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_ISEMU10K1, &is_emu10k1); + scp->is_emu10k1 = is_emu10k1 ? 1 : 0; + + device_set_desc(dev, "EMU10Kx MIDI Interface"); + return (0); +} + +static int +emu_midi_attach(device_t dev) +{ + struct emu_midi_softc * scp; + struct sndcard_func *func; + struct emu_midiinfo *midiinfo; + uint32_t inte_val, ipr_val; + + scp = device_get_softc(dev); + func = device_get_ivars(dev); + + scp->dev = dev; + midiinfo = (struct emu_midiinfo *)func->varinfo; + scp->port = midiinfo->port; + scp->card = midiinfo->card; + + mtx_init(&scp->mtx, device_get_nameunit(dev), "midi softc", MTX_DEF); + + if (scp->is_emu10k1) { + /* SB Live! - only one MIDI device here */ + inte_val = 0; + /* inte_val |= EMU_INTE_MIDITXENABLE;*/ + inte_val |= EMU_INTE_MIDIRXENABLE; + ipr_val = EMU_IPR_MIDITRANSBUFE; + ipr_val |= EMU_IPR_MIDIRECVBUFE; + } else { + if (scp->port == EMU_A_MUDATA1) { + /* EXTERNAL MIDI (AudigyDrive) */ + inte_val = 0; + /* inte_val |= A_EMU_INTE_MIDITXENABLE1;*/ + inte_val |= EMU_INTE_MIDIRXENABLE; + ipr_val = EMU_IPR_MIDITRANSBUFE; + ipr_val |= EMU_IPR_MIDIRECVBUFE; + } else { + /* MIDI hw config port 2 */ + inte_val = 0; + /* inte_val |= A_EMU_INTE_MIDITXENABLE2;*/ + inte_val |= EMU_INTE_A_MIDIRXENABLE2; + ipr_val = EMU_IPR_A_MIDITRANSBUFE2; + ipr_val |= EMU_IPR_A_MIDIRECBUFE2; + } + } + + scp->ihandle = emu_intr_register(scp->card, inte_val, ipr_val, &emu_midi_card_intr, scp); + /* Init the interface. */ + scp->mpu = mpu401_init(&emu_mpu_class, scp, emu_midi_intr, &scp->mpu_intr); + if (scp->mpu == NULL) { + emu_intr_unregister(scp->card, scp->ihandle); + mtx_destroy(&scp->mtx); + return (ENOMEM); + } + /* + * XXX I don't know how to check for Live!Drive / AudigyDrive + * presence. Let's hope that IR enabling code will not harm if + * it is not present. + */ + if (scp->is_emu10k1) + emu_enable_ir(scp->card); + else { + if (scp->port == EMU_A_MUDATA1) + emu_enable_ir(scp->card); + } + + return (0); +} + + +static int +emu_midi_detach(device_t dev) +{ + struct emu_midi_softc *scp; + + scp = device_get_softc(dev); + mpu401_uninit(scp->mpu); + emu_intr_unregister(scp->card, scp->ihandle); + mtx_destroy(&scp->mtx); + return (0); +} + +static device_method_t emu_midi_methods[] = { + DEVMETHOD(device_probe, emu_midi_probe), + DEVMETHOD(device_attach, emu_midi_attach), + DEVMETHOD(device_detach, emu_midi_detach), + + DEVMETHOD_END +}; + +static driver_t emu_midi_driver = { + "midi", + emu_midi_methods, + sizeof(struct emu_midi_softc), +}; +DRIVER_MODULE(snd_emu10kx_midi, emu10kx, emu_midi_driver, emu_midi_devclass, 0, 0); +MODULE_DEPEND(snd_emu10kx_midi, snd_emu10kx, SND_EMU10KX_MINVER, SND_EMU10KX_PREFVER, SND_EMU10KX_MAXVER); +MODULE_DEPEND(snd_emu10kx_midi, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_emu10kx_midi, SND_EMU10KX_PREFVER); diff --git a/sys/dev/sound/sound/Makefile b/sys/dev/sound/sound/Makefile index 527cbab997..fbb8b3aa53 100644 --- a/sys/dev/sound/sound/Makefile +++ b/sys/dev/sound/sound/Makefile @@ -1,6 +1,7 @@ # $FreeBSD: head/sys/modules/sound/sound/Makefile 212523 2010-09-13 02:05:42Z imp $ .PATH: ${.CURDIR}/.. +.PATH: ${.CURDIR}/../midi .PATH: ${.CURDIR}/../pcm KMOD= sound @@ -11,8 +12,11 @@ SRCS+= feeder.c feeder_rate.c feeder_volume.c SRCS+= feeder_chain.c feeder_eq.c feeder_format.c SRCS+= feeder_matrix.c feeder_mixer.c SRCS+= feeder_eq_gen.h feeder_rate_gen.h snd_fxdiv_gen.h +SRCS+= mpu_if.h mpufoi_if.h synth_if.h +SRCS+= mpu_if.c mpufoi_if.c synth_if.c SRCS+= ac97.c ac97_patch.c buffer.c channel.c clone.c dsp.c SRCS+= mixer.c sndstat.c sound.c unit.c vchan.c +SRCS+= midi.c mpu401.c sequencer.c .if !exists(@) feeder_eq_gen.h: @ -- 2.11.4.GIT