summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--etc/MAKEDEV4
-rw-r--r--sys/amd64/amd64/bios.c3
-rw-r--r--sys/conf/NOTES26
-rw-r--r--sys/conf/files10
-rw-r--r--sys/dev/sound/isa/emu8000.c2037
-rw-r--r--sys/dev/sound/isa/gusc.c104
-rw-r--r--sys/dev/sound/isa/gusmidi.c523
-rw-r--r--sys/dev/sound/isa/mpu.c835
-rw-r--r--sys/dev/sound/isa/mss.c12
-rw-r--r--sys/dev/sound/isa/opl.c1904
-rw-r--r--sys/dev/sound/isa/sbc.c2
-rw-r--r--sys/dev/sound/isa/uartsio.c524
-rw-r--r--sys/dev/sound/midi/midi.c702
-rw-r--r--sys/dev/sound/midi/midi.h294
-rw-r--r--sys/dev/sound/midi/midibuf.c424
-rw-r--r--sys/dev/sound/midi/midibuf.h64
-rw-r--r--sys/dev/sound/midi/midisynth.c632
-rw-r--r--sys/dev/sound/midi/midisynth.h97
-rw-r--r--sys/dev/sound/midi/miditypes.h34
-rw-r--r--sys/dev/sound/midi/sequencer.c2040
-rw-r--r--sys/dev/sound/midi/sequencer.h234
-rw-r--r--sys/dev/sound/pci/csa.c10
-rw-r--r--sys/dev/sound/pci/csamidi.c554
-rw-r--r--sys/dev/sound/pcm/mixer.c1
-rw-r--r--sys/i386/conf/NOTES26
-rw-r--r--sys/i386/i386/bios.c3
-rw-r--r--sys/isa/pnp.c47
-rw-r--r--sys/isa/pnpparse.c5
-rw-r--r--sys/isa/pnpvar.h4
-rw-r--r--sys/sys/soundcard.h4
30 files changed, 11069 insertions, 90 deletions
diff --git a/etc/MAKEDEV b/etc/MAKEDEV
index 3dbeb3c..69756f7 100644
--- a/etc/MAKEDEV
+++ b/etc/MAKEDEV
@@ -1344,7 +1344,9 @@ snd*)
# minor number 7 is unused
mknod music$unit c $chr `expr $unit '*' 16 + 8`
mknod pss$unit c $chr `expr $unit '*' 16 + 9`
- # minor numbers 10-15 are unused
+ # minor number 10 is unused
+ mknod midistat c $chr 11
+ # minor numbers 12-15 are unused
umask 77
;;
diff --git a/sys/amd64/amd64/bios.c b/sys/amd64/amd64/bios.c
index 4a5a942..06eafe0 100644
--- a/sys/amd64/amd64/bios.c
+++ b/sys/amd64/amd64/bios.c
@@ -588,7 +588,8 @@ pnpbios_identify(driver_t *driver, device_t parent)
isa_set_logicalid(dev, pd->devid);
ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0);
pnp_parse_resources(dev, &pd->devdata[0],
- pd->size - sizeof(struct pnp_sysdev));
+ pd->size - sizeof(struct pnp_sysdev),
+ isa_get_vendorid(dev), isa_get_logicalid(dev), 0);
if (!device_get_desc(dev))
device_set_desc_copy(dev, pnp_eisaformat(pd->devid));
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 1c46e4c..2a8214e 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -1649,8 +1649,32 @@ hint.pcm.0.flags="0x0"
# For PnP/PCI sound cards, no hints are required.
+#
+# midi: MIDI interfaces and synthesizers
+#
+
+device midi
+
+# For non-pnp sound cards with no bridge drivers:
+hint.midi.0.at="isa"
+hint.midi.0.irq="5"
+hint.midi.0.flags="0x0"
+
+# For serial ports (this example configures port 2):
+# TODO: implement generic tty-midi interface so that we can use
+# other uarts.
+hint.midi.0.at="isa"
+hint.midi.0.port="0x2F8"
+hint.midi.0.irq="3"
+
+#
+# seq: MIDI sequencer
+#
+
+device seq
+
# The bridge drivers for sound cards. These can be seperately configured
-# for providing services to the likes of new-midi (not in the tree yet).
+# for providing services to the likes of new-midi.
# When used with 'device pcm' they also provide pcm sound services.
#
# sbc: Creative SoundBlaster ISA PnP/non-PnP
diff --git a/sys/conf/files b/sys/conf/files
index 6ea2a2e..84130e1 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -248,17 +248,27 @@ dev/sn/if_sn.c optional sn
dev/sn/if_sn_isa.c optional sn isa
dev/sn/if_sn_pccard.c optional sn card
dev/sound/isa/ad1816.c optional pcm isa
+dev/sound/isa/emu8000.c optional midi isa
dev/sound/isa/es1888.c optional pcm isa
dev/sound/isa/ess.c optional pcm isa
dev/sound/isa/gusc.c optional gusc isa
dev/sound/isa/gusc.c optional pcm isa
+dev/sound/isa/gusmidi.c optional midi isa
+dev/sound/isa/mpu.c optional midi isa
dev/sound/isa/mss.c optional pcm isa
+dev/sound/isa/opl.c optional midi isa
dev/sound/isa/sb.c optional pcm isa
dev/sound/isa/sbc.c optional pcm isa
dev/sound/isa/sbc.c optional sbc isa
+dev/sound/isa/uartsio.c optional midi isa
+dev/sound/midi/midi.c optional midi
+dev/sound/midi/midibuf.c optional midi
+dev/sound/midi/midisynth.c optional midi
+dev/sound/midi/sequencer.c optional seq midi
#dev/sound/pci/aureal.c optional pcm pci
dev/sound/pci/csa.c optional csa pci
dev/sound/pci/csa.c optional pcm pci
+dev/sound/pci/csamidi.c optional midi csa
dev/sound/pci/csapcm.c optional pcm pci
dev/sound/pci/ds1.c optional pcm pci
dev/sound/pci/emu10k1.c optional pcm pci
diff --git a/sys/dev/sound/isa/emu8000.c b/sys/dev/sound/isa/emu8000.c
new file mode 100644
index 0000000..d69d67c
--- /dev/null
+++ b/sys/dev/sound/isa/emu8000.c
@@ -0,0 +1,2037 @@
+/*
+ * Low level EMU8000 chip driver for FreeBSD. This handles io against
+ * /dev/midi, the midi {in, out}put event queues and the event/message
+ * operation to the EMU8000 chip.
+ *
+ * (C) 1999 Seigo Tanimura
+ *
+ * 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$
+ *
+ */
+
+
+#include "opt_devfs.h"
+
+#include <dev/sound/midi/midi.h>
+
+#include <isa/isavar.h>
+
+static devclass_t midi_devclass;
+
+#ifndef DDB
+#undef DDB
+#define DDB(x)
+#endif /* DDB */
+
+/* These are the specs of EMU8000. */
+#define EMU8K_MAXVOICE 32
+#define EMU8K_MAXINFO 256
+
+#define EMU8K_IDX_DATA0 0
+#define EMU8K_IDX_DATA1 1
+#define EMU8K_IDX_DATA2 1
+#define EMU8K_IDX_DATA3 2
+#define EMU8K_IDX_PTR 2
+
+#define EMU8K_PORT_DATA0 0
+#define EMU8K_PORT_DATA1 0
+#define EMU8K_PORT_DATA2 2
+#define EMU8K_PORT_DATA3 0
+#define EMU8K_PORT_PTR 2
+
+#define EMU8K_DRAM_RAM 0x200000
+#define EMU8K_DRAM_MAX 0xffffe0
+
+/* And some convinient macros. */
+#define EMU8K_DMA_LEFT 0x00
+#define EMU8K_DMA_RIGHT 0x01
+#define EMU8K_DMA_LR 0x01
+#define EMU8K_DMA_READ 0x00
+#define EMU8K_DMA_WRITE 0x02
+#define EMU8K_DMA_RW 0x02
+#define EMU8K_DMA_MASK 0x03
+
+/* The followings are the init array for EMU8000, originally in ADIP. */
+
+/* Set 1 */
+static u_short init1_1[32] =
+{
+ 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330,
+ 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730,
+ 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30,
+ 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30,
+};
+
+static u_short init1_2[32] =
+{
+ 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330,
+ 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730,
+ 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30,
+ 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30,
+};
+
+static u_short init1_3[32] =
+{
+ 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330,
+ 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730,
+ 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30,
+ 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30,
+};
+
+static u_short init1_4[32] =
+{
+ 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330,
+ 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730,
+ 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30,
+ 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30,
+};
+
+/* Set 2 */
+
+static u_short init2_1[32] =
+{
+ 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330,
+ 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730,
+ 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30,
+ 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30,
+};
+
+static u_short init2_2[32] =
+{
+ 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330,
+ 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730,
+ 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30,
+ 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30,
+};
+
+static u_short init2_3[32] =
+{
+ 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330,
+ 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
+ 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
+ 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
+};
+
+static u_short init2_4[32] =
+{
+ 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
+ 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
+ 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
+ 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
+};
+
+/* Set 3 */
+
+static u_short init3_1[32] =
+{
+ 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
+ 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254,
+ 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234,
+ 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224,
+};
+
+static u_short init3_2[32] =
+{
+ 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254,
+ 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264,
+ 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294,
+ 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3,
+};
+
+static u_short init3_3[32] =
+{
+ 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287,
+ 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7,
+ 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386,
+ 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55,
+};
+
+static u_short init3_4[32] =
+{
+ 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308,
+ 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F,
+ 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319,
+ 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
+};
+
+/* Set 4 */
+
+static u_short init4_1[32] =
+{
+ 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
+ 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254,
+ 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234,
+ 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224,
+};
+
+static u_short init4_2[32] =
+{
+ 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254,
+ 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264,
+ 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294,
+ 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3,
+};
+
+static u_short init4_3[32] =
+{
+ 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287,
+ 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7,
+ 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386,
+ 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55,
+};
+
+static u_short init4_4[32] =
+{
+ 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308,
+ 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F,
+ 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319,
+ 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570,
+};
+
+/* The followings are the register, the channel and the port for the EMU8000 registers. */
+struct _emu_register {
+ int reg; /* Register */
+ int index; /* Index */
+ int port; /* Port */
+ int chn; /* Channel */
+ int size; /* Size, 0 == word, 1 == double word */
+};
+
+#define EMU8K_CHN_ANY (-1)
+
+static struct _emu_register emu_regs[] =
+{
+ /* Reg, Index, Port, Channel, Size */
+ { 0, EMU8K_IDX_DATA0, EMU8K_PORT_DATA0, EMU8K_CHN_ANY, 1}, /* CPF */
+ { 1, EMU8K_IDX_DATA0, EMU8K_PORT_DATA0, EMU8K_CHN_ANY, 1}, /* PTRX */
+ { 2, EMU8K_IDX_DATA0, EMU8K_PORT_DATA0, EMU8K_CHN_ANY, 1}, /* CVCF */
+ { 3, EMU8K_IDX_DATA0, EMU8K_PORT_DATA0, EMU8K_CHN_ANY, 1}, /* VTFT */
+ { 6, EMU8K_IDX_DATA0, EMU8K_PORT_DATA0, EMU8K_CHN_ANY, 1}, /* PSST */
+ { 7, EMU8K_IDX_DATA0, EMU8K_PORT_DATA0, EMU8K_CHN_ANY, 1}, /* CSL */
+ { 0, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 1}, /* CCCA */
+ { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 9, 1}, /* HWCF4 */
+ { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 10, 1}, /* HWCF5 */
+ { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 13, 1}, /* HWCF6 */
+ { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 20, 1}, /* SMALR */
+ { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 21, 1}, /* SMARR */
+ { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 22, 1}, /* SMALW */
+ { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 23, 1}, /* SMARW */
+ { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 26, 0}, /* SMLD */
+ { 1, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, 26, 0}, /* SMRD */
+ { 1, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, 27, 0}, /* WC */
+ { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 29, 0}, /* HWCF1 */
+ { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 30, 0}, /* HWCF2 */
+ { 1, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, 31, 0}, /* HWCF3 */
+ { 2, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 0}, /* INIT1 */
+ { 2, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, EMU8K_CHN_ANY, 0}, /* INIT2 */
+ { 3, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 0}, /* INIT3 */
+ { 3, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, EMU8K_CHN_ANY, 0}, /* INIT4 */
+ { 4, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 0}, /* ENVVOL */
+ { 5, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 0}, /* DCYSUSV */
+ { 6, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 0}, /* ENVVAL */
+ { 7, EMU8K_IDX_DATA1, EMU8K_PORT_DATA1, EMU8K_CHN_ANY, 0}, /* DCYSUS */
+ { 4, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, EMU8K_CHN_ANY, 0}, /* ATKHLDV */
+ { 5, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, EMU8K_CHN_ANY, 0}, /* LFO1VAL */
+ { 6, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, EMU8K_CHN_ANY, 0}, /* ATKHLD */
+ { 7, EMU8K_IDX_DATA2, EMU8K_PORT_DATA2, EMU8K_CHN_ANY, 0}, /* LFO2VAL */
+ { 0, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, EMU8K_CHN_ANY, 0}, /* IP */
+ { 1, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, EMU8K_CHN_ANY, 0}, /* IFATN */
+ { 2, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, EMU8K_CHN_ANY, 0}, /* PEFE */
+ { 3, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, EMU8K_CHN_ANY, 0}, /* FMMOD */
+ { 4, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, EMU8K_CHN_ANY, 0}, /* TREMFRQ */
+ { 5, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, EMU8K_CHN_ANY, 0}, /* FM2FRQ2 */
+ { 7, EMU8K_IDX_DATA3, EMU8K_PORT_DATA3, 0, 0}, /* PROBE */
+};
+
+/* These are the EMU8000 register names. */
+enum {
+ EMU8K_CPF = 0,
+ EMU8K_PTRX,
+ EMU8K_CVCF,
+ EMU8K_VTFT,
+ EMU8K_PSST,
+ EMU8K_CSL,
+ EMU8K_CCCA,
+ EMU8K_HWCF4,
+ EMU8K_HWCF5,
+ EMU8K_HWCF6,
+ EMU8K_SMALR,
+ EMU8K_SMARR,
+ EMU8K_SMALW,
+ EMU8K_SMARW,
+ EMU8K_SMLD,
+ EMU8K_SMRD,
+ EMU8K_WC,
+ EMU8K_HWCF1,
+ EMU8K_HWCF2,
+ EMU8K_HWCF3,
+ EMU8K_INIT1,
+ EMU8K_INIT2,
+ EMU8K_INIT3,
+ EMU8K_INIT4,
+ EMU8K_ENVVOL,
+ EMU8K_DCYSUSV,
+ EMU8K_ENVVAL,
+ EMU8K_DCYSUS,
+ EMU8K_ATKHLDV,
+ EMU8K_LFO1VAL,
+ EMU8K_ATKHLD,
+ EMU8K_LFO2VAL,
+ EMU8K_IP,
+ EMU8K_IFATN,
+ EMU8K_PEFE,
+ EMU8K_FMMOD,
+ EMU8K_TREMFRQ,
+ EMU8K_FM2FRQ2,
+ EMU8K_PROBE,
+ EMU8K_REGLAST, /* keep this! */
+};
+#define EMU8K_REGNUM (EMU8K_REGLAST)
+
+/* These are the synthesizer and the midi device information. */
+static struct synth_info emu_synthinfo = {
+ "EMU8000 Wavetable Synth",
+ 0,
+ SYNTH_TYPE_SAMPLE,
+ SAMPLE_TYPE_AWE32,
+ 0,
+ EMU8K_MAXVOICE,
+ 0,
+ EMU8K_MAXINFO,
+ 0,
+};
+
+static struct midi_info emu_midiinfo = {
+ "EMU8000 Wavetable Synth",
+ 0,
+ 0,
+ 0,
+};
+
+#if notyet
+/*
+ * These functions goes into emusynthdev_op_desc.
+ */
+static mdsy_killnote_t emu_killnote;
+static mdsy_setinstr_t emu_setinstr;
+static mdsy_startnote_t emu_startnote;
+static mdsy_reset_t emu_reset;
+static mdsy_hwcontrol_t emu_hwcontrol;
+static mdsy_loadpatch_t emu_loadpatch;
+static mdsy_panning_t emu_panning;
+static mdsy_aftertouch_t emu_aftertouch;
+static mdsy_controller_t emu_controller;
+static mdsy_patchmgr_t emu_patchmgr;
+static mdsy_bender_t emu_bender;
+static mdsy_allocvoice_t emu_allocvoice;
+static mdsy_setupvoice_t emu_setupvoice;
+static mdsy_sendsysex_t emu_sendsysex;
+static mdsy_prefixcmd_t emu_prefixcmd;
+static mdsy_volumemethod_t emu_volumemethod;
+
+/*
+ * This is the synthdev_info for an EMU8000 chip.
+ */
+static synthdev_info emusynth_op_desc = {
+ emu_killnote,
+ emu_setinstr,
+ emu_startnote,
+ emu_reset,
+ emu_hwcontrol,
+ emu_loadpatch,
+ emu_panning,
+ emu_aftertouch,
+ emu_controller,
+ emu_patchmgr,
+ emu_bender,
+ emu_allocvoice,
+ emu_setupvoice,
+ emu_sendsysex,
+ emu_prefixcmd,
+ emu_volumemethod,
+};
+#endif /* notyet */
+
+/*
+ * These functions goes into emu_op_desc to get called
+ * from sound.c.
+ */
+
+static int emu_probe(device_t dev);
+static int emu_attach(device_t dev);
+
+static d_open_t emu_open;
+static d_close_t emu_close;
+static d_read_t emu_read;
+static d_write_t emu_write;
+static d_ioctl_t emu_ioctl;
+static midi_callback_t emu_callback;
+
+/* These go to mididev_info. */
+static mdsy_readraw_t emu_readraw;
+static mdsy_writeraw_t emu_writeraw;
+
+/* Here is the parameter structure per a device. */
+struct emu_softc {
+ device_t dev; /* device information */
+ mididev_info *devinfo; /* midi device information */
+
+ struct resource *io[3]; /* Base of io port */
+ int io_rid[3]; /* Io resource ID */
+
+ u_int dramsize; /* DRAM size */
+ struct synth_info synthinfo; /* Synthesizer information */
+
+ int fflags; /* File flags */
+};
+
+typedef struct emu_softc *sc_p;
+
+/* These functions are local. */
+static u_int emu_dramsize(sc_p scp);
+static void emu_allocdmachn(sc_p scp, int chn, int mode);
+static void emu_dmaaddress(sc_p scp, int mode, u_int addr);
+static void emu_waitstream(sc_p scp, int mode);
+static void emu_readblkstream(sc_p scp, int mode, u_short *data, size_t len);
+static void emu_writeblkstream(sc_p scp, int mode, u_short *data, size_t len);
+static void emu_readstream(sc_p scp, int mode, u_short *data);
+static void emu_writestream(sc_p scp, int mode, u_short data);
+static void emu_releasedmachn(sc_p scp, int chn, int mode);
+static void emu_delay(sc_p scp, short n);
+static void emu_readcpf(sc_p scp, int chn, u_int *cp, u_int *f);
+static void emu_writecpf(sc_p scp, int chn, u_int cp, u_int f);
+static void emu_readptrx(sc_p scp, int chn, u_int *pt, u_int *rs, u_int *auxd);
+static void emu_writeptrx(sc_p scp, int chn, u_int pt, u_int rs, u_int auxd);
+static void emu_readcvcf(sc_p scp, int chn, u_int *cv, u_int *cf);
+static void emu_writecvcf(sc_p scp, int chn, u_int cv, u_int cf);
+static void emu_readvtft(sc_p scp, int chn, u_int *vt, u_int *ft);
+static void emu_writevtft(sc_p scp, int chn, u_int vt, u_int ft);
+static void emu_readpsst(sc_p scp, int chn, u_int *pan, u_int *st);
+static void emu_writepsst(sc_p scp, int chn, u_int pan, u_int st);
+static void emu_readcsl(sc_p scp, int chn, u_int *cs, u_int *lp);
+static void emu_writecsl(sc_p scp, int chn, u_int cs, u_int lp);
+static void emu_readccca(sc_p scp, int chn, u_int *q, u_int *dma, u_int *wr, u_int *right, u_int *ca);
+static void emu_writeccca(sc_p scp, int chn, u_int q, u_int dma, u_int wr, u_int right, u_int ca);
+static void emu_readhwcf4(sc_p scp, u_int *val);
+static void emu_writehwcf4(sc_p scp, u_int val);
+static void emu_readhwcf5(sc_p scp, u_int *val);
+static void emu_writehwcf5(sc_p scp, u_int val);
+static void emu_readhwcf6(sc_p scp, u_int *val);
+static void emu_writehwcf6(sc_p scp, u_int val);
+static void emu_readsmalr(sc_p scp, u_int *mt, u_int *smalr);
+static void emu_writesmalr(sc_p scp, u_int mt, u_int smalr);
+static void emu_readsmarr(sc_p scp, u_int *mt, u_int *smarr);
+static void emu_writesmarr(sc_p scp, u_int mt, u_int smarr);
+static void emu_readsmalw(sc_p scp, u_int *full, u_int *smalw);
+static void emu_writesmalw(sc_p scp, u_int full, u_int smalw);
+static void emu_readsmarw(sc_p scp, u_int *full, u_int *smarw);
+static void emu_writesmarw(sc_p scp, u_int full, u_int smarw);
+static void emu_readsmld(sc_p scp, u_short *smld);
+static void emu_writesmld(sc_p scp, u_short smld);
+static void emu_readsmrd(sc_p scp, u_short *smrd);
+static void emu_writesmrd(sc_p scp, u_short smrd);
+static void emu_readwc(sc_p scp, u_int *wc);
+static void emu_writewc(sc_p scp, u_int wc);
+static void emu_readhwcf1(sc_p scp, u_int *val);
+static void emu_writehwcf1(sc_p scp, u_int val);
+static void emu_readhwcf2(sc_p scp, u_int *val);
+static void emu_writehwcf2(sc_p scp, u_int val);
+static void emu_readhwcf3(sc_p scp, u_int *val);
+static void emu_writehwcf3(sc_p scp, u_int val);
+static void emu_readinit1(sc_p scp, int chn, u_int *val);
+static void emu_writeinit1(sc_p scp, int chn, u_int val);
+static void emu_readinit2(sc_p scp, int chn, u_int *val);
+static void emu_writeinit2(sc_p scp, int chn, u_int val);
+static void emu_readinit3(sc_p scp, int chn, u_int *val);
+static void emu_writeinit3(sc_p scp, int chn, u_int val);
+static void emu_readinit4(sc_p scp, int chn, u_int *val);
+static void emu_writeinit4(sc_p scp, int chn, u_int val);
+static void emu_readenvvol(sc_p scp, int chn, u_int *envvol);
+static void emu_writeenvvol(sc_p scp, int chn, u_int envvol);
+static void emu_readdcysusv(sc_p scp, int chn, u_int *ph1v, u_int *susv, u_int *off, u_int *dcyv);
+static void emu_writedcysusv(sc_p scp, int chn, u_int ph1v, u_int susv, u_int off, u_int dcyv);
+static void emu_readenvval(sc_p scp, int chn, u_int *envval);
+static void emu_writeenvval(sc_p scp, int chn, u_int envval);
+static void emu_readdcysus(sc_p scp, int chn, u_int *ph1, u_int *sus, u_int *dcy);
+static void emu_writedcysus(sc_p scp, int chn, u_int ph1, u_int sus, u_int dcy);
+static void emu_readatkhldv(sc_p scp, int chn, u_int *atkhldv);
+static void emu_writeatkhldv(sc_p scp, int chn, u_int atkhldv);
+static void emu_readlfo1val(sc_p scp, int chn, u_int *lfo1val);
+static void emu_writelfo1val(sc_p scp, int chn, u_int lfo1val);
+static void emu_readatkhld(sc_p scp, int chn, u_int *atkhld);
+static void emu_writeatkhld(sc_p scp, int chn, u_int atkhld);
+static void emu_readlfo2val(sc_p scp, int chn, u_int *lfo2val);
+static void emu_writelfo2val(sc_p scp, int chn, u_int lfo2val);
+static void emu_readip(sc_p scp, int chn, u_int *ip);
+static void emu_writeip(sc_p scp, int chn, u_int ip);
+static void emu_readifatn(sc_p scp, int chn, u_int *ifc, u_int *atn);
+static void emu_writeifatn(sc_p scp, int chn, u_int ifc, u_int atn);
+static void emu_readpefe(sc_p scp, int chn, u_int *pe, u_int *fe);
+static void emu_writepefe(sc_p scp, int chn, u_int pe, u_int fe);
+static void emu_readfmmod(sc_p scp, int chn, u_int *fm, u_int *mod);
+static void emu_writefmmod(sc_p scp, int chn, u_int fm, u_int mod);
+static void emu_readtremfrq(sc_p scp, int chn, u_int *trem, u_int *frq);
+static void emu_writetremfrq(sc_p scp, int chn, u_int trem, u_int frq);
+static void emu_readfm2frq2(sc_p scp, int chn, u_int *fm2, u_int *frq2);
+static void emu_writefm2frq2(sc_p scp, int chn, u_int fm2, u_int frq2);
+static void emu_readprobe(sc_p scp, u_int *val);
+static void emu_writeprobe(sc_p scp, u_int val);
+static void emu_command(sc_p scp, int reg, int chn, u_long val);
+static u_long emu_status(sc_p scp, int reg, int chn);
+static int emu_allocres(sc_p scp, device_t dev);
+static void emu_releaseres(sc_p scp, device_t dev);
+
+/* PnP IDs */
+static struct isa_pnp_id emu_ids[] = {
+ {0x21008c0e, "CTL0021 WaveTable Synthesizer"}, /* CTL0021 */
+ {0x22008c0e, "CTL0022 WaveTable Synthesizer"}, /* CTL0022 */
+};
+
+/*
+ * This is the device descriptor for the midi device.
+ */
+mididev_info emu_op_desc = {
+ "EMU8000 Wavetable Synth",
+
+ SNDCARD_AWE32,
+
+ emu_open,
+ emu_close,
+ emu_read,
+ emu_write,
+ emu_ioctl,
+ NULL,
+
+ emu_callback,
+
+ MIDI_BUFFSIZE, /* Queue Length */
+
+ 0, /* XXX This is not an *audio* device! */
+};
+
+/*
+ * Here are the main functions to interact to the user process.
+ */
+
+static int
+emu_probe(device_t dev)
+{
+ sc_p scp;
+ int unit;
+ u_int probe, hwcf1, hwcf2;
+
+ /* Check isapnp ids */
+ if (isa_get_logicalid(dev) != 0)
+ return (ISA_PNP_PROBE(device_get_parent(dev), dev, emu_ids));
+ /* XXX non-pnp emu? */
+
+ unit = device_get_unit(dev);
+ scp = device_get_softc(dev);
+
+ device_set_desc(dev, "EMU8000 Wavetable Synth");
+ bzero(scp, sizeof(*scp));
+
+ DEB(printf("emu%d: probing.\n", unit));
+
+ if (emu_allocres(scp, dev)) {
+ emu_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ emu_readprobe(scp, &probe);
+ emu_readhwcf1(scp, &hwcf1);
+ emu_readhwcf2(scp, &hwcf2);
+ if ((probe & 0x000f) != 0x000c
+ || (hwcf1 & 0x007e) != 0x0058
+ || (hwcf2 & 0x0003) != 0x0003) {
+ emu_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ DEB(printf("emu%d: probed.\n", unit));
+
+ return (0);
+}
+
+extern synthdev_info midisynth_op_desc;
+
+static int
+emu_attach(device_t dev)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit, i;
+
+ unit = device_get_unit(dev);
+ scp = device_get_softc(dev);
+
+ DEB(printf("emu%d: attaching.\n", unit));
+
+ if (emu_allocres(scp, dev)) {
+ emu_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ /* EMU8000 needs some initialization processes. */
+
+ /* 1. Write HWCF{1,2}. */
+ emu_writehwcf1(scp, 0x0059);
+ emu_writehwcf2(scp, 0x0020);
+
+ /* Disable the audio. */
+ emu_writehwcf3(scp, 0);
+
+ /* 2. Initialize the channels. */
+
+ /* 2a. Write DCYSUSV. */
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writedcysusv(scp, i, 0, 0, 1, 0);
+
+ /* 2b. Clear the envelope and sound engine registers. */
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++) {
+ emu_writeenvvol(scp, i, 0);
+ emu_writeenvval(scp, i, 0);
+ emu_writedcysus(scp, i, 0, 0, 0);
+ emu_writeatkhldv(scp, i, 0);
+ emu_writelfo1val(scp, i, 0);
+ emu_writeatkhld(scp, i, 0);
+ emu_writelfo2val(scp, i, 0);
+ emu_writeip(scp, i, 0);
+ emu_writeifatn(scp, i, 0, 0);
+ emu_writepefe(scp, i, 0, 0);
+ emu_writefmmod(scp, i, 0, 0);
+ emu_writetremfrq(scp, i, 0, 0);
+ emu_writefm2frq2(scp, i, 0, 0);
+ emu_writeptrx(scp, i, 0, 0, 0);
+ emu_writevtft(scp, i, 0, 0);
+ emu_writepsst(scp, i, 0, 0);
+ emu_writecsl(scp, i, 0, 0);
+ emu_writeccca(scp, i, 0, 0, 0, 0, 0);
+ }
+
+ /* 2c. Clear the current registers. */
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++) {
+ emu_writecpf(scp, i, 0, 0);
+ emu_writecvcf(scp, i, 0, 0);
+ }
+
+ /* 3. Initialize the sound memory DMA registers. */
+ emu_writesmalr(scp, 0, 0);
+ emu_writesmarr(scp, 0, 0);
+ emu_writesmalw(scp, 0, 0);
+ emu_writesmarw(scp, 0, 0);
+
+ /* 4. Fill the array. */
+
+ /* 4a. Set 1. */
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit1(scp, i, init1_1[i]);
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit2(scp, i, init1_2[i]);
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit3(scp, i, init1_3[i]);
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit4(scp, i, init1_4[i]);
+
+ /* 4b. Have a rest. */
+ emu_delay(scp, 1024); /* 1024 samples. */
+
+ /* 4c. Set 2. */
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit1(scp, i, init2_1[i]);
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit2(scp, i, init2_2[i]);
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit3(scp, i, init2_3[i]);
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit4(scp, i, init2_4[i]);
+
+ /* 4d. Set 3. */
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit1(scp, i, init3_1[i]);
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit2(scp, i, init3_2[i]);
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit3(scp, i, init3_3[i]);
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit4(scp, i, init3_4[i]);
+
+ /* 4e. Write to HWCF{4,5,6}. */
+ emu_writehwcf4(scp, 0);
+ emu_writehwcf5(scp, 0x00000083);
+ emu_writehwcf6(scp, 0x00008000);
+
+ /* 4f. Set 4. */
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit1(scp, i, init4_1[i]);
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit2(scp, i, init4_2[i]);
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit3(scp, i, init4_3[i]);
+ for (i = 0 ; i < EMU8K_MAXVOICE ; i++)
+ emu_writeinit4(scp, i, init4_4[i]);
+
+ /* 5. Determine the size of DRAM. */
+ scp->dev = dev;
+ scp->dramsize = emu_dramsize(scp);
+ printf("emu%d: DRAM size = %dKB\n", unit, scp->dramsize / 1024);
+
+ /* We have inited the EMU8000. Now work on FM. */
+
+ /* Write parameters for the left channel. */
+ emu_writedcysusv(scp, 30, 0, 0, 1, 0);
+ emu_writepsst(scp, 30, 0x80, 0xffffe0); /* full left */
+ emu_writecsl(scp, 30, 0, 0xfffff8); /* chorus */
+ emu_writeptrx(scp, 30, 0, 0, 0); /* reverb */
+ emu_writecpf(scp, 30, 0, 0);
+ emu_writeccca(scp, 30, 0, 0, 0, 0, 0xffffe3);
+
+ /* Then the right channel. */
+ emu_writedcysusv(scp, 31, 0, 0, 1, 0);
+ emu_writepsst(scp, 31, 0x80, 0xfffff0); /* full right */
+ emu_writecsl(scp, 31, 0, 0xfffff8); /* chorus */
+ emu_writeptrx(scp, 31, 0, 0, 0xff); /* reverb */
+ emu_writecpf(scp, 31, 0, 0);
+ emu_writeccca(scp, 31, 0, 0, 0, 0, 0xfffff3);
+
+ /* Skew volume and cutoff. */
+ emu_writevtft(scp, 30, 0x8000, 0xffff);
+ emu_writevtft(scp, 31, 0x8000, 0xffff);
+
+ /* Ready to sound. */
+ emu_writehwcf3(scp, 0x0004);
+
+ /* Fill the softc for this unit. */
+ scp->devinfo = devinfo = &midi_info[unit];
+ bcopy(&emu_synthinfo, &scp->synthinfo, sizeof(emu_synthinfo));
+
+ /* Fill the midi info. */
+ bcopy(&emu_op_desc, devinfo, sizeof(emu_op_desc));
+ midiinit(devinfo, dev);
+ devinfo->flags = 0;
+ bcopy(&midisynth_op_desc, &devinfo->synth, sizeof(midisynth_op_desc));
+ devinfo->synth.readraw = emu_readraw;
+ devinfo->synth.writeraw = emu_writeraw;
+ snprintf(devinfo->midistat, sizeof(devinfo->midistat), "at 0x%x, 0x%x, 0x%x",
+ (u_int)rman_get_start(scp->io[0]), (u_int)rman_get_start(scp->io[1]), (u_int)rman_get_start(scp->io[2]));
+
+ /* Init the queue. */
+ devinfo->midi_dbuf_in.unit_size = devinfo->midi_dbuf_out.unit_size = 1;
+ midibuf_init(&devinfo->midi_dbuf_in);
+ midibuf_init(&devinfo->midi_dbuf_out);
+
+ /* Increase the number of the synthesizers. */
+ nsynth++;
+
+ DEB(printf("emu%d: attached.\n", unit));
+
+ return (0);
+}
+
+static int
+emupnp_attach(device_t dev)
+{
+ return (emu_attach(dev));
+}
+
+static int
+emu_open(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ return (0);
+}
+
+static int
+emu_close(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ return (0);
+}
+
+static int
+emu_read(dev_t i_dev, struct uio *buf, int flag)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit/*, s, len, ret*/;
+
+ unit = MIDIUNIT(i_dev);
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("emu_read: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("emu_read: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+ if ((devinfo->fflags & FREAD) == 0) {
+ DEB(printf("emu_read: unit %d is not for reading.\n", unit));
+ return (EIO);
+ }
+
+ /* Drain the data. */
+ midibuf_init(&devinfo->midi_dbuf_in);
+
+ return (0);
+}
+
+static int
+emu_write(dev_t i_dev, struct uio *buf, int flag)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit/*, s, len, ret*/;
+
+ unit = MIDIUNIT(i_dev);
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("emu_write: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("emu_write: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+ if ((devinfo->fflags & FWRITE) == 0) {
+ DEB(printf("emu_write: unit %d is not for writing.\n", unit));
+ return (EIO);
+ }
+
+ /* Drain the data. */
+ midibuf_init(&devinfo->midi_dbuf_out);
+ midibuf_init(&devinfo->midi_dbuf_passthru);
+
+ return (0);
+}
+
+static int
+emu_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit;
+ struct synth_info *synthinfo;
+ struct midi_info *midiinfo;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("emu%d: ioctlling, cmd 0x%x.\n", unit, (int)cmd));
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("emu_ioctl: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("emu_ioctl: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+
+ switch (cmd) {
+ case SNDCTL_SYNTH_INFO:
+ synthinfo = (struct synth_info *)arg;
+ if (synthinfo->device >= nmidi + nsynth || synthinfo->device != unit)
+ return (ENXIO);
+ bcopy(&scp->synthinfo, synthinfo, sizeof(scp->synthinfo));
+ synthinfo->device = unit;
+ return (0);
+ break;
+ case SNDCTL_MIDI_INFO:
+ midiinfo = (struct midi_info *)arg;
+ if (midiinfo->device >= nmidi + nsynth || midiinfo->device != unit)
+ return (ENXIO);
+ bcopy(&emu_midiinfo, midiinfo, sizeof(emu_midiinfo));
+ strcpy(midiinfo->name, scp->synthinfo.name);
+ midiinfo->device = unit;
+ return (0);
+ break;
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+ break;
+ default:
+ return (ENOSYS);
+ }
+ /* NOTREACHED */
+ return (EINVAL);
+}
+
+static int
+emu_callback(mididev_info *devinfo, int reason)
+{
+ return (0);
+}
+
+static int
+emu_readraw(mididev_info *md, u_char *buf, int len, int nonblock)
+{
+ sc_p scp;
+ int unit;
+
+ if (md == NULL)
+ return (ENXIO);
+ unit = md->unit;
+ scp = md->softc;
+ if ((md->fflags & FREAD) == 0) {
+ DEB(printf("emu_readraw: unit %d is not for reading.\n", unit));
+ return (EIO);
+ }
+
+ /* NOP. */
+ return (0);
+}
+
+static int
+emu_writeraw(mididev_info *md, u_char *buf, int len, int nonblock)
+{
+ sc_p scp;
+ int unit;
+
+ if (md == NULL)
+ return (ENXIO);
+ unit = md->unit;
+ scp = md->softc;
+ if ((md->fflags & FWRITE) == 0) {
+ DEB(printf("emu_writeraw: unit %d is not for writing.\n", unit));
+ return (EIO);
+ }
+
+ /* NOP. */
+ return (0);
+}
+
+/*
+ * The functions below here are the synthesizer interfaces.
+ */
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+/* Determine the size of DRAM. */
+static u_int
+emu_dramsize(sc_p scp)
+{
+ u_int dramsize;
+ static u_short magiccode[] = {0x386d, 0xbd2a, 0x73df, 0xf2d8};
+ static u_short magiccode2[] = {0x5ef3, 0x2b90, 0xa4c8, 0x6a13};
+ u_short buf[sizeof(magiccode) / sizeof(*magiccode)];
+
+ /*
+ * Write the magic code to the bottom of DRAM.
+ * Writing to a wrapped address clobbers the code.
+ */
+ emu_allocdmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE);
+ emu_dmaaddress(scp, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE, EMU8K_DRAM_RAM);
+ emu_writeblkstream(scp, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE, magiccode, sizeof(magiccode) / sizeof(*magiccode));
+ emu_releasedmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE);
+
+ for (dramsize = 0 ; dramsize + EMU8K_DRAM_RAM < EMU8K_DRAM_MAX ; ) {
+
+ /* Read the magic code. */
+ emu_allocdmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_READ);
+ emu_dmaaddress(scp, EMU8K_DMA_LEFT | EMU8K_DMA_READ, EMU8K_DRAM_RAM);
+ emu_readblkstream(scp, EMU8K_DMA_LEFT | EMU8K_DMA_READ, buf, sizeof(buf) / sizeof(*buf));
+ emu_releasedmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_READ);
+
+ /* Compare the code. */
+ if (bcmp(magiccode, buf, sizeof(magiccode)))
+ break;
+
+ /* Increase the DRAM size. */
+ dramsize += 0x8000;
+
+ /* Try writing a different magic code to dramsize. */
+ emu_allocdmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE);
+ emu_dmaaddress(scp, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE, dramsize + EMU8K_DRAM_RAM);
+ emu_writeblkstream(scp, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE, magiccode2, sizeof(magiccode2) / sizeof(*magiccode2));
+ emu_releasedmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_WRITE);
+
+ /* Then read the magic code. */
+ emu_allocdmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_READ);
+ emu_dmaaddress(scp, EMU8K_DMA_LEFT | EMU8K_DMA_READ, dramsize + EMU8K_DRAM_RAM);
+ emu_readblkstream(scp, EMU8K_DMA_LEFT | EMU8K_DMA_READ, buf, sizeof(buf) / sizeof(*buf));
+ emu_releasedmachn(scp, 31, EMU8K_DMA_LEFT | EMU8K_DMA_READ);
+
+ /* Compare the code. */
+ if (bcmp(magiccode2, buf, sizeof(magiccode2)))
+ break;
+ }
+ if (dramsize + EMU8K_DRAM_RAM > EMU8K_DRAM_MAX)
+ dramsize = EMU8K_DRAM_MAX - EMU8K_DRAM_RAM;
+
+ return dramsize * 2; /* dramsize is in words. */
+}
+
+/* Allocates a channel to a DMA stream. */
+static void
+emu_allocdmachn(sc_p scp, int chn, int mode)
+{
+ /* Turn off the sound, prepare for a DMA stream. */
+ emu_writedcysusv(scp, chn, 0, 0, 1, 0);
+ emu_writevtft(scp, chn, 0, 0);
+ emu_writecvcf(scp, chn, 0, 0);
+ emu_writeptrx(scp, chn, 0x4000, 0, 0);
+ emu_writecpf(scp, chn, 0x4000, 0);
+ emu_writepsst(scp, chn, 0, 0);
+ emu_writecsl(scp, chn, 0, 0);
+
+ /* Enter DMA mode. */
+ emu_writeccca(scp, chn, 0, 1,
+ ((mode & EMU8K_DMA_WRITE) > 0) ? 1 : 0,
+ ((mode & EMU8K_DMA_RIGHT) > 0) ? 1 : 0,
+ 0);
+}
+
+/* Programs the initial address to a DMA. */
+static void
+emu_dmaaddress(sc_p scp, int mode, u_int addr)
+{
+ /* Wait until the stream comes ready. */
+ emu_waitstream(scp, mode);
+
+ switch(mode & EMU8K_DMA_MASK)
+ {
+ case EMU8K_DMA_LEFT | EMU8K_DMA_READ:
+ emu_writesmalr(scp, 0, addr);
+ emu_readsmld(scp, NULL); /* Read the stale data. */
+ break;
+ case EMU8K_DMA_RIGHT | EMU8K_DMA_READ:
+ emu_writesmarr(scp, 0, addr);
+ emu_readsmrd(scp, NULL); /* Read the stale data. */
+ break;
+ case EMU8K_DMA_LEFT | EMU8K_DMA_WRITE:
+ emu_writesmalw(scp, 0, addr);
+ break;
+ case EMU8K_DMA_RIGHT | EMU8K_DMA_WRITE:
+ emu_writesmarw(scp, 0, addr);
+ break;
+ }
+}
+
+/* Waits until a stream gets ready. */
+static void
+emu_waitstream(sc_p scp, int mode)
+{
+ int i;
+ u_int busy;
+
+ for (i = 0 ; i < 100000 ; i++) {
+ switch(mode & EMU8K_DMA_MASK)
+ {
+ case EMU8K_DMA_LEFT | EMU8K_DMA_READ:
+ emu_readsmalr(scp, &busy, NULL);
+ break;
+ case EMU8K_DMA_RIGHT | EMU8K_DMA_READ:
+ emu_readsmarr(scp, &busy, NULL);
+ break;
+ case EMU8K_DMA_LEFT | EMU8K_DMA_WRITE:
+ emu_readsmalw(scp, &busy, NULL);
+ break;
+ case EMU8K_DMA_RIGHT | EMU8K_DMA_WRITE:
+ emu_readsmarw(scp, &busy, NULL);
+ break;
+ }
+ if (!busy)
+ break;
+ emu_delay(scp, 1);
+ }
+ if (busy)
+ printf("emu%d: stream data still busy, timed out.\n", device_get_unit(scp->dev));
+}
+
+/* Reads a word block from a stream. */
+static void
+emu_readblkstream(sc_p scp, int mode, u_short *data, size_t len)
+{
+ while((len--) > 0)
+ emu_readstream(scp, mode, data++);
+}
+
+/* Writes a word block stream to a stream. */
+static void
+emu_writeblkstream(sc_p scp, int mode, u_short *data, size_t len)
+{
+ while((len--) > 0)
+ emu_writestream(scp, mode, *(data++));
+}
+
+/* Reads a word from a stream. */
+static void
+emu_readstream(sc_p scp, int mode, u_short *data)
+{
+ if ((mode & EMU8K_DMA_RW) != EMU8K_DMA_READ)
+ return;
+
+ switch(mode & EMU8K_DMA_MASK)
+ {
+ case EMU8K_DMA_LEFT | EMU8K_DMA_READ:
+ emu_readsmld(scp, data);
+ break;
+ case EMU8K_DMA_RIGHT | EMU8K_DMA_READ:
+ emu_readsmrd(scp, data);
+ break;
+ }
+}
+
+/* Writes a word to a stream. */
+static void
+emu_writestream(sc_p scp, int mode, u_short data)
+{
+ if ((mode & EMU8K_DMA_RW) != EMU8K_DMA_WRITE)
+ return;
+
+ switch(mode & EMU8K_DMA_MASK)
+ {
+ case EMU8K_DMA_LEFT | EMU8K_DMA_WRITE:
+ emu_writesmld(scp, data);
+ break;
+ case EMU8K_DMA_RIGHT | EMU8K_DMA_WRITE:
+ emu_writesmrd(scp, data);
+ break;
+ }
+}
+
+/* Releases a channel from a DMA stream. */
+static void
+emu_releasedmachn(sc_p scp, int chn, int mode)
+{
+ /* Wait until the stream comes ready. */
+ emu_waitstream(scp, mode);
+
+ /* Leave DMA mode. */
+ emu_writeccca(scp, chn, 0, 0, 0, 0, 0);
+}
+
+/*
+ * Waits cycles.
+ * Idea-stolen-from: sys/i386/isa/clock.c:DELAY()
+ */
+static void
+emu_delay(sc_p scp, short n)
+{
+ int wc_prev, wc, wc_left, wc_delta;
+
+ emu_readwc(scp, &wc_prev);
+ wc_left = n;
+
+ while (wc_left > 0) {
+ emu_readwc(scp, &wc);
+ wc_delta = wc - wc_prev; /* The counter increases. */
+ wc_prev = wc;
+ if (wc_delta < 0)
+ wc_delta += 0xffff;
+ wc_left -= wc_delta;
+ }
+}
+
+/* The followings provide abstruct access to the registers. */
+#define DECBIT(sts, shift, len) (((sts) >> (shift))) & (0xffffffff >> (32 - len))
+#define GENBIT(val, shift, len) (((val) & (0xffffffff >> (32 - len))) << (shift))
+
+/* CPF: Current Pitch and Fractional Address */
+static void
+emu_readcpf(sc_p scp, int chn, u_int *cp, u_int *f)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_CPF, chn);
+ if (cp != NULL)
+ *cp = DECBIT(sts, 16, 16);
+ if (f != NULL)
+ *f = DECBIT(sts, 0, 16);
+}
+
+static void
+emu_writecpf(sc_p scp, int chn, u_int cp, u_int f)
+{
+ emu_command(scp, EMU8K_CPF, chn,
+ GENBIT(cp, 16, 16)
+ | GENBIT(f, 0, 16));
+}
+
+/* PTRX: Pitch Target, Rvb Send and Aux Byte */
+static void
+emu_readptrx(sc_p scp, int chn, u_int *pt, u_int *rs, u_int *auxd)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_PTRX, chn);
+ if (pt != NULL)
+ *pt = DECBIT(sts, 16, 16);
+ if (rs != NULL)
+ *rs = DECBIT(sts, 8, 8);
+ if (auxd != NULL)
+ *auxd = DECBIT(sts, 0, 8);
+}
+
+static void
+emu_writeptrx(sc_p scp, int chn, u_int pt, u_int rs, u_int auxd)
+{
+ emu_command(scp, EMU8K_PTRX, chn,
+ GENBIT(pt, 16, 16)
+ | GENBIT(rs, 8, 8)
+ | GENBIT(auxd, 0, 8));
+}
+
+/* CVCF: Current Volume and Filter Cutoff */
+static void
+emu_readcvcf(sc_p scp, int chn, u_int *cv, u_int *cf)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_CVCF, chn);
+ if (cv != NULL)
+ *cv = DECBIT(sts, 16, 16);
+ if (cf != NULL)
+ *cf = DECBIT(sts, 0, 16);
+}
+
+static void
+emu_writecvcf(sc_p scp, int chn, u_int cv, u_int cf)
+{
+ emu_command(scp, EMU8K_CVCF, chn,
+ GENBIT(cv, 16, 16)
+ | GENBIT(cf, 0, 16));
+}
+
+/* VTFT: Volume and Filter Cutoff Targets */
+static void
+emu_readvtft(sc_p scp, int chn, u_int *vt, u_int *ft)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_VTFT, chn);
+ if (vt != NULL)
+ *vt = DECBIT(sts, 16, 16);
+ if (ft != NULL)
+ *ft = DECBIT(sts, 0, 16);
+}
+
+static void
+emu_writevtft(sc_p scp, int chn, u_int vt, u_int ft)
+{
+ emu_command(scp, EMU8K_VTFT, chn,
+ GENBIT(vt, 16, 16)
+ | GENBIT(ft, 0, 16));
+}
+
+/* PSST: Pan Send and Loop Start Address */
+static void
+emu_readpsst(sc_p scp, int chn, u_int *pan, u_int *st)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_PSST, chn);
+ if (pan != NULL)
+ *pan = DECBIT(sts, 24, 8);
+ if (st != NULL)
+ *st = DECBIT(sts, 0, 24);
+}
+
+static void
+emu_writepsst(sc_p scp, int chn, u_int pan, u_int st)
+{
+ emu_command(scp, EMU8K_PSST, chn,
+ GENBIT(pan, 24, 8)
+ | GENBIT(st, 0, 24));
+}
+
+/* CSL: Chorus Send and Loop End Address */
+static void
+emu_readcsl(sc_p scp, int chn, u_int *cs, u_int *lp)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_CSL, chn);
+ if (cs != NULL)
+ *cs = DECBIT(sts, 24, 8);
+ if (lp != NULL)
+ *lp = DECBIT(sts, 0, 24);
+}
+
+static void
+emu_writecsl(sc_p scp, int chn, u_int cs, u_int lp)
+{
+ emu_command(scp, EMU8K_CSL, chn,
+ GENBIT(cs, 24, 8)
+ | GENBIT(lp, 0, 24));
+}
+
+/* CCCA: Q, Control Bits and Current Address */
+static void
+emu_readccca(sc_p scp, int chn, u_int *q, u_int *dma, u_int *wr, u_int *right, u_int *ca)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_CCCA, chn);
+ if (q != NULL)
+ *q = DECBIT(sts, 28, 4);
+ if (dma != NULL)
+ *dma = DECBIT(sts, 26, 1);
+ if (wr != NULL)
+ *wr = DECBIT(sts, 25, 1);
+ if (right != NULL)
+ *right = DECBIT(sts, 24, 1);
+ if (ca != NULL)
+ *ca = DECBIT(sts, 0, 24);
+}
+
+static void
+emu_writeccca(sc_p scp, int chn, u_int q, u_int dma, u_int wr, u_int right, u_int ca)
+{
+ emu_command(scp, EMU8K_CCCA, chn,
+ GENBIT(q, 28, 4)
+ | GENBIT(dma, 26, 1)
+ | GENBIT(wr, 25, 1)
+ | GENBIT(right, 24, 1)
+ | GENBIT(ca, 0, 24));
+}
+
+/* HWCF4: Configuration Double Word 4 */
+static void
+emu_readhwcf4(sc_p scp, u_int *val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_HWCF4, 0);
+ if (val != NULL)
+ *val = sts;
+}
+
+static void
+emu_writehwcf4(sc_p scp, u_int val)
+{
+ if (val != 0)
+ printf("emu%d: writing value 0x%x to HWCF4.\n", device_get_unit(scp->dev), val);
+ emu_command(scp, EMU8K_HWCF4, 0, val);
+}
+
+/* HWCF5: Configuration Double Word 5 */
+static void
+emu_readhwcf5(sc_p scp, u_int *val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_HWCF5, 0);
+ if (val != NULL)
+ *val = sts;
+}
+
+static void
+emu_writehwcf5(sc_p scp, u_int val)
+{
+ if (val != 0x00000083)
+ printf("emu%d: writing value 0x%x to HWCF5.\n", device_get_unit(scp->dev), val);
+ emu_command(scp, EMU8K_HWCF5, 0, val);
+}
+
+/* HWCF6: Configuration Double Word 6 */
+static void
+emu_readhwcf6(sc_p scp, u_int *val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_HWCF6, 0);
+ if (val != NULL)
+ *val = sts;
+}
+
+static void
+emu_writehwcf6(sc_p scp, u_int val)
+{
+ if (val != 0x00008000)
+ printf("emu%d: writing value 0x%x to HWCF6.\n", device_get_unit(scp->dev), val);
+ emu_command(scp, EMU8K_HWCF6, 0, val);
+}
+
+/* SMALR: Sound Memory Address for Left SM Reads */
+static void
+emu_readsmalr(sc_p scp, u_int *mt, u_int *smalr)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_SMALR, 0);
+ if (mt != NULL)
+ *mt = DECBIT(sts, 31, 1);
+ if (smalr != NULL)
+ *smalr = DECBIT(sts, 0, 24);
+}
+
+static void
+emu_writesmalr(sc_p scp, u_int mt, u_int smalr)
+{
+ emu_command(scp, EMU8K_SMALR, 0,
+ GENBIT(mt, 31, 1)
+ | GENBIT(smalr, 0, 24));
+}
+
+/* SMARR: Sound Memory Address for Right SM Reads */
+static void
+emu_readsmarr(sc_p scp, u_int *mt, u_int *smarr)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_SMARR, 0);
+ if (mt != NULL)
+ *mt = DECBIT(sts, 31, 1);
+ if (smarr != NULL)
+ *smarr = DECBIT(sts, 0, 24);
+}
+
+static void
+emu_writesmarr(sc_p scp, u_int mt, u_int smarr)
+{
+ emu_command(scp, EMU8K_SMARR, 0,
+ GENBIT(mt, 31, 1)
+ | GENBIT(smarr, 0, 24));
+}
+
+/* SMALW: Sound Memory Address for Left SM Writes */
+static void
+emu_readsmalw(sc_p scp, u_int *full, u_int *smalw)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_SMALW, 0);
+ if (full != NULL)
+ *full = DECBIT(sts, 31, 1);
+ if (smalw != NULL)
+ *smalw = DECBIT(sts, 0, 24);
+}
+
+static void
+emu_writesmalw(sc_p scp, u_int full, u_int smalw)
+{
+ emu_command(scp, EMU8K_SMALW, 0,
+ GENBIT(full, 31, 1)
+ | GENBIT(smalw, 0, 24));
+}
+
+/* SMARW: Sound Memory Address for Right SM Writes */
+static void
+emu_readsmarw(sc_p scp, u_int *full, u_int *smarw)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_SMARW, 0);
+ if (full != NULL)
+ *full = DECBIT(sts, 31, 1);
+ if (smarw != NULL)
+ *smarw = DECBIT(sts, 0, 24);
+}
+
+static void
+emu_writesmarw(sc_p scp, u_int full, u_int smarw)
+{
+ emu_command(scp, EMU8K_SMARW, 0,
+ GENBIT(full, 31, 1)
+ | GENBIT(smarw, 0, 24));
+}
+
+/* SMLD: Sound Memory Left Data */
+static void
+emu_readsmld(sc_p scp, u_short *smld)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_SMLD, 0);
+ if (smld != NULL)
+ *smld = sts;
+}
+
+static void
+emu_writesmld(sc_p scp, u_short smld)
+{
+ emu_command(scp, EMU8K_SMLD, 0, smld);
+}
+
+/* SMRD: Sound Memory Right Data */
+static void
+emu_readsmrd(sc_p scp, u_short *smrd)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_SMRD, 0);
+ if (smrd != NULL)
+ *smrd = sts;
+}
+
+static void
+emu_writesmrd(sc_p scp, u_short smrd)
+{
+ emu_command(scp, EMU8K_SMRD, 0, smrd);
+}
+
+/* WC: Sample COunter */
+static void
+emu_readwc(sc_p scp, u_int *wc)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_WC, 0);
+ if (wc != NULL)
+ *wc = sts;
+}
+
+static void
+emu_writewc(sc_p scp, u_int wc)
+{
+ emu_command(scp, EMU8K_WC, 0, wc);
+}
+
+/* HWCF1: Configuration Double Word 1 */
+static void
+emu_readhwcf1(sc_p scp, u_int *val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_HWCF1, 0);
+ if (val != NULL)
+ *val = sts;
+}
+
+static void
+emu_writehwcf1(sc_p scp, u_int val)
+{
+ if (val != 0x0059)
+ printf("emu%d: writing value 0x%x to HWCF1.\n", device_get_unit(scp->dev), val);
+ emu_command(scp, EMU8K_HWCF1, 0, val);
+}
+
+/* HWCF2: Configuration Double Word 2 */
+static void
+emu_readhwcf2(sc_p scp, u_int *val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_HWCF2, 0);
+ if (val != NULL)
+ *val = sts;
+}
+
+static void
+emu_writehwcf2(sc_p scp, u_int val)
+{
+ if (val != 0x0020)
+ printf("emu%d: writing value 0x%x to HWCF2.\n", device_get_unit(scp->dev), val);
+ emu_command(scp, EMU8K_HWCF2, 0, val);
+}
+
+/* HWCF3: Configuration Double Word 3 */
+static void
+emu_readhwcf3(sc_p scp, u_int *val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_HWCF3, 0);
+ if (val != NULL)
+ *val = sts;
+}
+
+static void
+emu_writehwcf3(sc_p scp, u_int val)
+{
+ if (val != 0x0004 && val != 0)
+ printf("emu%d: writing value 0x%x to HWCF3.\n", device_get_unit(scp->dev), val);
+ emu_command(scp, EMU8K_HWCF3, 0, val);
+}
+
+/* INIT1: Initialization Array 1 */
+static void
+emu_readinit1(sc_p scp, int chn, u_int *val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_INIT1, chn);
+ if (val != NULL)
+ *val = sts;
+}
+
+static void
+emu_writeinit1(sc_p scp, int chn, u_int val)
+{
+ emu_command(scp, EMU8K_INIT1, chn, val);
+}
+
+/* INIT2: Initialization Array 2 */
+static void
+emu_readinit2(sc_p scp, int chn, u_int *val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_INIT2, chn);
+ if (val != NULL)
+ *val = sts;
+}
+
+static void
+emu_writeinit2(sc_p scp, int chn, u_int val)
+{
+ emu_command(scp, EMU8K_INIT2, chn, val);
+}
+
+/* INIT3: Initialization Array 3 */
+static void
+emu_readinit3(sc_p scp, int chn, u_int *val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_INIT3, chn);
+ if (val != NULL)
+ *val = sts;
+}
+
+static void
+emu_writeinit3(sc_p scp, int chn, u_int val)
+{
+ emu_command(scp, EMU8K_INIT3, chn, val);
+}
+
+/* INIT4: Initialization Array 4 */
+static void
+emu_readinit4(sc_p scp, int chn, u_int *val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_INIT4, chn);
+ if (val != NULL)
+ *val = sts;
+}
+
+static void
+emu_writeinit4(sc_p scp, int chn, u_int val)
+{
+ emu_command(scp, EMU8K_INIT4, chn, val);
+}
+
+/* ENVVOL: Volume Envelope Decay */
+static void
+emu_readenvvol(sc_p scp, int chn, u_int *envvol)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_ENVVOL, chn);
+ if (envvol != NULL)
+ *envvol = sts;
+}
+
+static void
+emu_writeenvvol(sc_p scp, int chn, u_int envvol)
+{
+ emu_command(scp, EMU8K_ENVVOL, chn, envvol);
+}
+
+/* DCYSUSV: Volume Envelope Sustain and Decay */
+static void
+emu_readdcysusv(sc_p scp, int chn, u_int *ph1v, u_int *susv, u_int *off, u_int *dcyv)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_DCYSUSV, chn);
+ if (ph1v != NULL)
+ *ph1v = DECBIT(sts, 15, 1);
+ if (susv != NULL)
+ *susv = DECBIT(sts, 8, 7);
+ if (off != NULL)
+ *off = DECBIT(sts, 7, 1);
+ if (dcyv != NULL)
+ *dcyv = DECBIT(sts, 0, 7);
+}
+
+static void
+emu_writedcysusv(sc_p scp, int chn, u_int ph1v, u_int susv, u_int off, u_int dcyv)
+{
+ emu_command(scp, EMU8K_DCYSUSV, chn,
+ GENBIT(ph1v, 15, 1)
+ | GENBIT(susv, 8, 7)
+ | GENBIT(off, 7, 1)
+ | GENBIT(dcyv, 0, 7));
+}
+
+/* ENVVAL: Modulation Envelope Decay */
+static void
+emu_readenvval(sc_p scp, int chn, u_int *envval)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_ENVVAL, chn);
+ if (envval != NULL)
+ *envval = sts;
+}
+
+static void
+emu_writeenvval(sc_p scp, int chn, u_int envval)
+{
+ emu_command(scp, EMU8K_ENVVAL, chn, envval);
+}
+
+/* DCYSUS: Modulation Envelope Sustain and Decay */
+static void
+emu_readdcysus(sc_p scp, int chn, u_int *ph1, u_int *sus, u_int *dcy)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_DCYSUS, chn);
+ if (ph1 != NULL)
+ *ph1 = DECBIT(sts, 15, 1);
+ if (sus != NULL)
+ *sus = DECBIT(sts, 8, 7);
+ if (dcy != NULL)
+ *dcy = DECBIT(sts, 0, 7);
+}
+
+static void
+emu_writedcysus(sc_p scp, int chn, u_int ph1, u_int sus, u_int dcy)
+{
+ emu_command(scp, EMU8K_DCYSUS, chn,
+ GENBIT(ph1, 15, 1)
+ | GENBIT(sus, 8, 7)
+ | GENBIT(dcy, 0, 7));
+}
+
+/* ATKHLDV: Volume Envelope Hold and Attack */
+static void
+emu_readatkhldv(sc_p scp, int chn, u_int *atkhldv)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_ATKHLDV, chn);
+ if (atkhldv != NULL)
+ *atkhldv = sts;
+}
+
+static void
+emu_writeatkhldv(sc_p scp, int chn, u_int atkhldv)
+{
+ emu_command(scp, EMU8K_ATKHLDV, chn, atkhldv);
+}
+
+/* LFO1VAL: LFO #1 Delay */
+static void
+emu_readlfo1val(sc_p scp, int chn, u_int *lfo1val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_LFO1VAL, chn);
+ if (lfo1val != NULL)
+ *lfo1val = sts;
+}
+
+static void
+emu_writelfo1val(sc_p scp, int chn, u_int lfo1val)
+{
+ emu_command(scp, EMU8K_LFO1VAL, chn, lfo1val);
+}
+
+/* ATKHLD: Modulation Envelope Hold and Attack */
+static void
+emu_readatkhld(sc_p scp, int chn, u_int *atkhld)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_ATKHLD, chn);
+ if (atkhld != NULL)
+ *atkhld = sts;
+}
+
+static void
+emu_writeatkhld(sc_p scp, int chn, u_int atkhld)
+{
+ emu_command(scp, EMU8K_ATKHLD, chn, atkhld);
+}
+
+/* LFO2VAL: LFO #2 Delay */
+static void
+emu_readlfo2val(sc_p scp, int chn, u_int *lfo2val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_LFO2VAL, chn);
+ if (lfo2val != NULL)
+ *lfo2val = sts;
+}
+
+static void
+emu_writelfo2val(sc_p scp, int chn, u_int lfo2val)
+{
+ emu_command(scp, EMU8K_LFO2VAL, chn, lfo2val);
+}
+
+/* IP: Initial Pitch */
+static void
+emu_readip(sc_p scp, int chn, u_int *ip)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_IP, chn);
+ if (ip != NULL)
+ *ip = sts;
+}
+
+static void
+emu_writeip(sc_p scp, int chn, u_int ip)
+{
+ emu_command(scp, EMU8K_IP, chn, ip);
+}
+
+/* IFATN: Initial Filter Cutoff and Attenuation */
+static void
+emu_readifatn(sc_p scp, int chn, u_int *ifc, u_int *atn)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_IFATN, chn);
+ if (ifc != NULL)
+ *ifc = DECBIT(sts, 8, 8);
+ if (atn != NULL)
+ *atn = DECBIT(sts, 0, 8);
+}
+
+static void
+emu_writeifatn(sc_p scp, int chn, u_int ifc, u_int atn)
+{
+ emu_command(scp, EMU8K_IFATN, chn,
+ GENBIT(ifc, 8, 8)
+ | GENBIT(atn, 0, 8));
+}
+
+/* PEFE: Pitch and Filter Envelope Heights */
+static void
+emu_readpefe(sc_p scp, int chn, u_int *pe, u_int *fe)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_PEFE, chn);
+ if (pe != NULL)
+ *pe = DECBIT(sts, 8, 8);
+ if (fe != NULL)
+ *fe = DECBIT(sts, 0, 8);
+}
+
+static void
+emu_writepefe(sc_p scp, int chn, u_int pe, u_int fe)
+{
+ emu_command(scp, EMU8K_PEFE, chn,
+ GENBIT(pe, 8, 8)
+ | GENBIT(fe, 0, 8));
+}
+
+/* FMMOD: Vibrato and Filter Modulation from LFO #1 */
+static void
+emu_readfmmod(sc_p scp, int chn, u_int *fm, u_int *mod)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_FMMOD, chn);
+ if (fm != NULL)
+ *fm = DECBIT(sts, 8, 8);
+ if (mod != NULL)
+ *mod = DECBIT(sts, 0, 8);
+}
+
+static void
+emu_writefmmod(sc_p scp, int chn, u_int fm, u_int mod)
+{
+ emu_command(scp, EMU8K_FMMOD, chn,
+ GENBIT(fm, 8, 8)
+ | GENBIT(mod, 0, 8));
+}
+
+/* TREMFRQ: LFO #1 Tremolo Amount and Frequency */
+static void
+emu_readtremfrq(sc_p scp, int chn, u_int *trem, u_int *frq)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_TREMFRQ, chn);
+ if (trem != NULL)
+ *trem = DECBIT(sts, 8, 8);
+ if (frq != NULL)
+ *frq = DECBIT(sts, 0, 8);
+}
+
+static void
+emu_writetremfrq(sc_p scp, int chn, u_int trem, u_int frq)
+{
+ emu_command(scp, EMU8K_TREMFRQ, chn,
+ GENBIT(trem, 8, 8)
+ | GENBIT(frq, 0, 8));
+}
+
+/* FM2FRQ2: LFO #2 Vibrato Amount and Frequency */
+static void
+emu_readfm2frq2(sc_p scp, int chn, u_int *fm2, u_int *frq2)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_FM2FRQ2, chn);
+ if (fm2 != NULL)
+ *fm2 = DECBIT(sts, 8, 8);
+ if (frq2 != NULL)
+ *frq2 = DECBIT(sts, 0, 8);
+}
+
+static void
+emu_writefm2frq2(sc_p scp, int chn, u_int fm2, u_int frq2)
+{
+ emu_command(scp, EMU8K_FM2FRQ2, chn,
+ GENBIT(fm2, 8, 8)
+ | GENBIT(frq2, 0, 8));
+}
+
+/* PROBE: Probe Register */
+static void
+emu_readprobe(sc_p scp, u_int *val)
+{
+ u_long sts;
+
+ sts = emu_status(scp, EMU8K_PROBE, 0);
+ if (val != NULL)
+ *val = sts;
+}
+
+static void
+emu_writeprobe(sc_p scp, u_int val)
+{
+ emu_command(scp, EMU8K_PROBE, 0, val);
+}
+
+/* Writes to a register. */
+static void
+emu_command(sc_p scp, int reg, int chn, u_long val)
+{
+ if (chn < 0 || chn >= EMU8K_MAXVOICE || reg < 0 || reg >= EMU8K_REGNUM)
+ return;
+
+ /* Override the channel if necessary. */
+ if (emu_regs[reg].chn != EMU8K_CHN_ANY)
+ chn = emu_regs[reg].chn;
+
+ /* Select the register first. */
+ bus_space_write_2(rman_get_bustag(scp->io[EMU8K_IDX_PTR]), rman_get_bushandle(scp->io[EMU8K_IDX_PTR]), EMU8K_PORT_PTR, (chn & 0x1f) | ((emu_regs[reg].reg & 0x07) << 5));
+
+ /* Then we write the data. */
+ bus_space_write_2(rman_get_bustag(scp->io[emu_regs[reg].index]), rman_get_bushandle(scp->io[emu_regs[reg].index]), emu_regs[reg].port, val & 0xffff);
+ if (emu_regs[reg].size)
+ /* double word */
+ bus_space_write_2(rman_get_bustag(scp->io[emu_regs[reg].index]), rman_get_bushandle(scp->io[emu_regs[reg].index]), emu_regs[reg].port + 2, (val >> 16) & 0xffff);
+}
+
+/* Reads from a register. */
+static u_long
+emu_status(sc_p scp, int reg, int chn)
+{
+ u_long status;
+
+ if (chn < 0 || chn >= EMU8K_MAXVOICE || reg < 0 || reg >= EMU8K_REGNUM)
+ return (0xffffffff);
+
+ /* Override the channel if necessary. */
+ if (emu_regs[reg].chn != EMU8K_CHN_ANY)
+ chn = emu_regs[reg].chn;
+
+ /* Select the register first. */
+ bus_space_write_2(rman_get_bustag(scp->io[EMU8K_IDX_PTR]), rman_get_bushandle(scp->io[EMU8K_IDX_PTR]), EMU8K_PORT_PTR, (chn & 0x1f) | ((emu_regs[reg].reg & 0x07) << 5));
+
+ /* Then we read the data. */
+ status = bus_space_read_2(rman_get_bustag(scp->io[emu_regs[reg].index]), rman_get_bushandle(scp->io[emu_regs[reg].index]), emu_regs[reg].port) & 0xffff;
+ if (emu_regs[reg].size)
+ /* double word */
+ status |= (bus_space_read_2(rman_get_bustag(scp->io[emu_regs[reg].index]), rman_get_bushandle(scp->io[emu_regs[reg].index]), emu_regs[reg].port + 2) & 0xffff) << 16;
+
+ return (status);
+}
+
+/* Allocates resources. */
+static int
+emu_allocres(sc_p scp, device_t dev)
+{
+ int iobase;
+
+ if (scp->io[0] == NULL) {
+ scp->io_rid[0] = 0;
+ scp->io[0] = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid[0], 0, ~0, 4, RF_ACTIVE);
+ }
+ if (scp->io[0] == NULL)
+ return (1);
+ iobase = rman_get_start(scp->io[0]);
+ if (scp->io[1] == NULL) {
+ scp->io_rid[1] = 1;
+ scp->io[1] = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid[1], iobase + 0x400, iobase + 0x400 + 3, 4, RF_ACTIVE);
+ }
+ if (scp->io[2] == NULL) {
+ scp->io_rid[2] = 2;
+ scp->io[2] = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid[2], iobase + 0x800, iobase + 0x800 + 3, 4, RF_ACTIVE);
+ }
+
+ if (scp->io[0] == NULL || scp->io[1] == NULL || scp->io[2] == NULL) {
+ printf("emu_allocres: failed.\n");
+ return (1);
+ }
+
+ return (0);
+}
+
+/* Releases resources. */
+static void
+emu_releaseres(sc_p scp, device_t dev)
+{
+ if (scp->io[0] != NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, scp->io_rid[0], scp->io[0]);
+ scp->io[0] = NULL;
+ }
+ if (scp->io[1] != NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, scp->io_rid[1], scp->io[1]);
+ scp->io[1] = NULL;
+ }
+ if (scp->io[2] != NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, scp->io_rid[2], scp->io[2]);
+ scp->io[2] = NULL;
+ }
+}
+
+static device_method_t emu_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe , emu_probe ),
+ DEVMETHOD(device_attach, emu_attach),
+
+ { 0, 0 },
+};
+
+static driver_t emu_driver = {
+ "midi",
+ emu_methods,
+ sizeof(struct emu_softc),
+};
+
+DRIVER_MODULE(emu, isa, emu_driver, midi_devclass, 0, 0);
diff --git a/sys/dev/sound/isa/gusc.c b/sys/dev/sound/isa/gusc.c
index 7235e16..f75f2f0 100644
--- a/sys/dev/sound/isa/gusc.c
+++ b/sys/dev/sound/isa/gusc.c
@@ -53,6 +53,13 @@
#define LOGICALID_OPL 0x0300561e
#define LOGICALID_MIDI 0x0400561e
+/* PnP IDs */
+static struct isa_pnp_id gusc_ids[] = {
+ {LOGICALID_PCM, "GRV0000 Gravis UltraSound PnP PCM"}, /* GRV0000 */
+ {LOGICALID_OPL, "GRV0003 Gravis UltraSound PnP OPL"}, /* GRV0003 */
+ {LOGICALID_MIDI, "GRV0004 Gravis UltraSound PnP MIDI"}, /* GRV0004 */
+};
+
/* Interrupt handler. */
struct gusc_ihandler {
void (*intr)(void *);
@@ -88,9 +95,7 @@ static struct resource *gusc_alloc_resource(device_t bus, device_t child, int ty
static int gusc_release_resource(device_t bus, device_t child, int type, int rid,
struct resource *r);
-#if notyet
static device_t find_masterdev(sc_p scp);
-#endif /* notyet */
static int alloc_resource(sc_p scp);
static int release_resource(sc_p scp);
@@ -100,52 +105,53 @@ static int
gusc_probe(device_t dev)
{
device_t child;
- u_int32_t vend_id, logical_id;
+ u_int32_t logical_id;
char *s;
struct sndcard_func *func;
-
- vend_id = isa_get_vendorid(dev);
- if (vend_id == 0)
- return gusisa_probe(dev);
+ int ret;
logical_id = isa_get_logicalid(dev);
s = NULL;
- if (vend_id == 0x0100561e) { /* Gravis */
- switch (logical_id) {
- case LOGICALID_PCM:
- s = "Gravis UltraSound Plug & Play PCM";
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
- if (func == NULL)
- return (ENOMEM);
- bzero(func, sizeof(*func));
- func->func = SCF_PCM;
- child = device_add_child(dev, "pcm", -1);
- device_set_ivars(child, func);
- break;
-#if notyet
- case LOGICALID_OPL:
- s = "Gravis UltraSound Plug & Play OPL";
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
- if (func == NULL)
- return (ENOMEM);
- bzero(func, sizeof(*func));
- func->func = SCF_SYNTH;
- child = device_add_child(dev, "midi", -1);
- device_set_ivars(child, func);
- break;
- case LOGICALID_MIDI:
- s = "Gravis UltraSound Plug & Play MIDI";
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
- if (func == NULL)
- return (ENOMEM);
- bzero(func, sizeof(*func));
- func->func = SCF_MIDI;
- child = device_add_child(dev, "midi", -1);
- device_set_ivars(child, func);
- break;
-#endif /* notyet */
- }
+ /* Check isapnp ids */
+ if (logical_id != 0 && (ret = ISA_PNP_PROBE(device_get_parent(dev), dev, gusc_ids)) != 0)
+ return (ret);
+ else {
+ if (logical_id == 0)
+ return gusisa_probe(dev);
+ }
+
+ switch (logical_id) {
+ case LOGICALID_PCM:
+ s = "Gravis UltraSound Plug & Play PCM";
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
+ if (func == NULL)
+ return (ENOMEM);
+ bzero(func, sizeof(*func));
+ func->func = SCF_PCM;
+ child = device_add_child(dev, "pcm", -1);
+ device_set_ivars(child, func);
+ break;
+ case LOGICALID_OPL:
+ s = "Gravis UltraSound Plug & Play OPL";
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
+ if (func == NULL)
+ return (ENOMEM);
+ bzero(func, sizeof(*func));
+ func->func = SCF_SYNTH;
+ child = device_add_child(dev, "midi", -1);
+ device_set_ivars(child, func);
+ break;
+ case LOGICALID_MIDI:
+ s = "Gravis UltraSound Plug & Play MIDI";
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
+ if (func == NULL)
+ return (ENOMEM);
+ bzero(func, sizeof(*func));
+ func->func = SCF_MIDI;
+ child = device_add_child(dev, "midi", -1);
+ device_set_ivars(child, func);
+ break;
}
if (s != NULL) {
@@ -265,7 +271,6 @@ gusisa_probe(device_t dev)
bus_set_resource(dev, SYS_RES_DRQ, 1,
flags & DV_F_DRQ_MASK, 1);
-#if notyet
/* We can support the CS4231 and MIDI devices. */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
@@ -275,7 +280,6 @@ gusisa_probe(device_t dev)
func->func = SCF_MIDI;
child = device_add_child(dev, "midi", -1);
device_set_ivars(child, func);
-#endif /* notyet */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
@@ -342,13 +346,11 @@ gusc_intr(void *arg)
(*scp->pcm_intr.intr)(scp->pcm_intr.arg);
did_something = 1;
}
-#if notyet
if (scp->midi_intr.intr != NULL &&
(port_rd(scp->io[1], 0) & 0x80)) {
(*scp->midi_intr.intr)(scp->midi_intr.arg);
did_something = 1;
}
-#endif /* notyet */
} while (did_something != 0);
}
@@ -444,7 +446,6 @@ gusc_setup_intr(device_t dev, device_t child, struct resource *irq,
arg, cookiep);
}
-#if notyet
static device_t
find_masterdev(sc_p scp)
{
@@ -467,7 +468,6 @@ find_masterdev(sc_p scp)
return (dev);
}
-#endif /* notyet */
static int io_range[3] = {0x10, 0x8 , 0x4 };
static int io_offset[3] = {0x0 , 0x100, 0x10c};
@@ -475,9 +475,7 @@ static int
alloc_resource(sc_p scp)
{
int i, base, lid, flags;
-#if notyet
device_t dev;
-#endif /* notyet */
flags = 0;
if (isa_get_vendorid(scp->dev))
@@ -534,7 +532,6 @@ alloc_resource(sc_p scp)
}
}
break;
-#if notyet
case LOGICALID_OPL:
if (scp->io[0] == NULL) {
scp->io_rid[0] = 0;
@@ -567,7 +564,6 @@ alloc_resource(sc_p scp)
scp->irq_alloced = 0;
}
break;
-#endif /* notyet */
}
return (0);
}
@@ -576,9 +572,7 @@ static int
release_resource(sc_p scp)
{
int i, lid, flags;
-#if notyet
device_t dev;
-#endif /* notyet */
flags = 0;
if (isa_get_vendorid(scp->dev))
@@ -607,7 +601,6 @@ release_resource(sc_p scp)
}
}
break;
-#if notyet
case LOGICALID_OPL:
if (scp->io[0] != NULL) {
bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[0], scp->io[0]);
@@ -628,7 +621,6 @@ release_resource(sc_p scp)
scp->irq = NULL;
}
break;
-#endif /* notyet */
}
return (0);
}
diff --git a/sys/dev/sound/isa/gusmidi.c b/sys/dev/sound/isa/gusmidi.c
new file mode 100644
index 0000000..11644f8
--- /dev/null
+++ b/sys/dev/sound/isa/gusmidi.c
@@ -0,0 +1,523 @@
+/*
+ * GUS midi interface driver.
+ * Based on the newmidi MPU401 driver.
+ *
+ * Copyright (c) 1999 Ville-Pertti Keinonen
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * Modified: Riccardo Facchetti 24 Mar 1995 - Added the Audio Excel DSP 16
+ * initialization routine.
+ *
+ * Ported to the new Audio Driver by Luigi Rizzo:
+ * (C) 1999 Seigo Tanimura <tanimura@r.dl.itc.u-tokyo.ac.jp>
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <dev/sound/midi/midi.h>
+
+#include <dev/sound/chip.h>
+#include <machine/cpufunc.h>
+
+static devclass_t midi_devclass;
+
+extern synthdev_info midisynth_op_desc;
+
+/* These are the synthesizer and the midi interface information. */
+static struct synth_info gusmidi_synthinfo = {
+ "GUS MIDI",
+ 0,
+ SYNTH_TYPE_MIDI,
+ 0,
+ 0,
+ 128,
+ 128,
+ 128,
+ SYNTH_CAP_INPUT,
+};
+
+static struct midi_info gusmidi_midiinfo = {
+ "GUS MIDI",
+ 0,
+ 0,
+ 0,
+};
+
+#define MIDICTL_MASTER_RESET 0x03
+#define MIDICTL_TX_IRQ_EN 0x20
+#define MIDICTL_RX_IRQ_EN 0x80
+
+#define MIDIST_RXFULL 0x01
+#define MIDIST_TXDONE 0x02
+#define MIDIST_ERR_FR 0x10
+#define MIDIST_ERR_OVR 0x20
+#define MIDIST_INTR_PEND 0x80
+
+#define PORT_CTL 0
+#define PORT_ST 0
+#define PORT_TX 1
+#define PORT_RX 1
+
+/*
+ * These functions goes into gusmidi_op_desc to get called
+ * from sound.c.
+ */
+
+static int gusmidi_probe(device_t dev);
+static int gusmidi_attach(device_t dev);
+
+static d_open_t gusmidi_open;
+static d_ioctl_t gusmidi_ioctl;
+driver_intr_t gusmidi_intr;
+static midi_callback_t gusmidi_callback;
+
+/* Here is the parameter structure per a device. */
+struct gusmidi_softc {
+ device_t dev; /* device information */
+ mididev_info *devinfo; /* midi device information */
+
+ struct resource *io; /* Base of io port */
+ int io_rid; /* Io resource ID */
+ struct resource *irq; /* Irq */
+ int irq_rid; /* Irq resource ID */
+ void *ih; /* Interrupt cookie */
+
+ struct callout_handle dh; /* Callout handler for delay */
+
+ int ctl; /* Control bits. */
+};
+
+typedef struct gusmidi_softc *sc_p;
+
+/* These functions are local. */
+static int gusmidi_init(device_t dev);
+static int gusmidi_allocres(sc_p scp, device_t dev);
+static void gusmidi_releaseres(sc_p scp, device_t dev);
+static void gusmidi_startplay(sc_p scp);
+static void gusmidi_xmit(sc_p scp);
+static u_int gusmidi_readport(sc_p scp, int off);
+static void gusmidi_writeport(sc_p scp, int off, u_int8_t value);
+
+/*
+ * This is the device descriptor for the midi device.
+ */
+static mididev_info gusmidi_op_desc = {
+ "GUS midi",
+
+ SNDCARD_GUS,
+
+ gusmidi_open,
+ NULL,
+ NULL,
+ NULL,
+ gusmidi_ioctl,
+ NULL,
+
+ gusmidi_callback,
+
+ MIDI_BUFFSIZE, /* Queue Length */
+
+ 0, /* XXX This is not an *audio* device! */
+};
+
+static int
+gusmidi_probe(device_t dev)
+{
+ char *s;
+ sc_p scp;
+ struct sndcard_func *func;
+
+ /* The parent device has already been probed. */
+
+ func = device_get_ivars(dev);
+ if (func == NULL || func->func != SCF_MIDI)
+ return (ENXIO);
+
+ s = "GUS Midi Interface";
+
+ scp = device_get_softc(dev);
+ bzero(scp, sizeof(*scp));
+ scp->io_rid = 1;
+ scp->irq_rid = 0;
+#if notdef
+ ret = mpu_probe2(dev);
+ if (ret != 0)
+ return (ret);
+#endif /* notdef */
+ device_set_desc(dev, s);
+ return (0);
+}
+
+static int
+gusmidi_attach(device_t dev)
+{
+ sc_p scp;
+
+ scp = device_get_softc(dev);
+
+ /* Allocate the resources, switch to uart mode. */
+ if (gusmidi_allocres(scp, dev)) {
+ gusmidi_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ gusmidi_init(dev);
+
+ return (0);
+}
+
+static int
+gusmidi_init(device_t dev)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit;
+
+ scp = device_get_softc(dev);
+ unit = device_get_unit(dev);
+
+ /* Fill the softc. */
+ scp->dev = dev;
+ scp->devinfo = devinfo = &midi_info[unit];
+
+ /* Fill the midi info. */
+ bcopy(&gusmidi_op_desc, devinfo, sizeof(gusmidi_op_desc));
+ midiinit(devinfo, dev);
+ devinfo->flags = 0;
+ bcopy(&midisynth_op_desc, &devinfo->synth, sizeof(midisynth_op_desc));
+
+ if (scp->irq != NULL)
+ snprintf(devinfo->midistat, sizeof(devinfo->midistat), "at 0x%x irq %d",
+ (u_int)rman_get_start(scp->io), (int)rman_get_start(scp->irq));
+ else
+ snprintf(devinfo->midistat, sizeof(devinfo->midistat), "at 0x%x",
+ (u_int)rman_get_start(scp->io));
+
+ /* Init the queue. */
+ devinfo->midi_dbuf_in.unit_size = devinfo->midi_dbuf_out.unit_size = 1;
+ midibuf_init(&devinfo->midi_dbuf_in);
+ midibuf_init(&devinfo->midi_dbuf_out);
+
+ bus_setup_intr(dev, scp->irq, INTR_TYPE_TTY, gusmidi_intr, scp,
+ &scp->ih);
+
+ /* Increase the number of midi devices. */
+ nmidi++;
+
+ return (0);
+}
+
+static int
+gusmidi_open(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit;
+
+ unit = MIDIUNIT(i_dev);
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("gusmidi_open: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("gusmidi_open: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+
+ gusmidi_writeport(scp, PORT_CTL, MIDICTL_MASTER_RESET);
+ DELAY(100);
+
+ gusmidi_writeport(scp, PORT_CTL, scp->ctl);
+
+ return (0);
+}
+
+static int
+gusmidi_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit;
+ struct synth_info *synthinfo;
+ struct midi_info *midiinfo;
+
+ unit = MIDIUNIT(i_dev);
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("gusmidi_ioctl: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("gusmidi_ioctl: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+
+ switch (cmd) {
+ case SNDCTL_SYNTH_INFO:
+ synthinfo = (struct synth_info *)arg;
+ if (synthinfo->device > nmidi + nsynth || synthinfo->device != unit)
+ return (ENXIO);
+ bcopy(&gusmidi_synthinfo, synthinfo, sizeof(gusmidi_synthinfo));
+ synthinfo->device = unit;
+ return (0);
+ break;
+ case SNDCTL_MIDI_INFO:
+ midiinfo = (struct midi_info *)arg;
+ if (midiinfo->device > nmidi + nsynth || midiinfo->device != unit)
+ return (ENXIO);
+ bcopy(&gusmidi_midiinfo, midiinfo, sizeof(gusmidi_midiinfo));
+ midiinfo->device = unit;
+ return (0);
+ break;
+ default:
+ return (ENOSYS);
+ }
+ /* NOTREACHED */
+ return (EINVAL);
+}
+
+void
+gusmidi_intr(void *arg)
+{
+ sc_p scp;
+ int s;
+ u_char c;
+ mididev_info *devinfo;
+ int stat, did_something;
+
+ scp = (sc_p)arg;
+ devinfo = scp->devinfo;
+
+ s = splclock();
+
+ /* XXX No framing/overrun checks... */
+ do {
+ stat = gusmidi_readport(scp, PORT_ST);
+ did_something = 0;
+ if (stat & MIDIST_RXFULL) {
+ c = gusmidi_readport(scp, PORT_RX);
+ if ((devinfo->flags & MIDI_F_PASSTHRU) &&
+ (!(devinfo->flags & MIDI_F_BUSY) ||
+ !(devinfo->fflags & FWRITE))) {
+ midibuf_input_intr(&devinfo->midi_dbuf_passthru,
+ &c, sizeof c);
+ devinfo->callback(devinfo,
+ MIDI_CB_START | MIDI_CB_WR);
+ }
+ if ((devinfo->flags & MIDI_F_READING) && c != 0xfe)
+ midibuf_input_intr(&devinfo->midi_dbuf_in,
+ &c, sizeof c);
+ did_something = 1;
+ }
+ if (stat & MIDIST_TXDONE) {
+ if (devinfo->flags & MIDI_F_WRITING) {
+ gusmidi_xmit(scp);
+ did_something = 1;
+ } else if (scp->ctl & MIDICTL_TX_IRQ_EN) {
+ /* This shouldn't happen. */
+ scp->ctl &= ~MIDICTL_TX_IRQ_EN;
+ gusmidi_writeport(scp, PORT_CTL, scp->ctl);
+ }
+ }
+ } while (did_something != 0);
+
+ /* Invoke the upper layer. */
+ midi_intr(devinfo);
+
+ splx(s);
+}
+
+static int
+gusmidi_callback(mididev_info *d, int reason)
+{
+ int unit;
+ sc_p scp;
+
+ if (d == NULL) {
+ DEB(printf("gusmidi_callback: device not configured.\n"));
+ return (ENXIO);
+ }
+
+ unit = d->unit;
+ scp = d->softc;
+
+ switch (reason & MIDI_CB_REASON_MASK) {
+ case MIDI_CB_START:
+ if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) == 0) {
+ /* Begin recording. */
+ d->flags |= MIDI_F_READING;
+ scp->ctl |= MIDICTL_RX_IRQ_EN;
+ }
+ if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) == 0)
+ /* Start playing. */
+ gusmidi_startplay(scp);
+ break;
+ case MIDI_CB_STOP:
+ case MIDI_CB_ABORT:
+ if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) != 0) {
+ /* Stop recording. */
+ d->flags &= ~MIDI_F_READING;
+ scp->ctl &= ~MIDICTL_RX_IRQ_EN;
+ }
+ if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) != 0) {
+ /* Stop Playing. */
+ d->flags &= ~MIDI_F_WRITING;
+ scp->ctl &= ~MIDICTL_TX_IRQ_EN;
+ }
+ break;
+ }
+ gusmidi_writeport(scp, PORT_CTL, scp->ctl);
+
+ return (0);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+/*
+ * Starts to play the data in the output queue.
+ * Call this at >=splclock.
+ */
+static void
+gusmidi_startplay(sc_p scp)
+{
+ mididev_info *devinfo;
+
+ devinfo = scp->devinfo;
+
+ /* Can we play now? */
+ if (devinfo->midi_dbuf_out.rl == 0)
+ return;
+
+ devinfo->flags |= MIDI_F_WRITING;
+ scp->ctl |= MIDICTL_TX_IRQ_EN;
+}
+
+static void
+gusmidi_xmit(sc_p scp)
+{
+ register mididev_info *devinfo;
+ register midi_dbuf *dbuf;
+ u_char c;
+
+ devinfo = scp->devinfo;
+
+ /* See which source to use. */
+ if ((devinfo->flags & MIDI_F_PASSTHRU) == 0 || ((devinfo->flags & MIDI_F_BUSY) != 0 && (devinfo->fflags & FWRITE) != 0))
+ dbuf = &devinfo->midi_dbuf_out;
+ else
+ dbuf = &devinfo->midi_dbuf_passthru;
+
+ /* Transmit the data in the queue. */
+ while ((devinfo->flags & MIDI_F_WRITING) &&
+ (gusmidi_readport(scp, PORT_ST) & MIDIST_TXDONE)) {
+ /* Do we have the data to transmit? */
+ if (dbuf->rl == 0) {
+ /* Stop playing. */
+ devinfo->flags &= ~MIDI_F_WRITING;
+ scp->ctl &= ~MIDICTL_TX_IRQ_EN;
+ gusmidi_writeport(scp, PORT_CTL, scp->ctl);
+ break;
+ } else {
+ /* Send the data. */
+ midibuf_output_intr(dbuf, &c, sizeof(c));
+ gusmidi_writeport(scp, PORT_TX, c);
+ /* We are playing now. */
+ }
+ }
+}
+
+/* Reads from a port. */
+static u_int
+gusmidi_readport(sc_p scp, int off)
+{
+ return bus_space_read_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), off) & 0xff;
+}
+
+/* Writes to a port. */
+static void
+gusmidi_writeport(sc_p scp, int off, u_int8_t value)
+{
+ bus_space_write_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), off, value);
+}
+
+/* Allocates resources. */
+static int
+gusmidi_allocres(sc_p scp, device_t dev)
+{
+ if (scp->io == NULL) {
+ scp->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid, 0, ~0, 2, RF_ACTIVE);
+ if (scp->io == NULL)
+ return (1);
+ }
+#if notdef
+ if (scp->irq == NULL && !(device_get_flags(dev) & MPU_DF_NO_IRQ)) {
+#else
+ if (scp->irq == NULL) {
+#endif /* notdef */
+ scp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &scp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (scp->irq == NULL)
+ return (1);
+ }
+
+ return (0);
+}
+
+/* Releases resources. */
+static void
+gusmidi_releaseres(sc_p scp, device_t dev)
+{
+ if (scp->irq != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, scp->irq_rid, scp->irq);
+ scp->irq = NULL;
+ }
+ if (scp->io != NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, scp->io_rid, scp->io);
+ scp->io = NULL;
+ }
+}
+
+static device_method_t gusmidi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe , gusmidi_probe ),
+ DEVMETHOD(device_attach, gusmidi_attach),
+
+ { 0, 0 },
+};
+
+driver_t gusmidi_driver = {
+ "midi",
+ gusmidi_methods,
+ sizeof(struct gusmidi_softc),
+};
+
+DRIVER_MODULE(gusmidi, gusc, gusmidi_driver, midi_devclass, 0, 0);
diff --git a/sys/dev/sound/isa/mpu.c b/sys/dev/sound/isa/mpu.c
new file mode 100644
index 0000000..7fd89bb
--- /dev/null
+++ b/sys/dev/sound/isa/mpu.c
@@ -0,0 +1,835 @@
+/*
+ * The low level driver for Roland MPU-401 compatible Midi interfaces.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ * Modified: Riccardo Facchetti 24 Mar 1995 - Added the Audio Excel DSP 16
+ * initialization routine.
+ *
+ * Ported to the new Audio Driver by Luigi Rizzo:
+ * (C) 1999 Seigo Tanimura
+ *
+ * This is the MPU401 midi interface driver for FreeBSD, based on the Luigi Sound Driver.
+ * This handles io against /dev/midi, the midi {in, out}put event queues
+ * and the event/message transmittion to/from an MPU401 interface.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "opt_devfs.h"
+
+#include <dev/sound/midi/midi.h>
+#include <dev/sound/chip.h>
+#include <machine/cpufunc.h>
+
+#include <isa/isavar.h>
+#include <isa/sioreg.h>
+#include <isa/ic/ns16550.h>
+
+#define MPU_USEMICROTIMER 0
+
+static devclass_t midi_devclass;
+
+#ifndef DDB
+#undef DDB
+#define DDB(x)
+#endif /* DDB */
+
+#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 25000
+
+/* Device flag. */
+#define MPU_DF_NO_IRQ 1
+
+extern synthdev_info midisynth_op_desc;
+
+/* PnP IDs */
+static struct isa_pnp_id mpu_ids[] = {
+ {0x01200001, "@H@2001 Midi Interface"}, /* @H@2001 */
+ {0x01100001, "@H@1001 Midi Interface"}, /* @H@1001 */
+#if notdef
+ /* TODO: write bridge driver for these devices */
+ {0x0000630e, "CSC0000 Midi Interface"}, /* CSC0000 */
+ {0x2100a865, "YMH0021 Midi Interface"}, /* YMH0021 */
+ {0x80719304, "ADS7180 Midi Interface"}, /* ADS7180 */
+ {0x0300561e, "GRV0003 Midi Interface"}, /* GRV0003 */
+#endif
+};
+
+/* These are the synthesizer and the midi interface information. */
+static struct synth_info mpu_synthinfo = {
+ "MPU401 MIDI",
+ 0,
+ SYNTH_TYPE_MIDI,
+ 0,
+ 0,
+ 128,
+ 128,
+ 128,
+ SYNTH_CAP_INPUT,
+};
+
+static struct midi_info mpu_midiinfo = {
+ "MPU401 MIDI",
+ 0,
+ 0,
+ 0,
+};
+
+/*
+ * These functions goes into mpu_op_desc to get called
+ * from sound.c.
+ */
+
+static int mpu_probe(device_t dev);
+static int mpu_probe1(device_t dev);
+static int mpu_probe2(device_t dev);
+static int mpu_attach(device_t dev);
+static int mpusbc_probe(device_t dev);
+static int mpusbc_attach(device_t dev);
+
+static d_ioctl_t mpu_ioctl;
+static driver_intr_t mpu_intr;
+static midi_callback_t mpu_callback;
+
+/* Here is the parameter structure per a device. */
+struct mpu_softc {
+ device_t dev; /* device information */
+ mididev_info *devinfo; /* midi device information */
+
+ struct resource *io; /* Base of io port */
+ int io_rid; /* Io resource ID */
+ u_long irq_val; /* Irq value */
+ struct resource *irq; /* Irq */
+ int irq_rid; /* Irq resource ID */
+ void *ih; /* Interrupt cookie */
+
+ struct callout_handle dh; /* Callout handler for delay */
+
+ int fflags; /* File flags */
+};
+
+typedef struct mpu_softc *sc_p;
+
+/* These functions are local. */
+static void mpu_startplay(sc_p scp);
+static void mpu_xmit(sc_p scp);
+#if MPU_USEMICROTIMER
+static void mpu_timeout(sc_p scp);
+static timeout_t mpu_timer;
+#endif /* MPU_USEMICROTIMER */
+static int mpu_resetmode(sc_p scp);
+static int mpu_uartmode(sc_p scp);
+static int mpu_waitack(sc_p scp);
+static int mpu_status(sc_p scp);
+static int mpu_command(sc_p scp, u_int8_t value);
+static int mpu_readdata(sc_p scp);
+static int mpu_writedata(sc_p scp, u_int8_t value);
+static u_int mpu_readport(sc_p scp, int off);
+static void mpu_writeport(sc_p scp, int off, u_int8_t value);
+static int mpu_allocres(sc_p scp, device_t dev);
+static void mpu_releaseres(sc_p scp, device_t dev);
+
+/*
+ * This is the device descriptor for the midi device.
+ */
+static mididev_info mpu_op_desc = {
+ "MPU401 midi",
+
+ SNDCARD_MPU401,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ mpu_ioctl,
+ NULL,
+
+ mpu_callback,
+
+ MIDI_BUFFSIZE, /* Queue Length */
+
+ 0, /* XXX This is not an *audio* device! */
+};
+
+/*
+ * Here are the main functions to interact to the user process.
+ */
+
+static int
+mpu_probe(device_t dev)
+{
+ sc_p scp;
+ int ret;
+
+ /* Check isapnp ids */
+ if (isa_get_logicalid(dev) != 0)
+ return (ISA_PNP_PROBE(device_get_parent(dev), dev, mpu_ids));
+
+ scp = device_get_softc(dev);
+
+ device_set_desc(dev, mpu_op_desc.name);
+ bzero(scp, sizeof(*scp));
+
+ scp->io_rid = 0;
+ ret = mpu_probe1(dev);
+ if (ret != 0)
+ return (ret);
+ ret = mpu_probe2(dev);
+ if (ret != 0)
+ return (ret);
+
+ return (0);
+}
+
+/*
+ * Make sure this is an MPU401, not an 16550 uart.
+ * Called only for non-pnp devices.
+ */
+static int
+mpu_probe1(device_t dev)
+{
+ sc_p scp;
+ int iir;
+ struct resource *io;
+
+ scp = device_get_softc(dev);
+
+ /*
+ * If an MPU401 is ready to both input and output,
+ * the status register value is zero, which may
+ * confuse an 16550 uart to probe as an MPU401.
+ * We read the IIR (base + 2), which is not used
+ * by an MPU401.
+ */
+ io = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid, 0, ~0, 3, RF_ACTIVE);
+ iir = bus_space_read_1(rman_get_bustag(io), rman_get_bushandle(io), com_iir) & 0xff;
+ bus_release_resource(dev, SYS_RES_IOPORT, scp->io_rid, io);
+ if ((iir & ~(IIR_IMASK | IIR_FIFO_MASK)) == 0)
+ /* Likely to be an 16550. */
+ return (ENXIO);
+
+ return (0);
+}
+
+/* Look up the irq. */
+static int
+mpu_probe2(device_t dev)
+{
+ sc_p scp;
+ int unit, i;
+ intrmask_t irqp0, irqp1;
+
+ scp = device_get_softc(dev);
+ unit = device_get_unit(dev);
+
+ scp->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid, 0, ~0, 2, RF_ACTIVE);
+ if (scp->io == NULL)
+ return (ENXIO);
+
+ DEB(printf("mpu%d: probing.\n", unit));
+
+ /* Reset the interface. */
+ if (mpu_resetmode(scp) != 0 || mpu_waitack(scp) != 0) {
+ printf("mpu%d: reset failed.\n", unit);
+ mpu_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ /*
+ * At this point, we are likely to have an interface.
+ *
+ * Switching the interface to uart mode gives us an interrupt.
+ * We can make use of it to determine the irq.
+ * Idea-stolen-from: sys/isa/sio.c:sioprobe()
+ */
+
+ disable_intr();
+
+ /*
+ * See the initial irq. We have to do this now,
+ * otherwise a midi module/instrument might send
+ * an active sensing, to mess up the irq.
+ */
+ irqp0 = isa_irq_pending();
+ irqp1 = 0;
+
+ /* Switch to uart mode. */
+ if (mpu_uartmode(scp) != 0) {
+ enable_intr();
+ printf("mpu%d: mode switching failed.\n", unit);
+ mpu_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ if (device_get_flags(dev) & MPU_DF_NO_IRQ) {
+ irqp0 = irqp1 = 0;
+ goto no_irq;
+ }
+
+ /* See which irq we have now. */
+ for (i = 0 ; i < MPU_TRYDATA ; i++) {
+ DELAY(MPU_DELAY);
+ irqp1 = isa_irq_pending();
+ if (irqp1 != irqp0)
+ break;
+ }
+ if (irqp1 == irqp0) {
+ enable_intr();
+ printf("mpu%d: switching the mode gave no interrupt.\n", unit);
+ mpu_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+no_irq:
+ /* Wait to see an ACK. */
+ if (mpu_waitack(scp) != 0) {
+ enable_intr();
+ printf("mpu%d: not acked.\n", unit);
+ mpu_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ enable_intr();
+
+ if (device_get_flags(dev) & MPU_DF_NO_IRQ)
+ scp->irq_val = 0;
+ else
+ /* We have found the irq. */
+ scp->irq_val = ffs(~irqp0 & irqp1) - 1;
+
+ DEB(printf("mpu%d: probed.\n", unit));
+
+ return (0);
+}
+
+static int
+mpusbc_probe(device_t dev)
+{
+ char *s;
+ sc_p scp;
+ struct sndcard_func *func;
+
+ /* The parent device has already been probed. */
+
+ func = device_get_ivars(dev);
+ if (func == NULL || func->func != SCF_MIDI)
+ return (ENXIO);
+
+ s = "SB Midi Interface";
+
+ scp = device_get_softc(dev);
+ bzero(scp, sizeof(*scp));
+ scp->io_rid = 1;
+ scp->irq_rid = 0;
+ device_set_desc(dev, s);
+ return (0);
+}
+
+static int
+mpu_attach(device_t dev)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit;
+
+ scp = device_get_softc(dev);
+ unit = device_get_unit(dev);
+
+ DEB(printf("mpu%d: attaching.\n", unit));
+
+ /* Allocate the resources, switch to uart mode. */
+ if (mpu_allocres(scp, dev) || mpu_uartmode(scp)) {
+ mpu_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ /* mpu_probe() has put the interface to uart mode. */
+
+ /* Fill the softc. */
+ scp->dev = dev;
+ scp->devinfo = devinfo = &midi_info[unit];
+ callout_handle_init(&scp->dh);
+
+ /* Fill the midi info. */
+ bcopy(&mpu_op_desc, devinfo, sizeof(mpu_op_desc));
+ midiinit(devinfo, dev);
+ devinfo->flags = 0;
+ bcopy(&midisynth_op_desc, &devinfo->synth, sizeof(midisynth_op_desc));
+ if (scp->irq != NULL)
+ snprintf(devinfo->midistat, sizeof(devinfo->midistat), "at 0x%x irq %d",
+ (u_int)rman_get_start(scp->io), (int)rman_get_start(scp->irq));
+ else
+ snprintf(devinfo->midistat, sizeof(devinfo->midistat), "at 0x%x",
+ (u_int)rman_get_start(scp->io));
+
+ /* Init the queue. */
+ devinfo->midi_dbuf_in.unit_size = devinfo->midi_dbuf_out.unit_size = 1;
+ midibuf_init(&devinfo->midi_dbuf_in);
+ midibuf_init(&devinfo->midi_dbuf_out);
+
+ /* Increase the number of midi devices. */
+ nmidi++;
+
+ /* Now we can handle the interrupts. */
+ if (scp->irq != NULL)
+ bus_setup_intr(dev, scp->irq, INTR_TYPE_TTY, mpu_intr, scp,
+ &scp->ih);
+
+ DEB(printf("mpu%d: attached.\n", unit));
+
+ return (0);
+}
+
+static int
+mpusbc_attach(device_t dev)
+{
+ sc_p scp;
+ int unit;
+
+ scp = device_get_softc(dev);
+ unit = device_get_unit(dev);
+
+ mpu_attach(dev);
+
+ return (0);
+}
+
+static int
+mpu_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit;
+ struct synth_info *synthinfo;
+ struct midi_info *midiinfo;
+
+ unit = MIDIUNIT(i_dev);
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("mpu_ioctl: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("mpu_ioctl: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+
+ switch (cmd) {
+ case SNDCTL_SYNTH_INFO:
+ synthinfo = (struct synth_info *)arg;
+ if (synthinfo->device > nmidi + nsynth || synthinfo->device != unit)
+ return (ENXIO);
+ bcopy(&mpu_synthinfo, synthinfo, sizeof(mpu_synthinfo));
+ synthinfo->device = unit;
+ return (0);
+ break;
+ case SNDCTL_MIDI_INFO:
+ midiinfo = (struct midi_info *)arg;
+ if (midiinfo->device > nmidi + nsynth || midiinfo->device != unit)
+ return (ENXIO);
+ bcopy(&mpu_midiinfo, midiinfo, sizeof(mpu_midiinfo));
+ midiinfo->device = unit;
+ return (0);
+ break;
+ default:
+ return (ENOSYS);
+ }
+ /* NOTREACHED */
+ return (EINVAL);
+}
+
+static void
+mpu_intr(void *arg)
+{
+ sc_p scp;
+ u_char c;
+ mididev_info *devinfo;
+
+ scp = (sc_p)arg;
+ devinfo = scp->devinfo;
+
+ /* Read the received data. */
+ while ((mpu_status(scp) & MPU_INPUTBUSY) == 0) {
+ /* Receive the data. */
+ c = mpu_readdata(scp);
+ /* Queue into the passthru buffer and start transmitting if we can. */
+ if ((devinfo->flags & MIDI_F_PASSTHRU) != 0 && ((devinfo->flags & MIDI_F_BUSY) == 0 || (devinfo->fflags & FWRITE) == 0)) {
+ midibuf_input_intr(&devinfo->midi_dbuf_passthru, &c, sizeof(c));
+ devinfo->callback(devinfo, MIDI_CB_START | MIDI_CB_WR);
+ }
+ /* Queue if we are reading. Discard an active sensing. */
+ if ((devinfo->flags & MIDI_F_READING) != 0 && c != 0xfe)
+ midibuf_input_intr(&devinfo->midi_dbuf_in, &c, sizeof(c));
+ }
+
+ /* Invoke the upper layer. */
+ midi_intr(devinfo);
+
+}
+
+static int
+mpu_callback(mididev_info *d, int reason)
+{
+ int unit;
+ sc_p scp;
+
+ if (d == NULL) {
+ DEB(printf("mpu_callback: device not configured.\n"));
+ return (ENXIO);
+ }
+
+ unit = d->unit;
+ scp = d->softc;
+
+ switch (reason & MIDI_CB_REASON_MASK) {
+ case MIDI_CB_START:
+ if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) == 0)
+ /* Begin recording. */
+ d->flags |= MIDI_F_READING;
+ if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) == 0)
+ /* Start playing. */
+ mpu_startplay(scp);
+ break;
+ case MIDI_CB_STOP:
+ case MIDI_CB_ABORT:
+ if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) != 0)
+ /* Stop recording. */
+ d->flags &= ~MIDI_F_READING;
+ if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) != 0)
+ /* Stop Playing. */
+ d->flags &= ~MIDI_F_WRITING;
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+/*
+ * Starts to play the data in the output queue.
+ * Call this at >=splclock.
+ */
+static void
+mpu_startplay(sc_p scp)
+{
+ mididev_info *devinfo;
+
+ devinfo = scp->devinfo;
+
+ /* Can we play now? */
+ if (devinfo->midi_dbuf_out.rl == 0)
+ return;
+
+ devinfo->flags |= MIDI_F_WRITING;
+#if MPU_USEMICROTIMER
+ mpu_timeout(scp);
+#else
+ mpu_xmit(scp);
+#endif /* MPU_USEMICROTIMER */
+}
+
+static void
+mpu_xmit(sc_p scp)
+{
+ register mididev_info *devinfo;
+ register midi_dbuf *dbuf;
+ u_char c;
+
+ devinfo = scp->devinfo;
+
+ /* See which source to use. */
+ if ((devinfo->flags & MIDI_F_PASSTHRU) == 0 || ((devinfo->flags & MIDI_F_BUSY) != 0 && (devinfo->fflags & FWRITE) != 0))
+ dbuf = &devinfo->midi_dbuf_out;
+ else
+ dbuf = &devinfo->midi_dbuf_passthru;
+
+ /* Transmit the data in the queue. */
+#if MPU_USEMICROTIMER
+ while ((devinfo->flags & MIDI_F_WRITING) != 0 && (mpu_status(scp) & MPU_OUTPUTBUSY) == 0) {
+ /* Do we have the data to transmit? */
+ if (dbuf->rl == 0) {
+ /* Stop playing. */
+ devinfo->flags &= ~MIDI_F_WRITING;
+ break;
+ } else {
+ /* Send the data. */
+ midibuf_output_intr(dbuf, &c, sizeof(c));
+ mpu_writedata(scp, c);
+ /* We are playing now. */
+ devinfo->flags |= MIDI_F_WRITING;
+ }
+ }
+
+ /* De we have still more? */
+ if ((devinfo->flags & MIDI_F_WRITING) != 0)
+ /* Handle them on the next interrupt. */
+ mpu_timeout(scp);
+#else
+ while ((devinfo->flags & MIDI_F_WRITING) != 0 && dbuf->rl > 0) {
+ /* XXX Wait until we can write the data. */
+ while ((mpu_status(scp) & MPU_OUTPUTBUSY) != 0);
+ /* Send the data. */
+ midibuf_output_intr(dbuf, &c, sizeof(c));
+ mpu_writedata(scp, c);
+ /* We are playing now. */
+ devinfo->flags |= MIDI_F_WRITING;
+ }
+ /* Stop playing. */
+ devinfo->flags &= ~MIDI_F_WRITING;
+#endif /* MPU_USEMICROTIMER */
+}
+
+#if MPU_USEMICROTIMER
+/* Arm a timer. */
+static void
+mpu_timeout(sc_p scp)
+{
+ microtimeout(mpu_timer, scp, hz * hzmul / 3125);
+}
+
+/* Called when a timer has beeped. */
+static void
+mpu_timer(void *arg)
+{
+ sc_p scp;
+
+ scp = arg;
+ mpu_xmit(scp);
+}
+#endif /* MPU_USEMICROTIMER */
+
+/* Reset mpu. */
+static int
+mpu_resetmode(sc_p scp)
+{
+ int i, resp;
+
+ /* Reset the mpu. */
+ resp = 0;
+ for (i = 0 ; i < MPU_TRYDATA ; i++) {
+ resp = mpu_command(scp, MPU_RESET);
+ if (resp == 0)
+ break;
+ }
+ if (resp != 0)
+ return (1);
+
+ DELAY(MPU_DELAY);
+ return (0);
+}
+
+/* Switch to uart mode. */
+static int
+mpu_uartmode(sc_p scp)
+{
+ int i, resp;
+
+ /* Switch to uart mode. */
+ resp = 0;
+ for (i = 0 ; i < MPU_TRYDATA ; i++) {
+ resp = mpu_command(scp, MPU_UART);
+ if (resp == 0)
+ break;
+ }
+ if (resp != 0)
+ return (1);
+
+ DELAY(MPU_DELAY);
+ return (0);
+}
+
+/* Wait to see an ACK. */
+static int
+mpu_waitack(sc_p scp)
+{
+ int i, resp;
+
+ resp = 0;
+ for (i = 0 ; i < MPU_TRYDATA ; i++) {
+ resp = mpu_readdata(scp);
+ if (resp >= 0)
+ break;
+ }
+ if (resp != MPU_ACK)
+ return (1);
+
+ DELAY(MPU_DELAY);
+ return (0);
+}
+
+/* Reads the status. */
+static int
+mpu_status(sc_p scp)
+{
+ return mpu_readport(scp, MPU_STATPORT);
+}
+
+/* Writes a command. */
+static int
+mpu_command(sc_p scp, u_int8_t value)
+{
+ u_int status;
+
+ /* Is the interface ready to write? */
+ status = mpu_status(scp);
+ if ((status & MPU_OUTPUTBUSY) != 0)
+ /* The interface is busy. */
+ return (EAGAIN);
+
+ mpu_writeport(scp, MPU_CMDPORT, value);
+
+ return (0);
+}
+
+/* Reads a byte of data. */
+static int
+mpu_readdata(sc_p scp)
+{
+ u_int status;
+
+ /* Is the interface ready to write? */
+ status = mpu_status(scp);
+ if ((status & MPU_INPUTBUSY) != 0)
+ /* The interface is busy. */
+ return (-EAGAIN);
+
+ return (int)mpu_readport(scp, MPU_DATAPORT) & 0xff;
+}
+
+/* Writes a byte of data. */
+static int
+mpu_writedata(sc_p scp, u_int8_t value)
+{
+ u_int status;
+
+ /* Is the interface ready to write? */
+ status = mpu_status(scp);
+ if ((status & MPU_OUTPUTBUSY) != 0)
+ /* The interface is busy. */
+ return (EAGAIN);
+
+ mpu_writeport(scp, MPU_DATAPORT, value);
+
+ return (0);
+}
+
+/* Reads from a port. */
+static u_int
+mpu_readport(sc_p scp, int off)
+{
+ return bus_space_read_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), off) & 0xff;
+}
+
+/* Writes to a port. */
+static void
+mpu_writeport(sc_p scp, int off, u_int8_t value)
+{
+ bus_space_write_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), off, value);
+}
+
+/* Allocates resources. */
+static int
+mpu_allocres(sc_p scp, device_t dev)
+{
+ if (scp->io == NULL) {
+ scp->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid, 0, ~0, 2, RF_ACTIVE);
+ if (scp->io == NULL)
+ return (1);
+ }
+ if (scp->irq == NULL && !(device_get_flags(dev) & MPU_DF_NO_IRQ)) {
+ if (scp->irq_val == 0)
+ scp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &scp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ else
+ scp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &scp->irq_rid, scp->irq_val, scp->irq_val, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (scp->irq == NULL)
+ return (1);
+ }
+
+ return (0);
+}
+
+/* Releases resources. */
+static void
+mpu_releaseres(sc_p scp, device_t dev)
+{
+ if (scp->irq != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, scp->irq_rid, scp->irq);
+ scp->irq = NULL;
+ }
+ if (scp->io != NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, scp->io_rid, scp->io);
+ scp->io = NULL;
+ }
+}
+
+static device_method_t mpu_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe , mpu_probe ),
+ DEVMETHOD(device_attach, mpu_attach),
+
+ { 0, 0 },
+};
+
+static driver_t mpu_driver = {
+ "midi",
+ mpu_methods,
+ sizeof(struct mpu_softc),
+};
+
+DRIVER_MODULE(mpu, isa, mpu_driver, midi_devclass, 0, 0);
+
+static device_method_t mpusbc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe , mpusbc_probe ),
+ DEVMETHOD(device_attach, mpusbc_attach),
+
+ { 0, 0 },
+};
+
+static driver_t mpusbc_driver = {
+ "midi",
+ mpusbc_methods,
+ sizeof(struct mpu_softc),
+};
+
+DRIVER_MODULE(mpusbc, sbc, mpusbc_driver, midi_devclass, 0, 0);
diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c
index 374dc31..4e1967f 100644
--- a/sys/dev/sound/isa/mss.c
+++ b/sys/dev/sound/isa/mss.c
@@ -34,10 +34,6 @@
#include <dev/sound/isa/mss.h>
#include <dev/sound/chip.h>
-#if notyet
-#include "midi.h"
-#endif /* notyet */
-
#define MSS_BUFFSIZE (65536 - 256)
#define abs(x) (((x) < 0) ? -(x) : (x))
@@ -352,12 +348,8 @@ gusmax_setup(struct mss_info *mss, device_t dev, struct resource *alt)
port_wr(alt, 0x0f, 0x00);
irqctl = irq_bits[isa_get_irq(parent)];
-#if notyet
-#if NMIDI > 0
/* Share the IRQ with the MIDI driver. */
irqctl |= 0x40;
-#endif /* NMIDI > 0 */
-#endif /* notyet */
dmactl = dma_bits[isa_get_drq(parent)];
if (device_get_flags(parent) & DV_F_DUAL_DMA)
dmactl |= dma_bits[device_get_flags(parent) & DV_F_DRQ_MASK]
@@ -495,7 +487,7 @@ mss_probe(device_t dev)
int flags, irq, drq, result = ENXIO, setres = 0;
struct mss_info *mss;
- if (isa_get_vendorid(dev)) return ENXIO; /* not yet */
+ if (isa_get_logicalid(dev)) return ENXIO; /* not yet */
mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT);
if (!mss) return ENXIO;
@@ -1556,7 +1548,7 @@ guspcm_attach(device_t dev)
mss->drq1_rid = 1;
mss->drq2_rid = -1;
- if (isa_get_vendorid(parent) == 0)
+ if (isa_get_logicalid(parent) == 0)
mss->bd_id = MD_GUSMAX;
else {
mss->bd_id = MD_GUSPNP;
diff --git a/sys/dev/sound/isa/opl.c b/sys/dev/sound/isa/opl.c
new file mode 100644
index 0000000..3d2c9d6
--- /dev/null
+++ b/sys/dev/sound/isa/opl.c
@@ -0,0 +1,1904 @@
+/*
+ * A low level driver for Yamaha YM3812 and OPL-3 -chips
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Major improvements to the FM handling 30AUG92 by Rob Hooft,
+ */
+/*
+ * hooft@chem.ruu.nl
+ */
+/*
+ *
+ * Ported to the new Audio Driver by Luigi Rizzo:
+ * (C) 1999 Seigo Tanimura
+ *
+ * This is the OPL2/3/4 chip driver for FreeBSD, based on the Luigi Sound Driver.
+ * This handles io against /dev/midi, the midi {in, out}put event queues
+ * and the event/message operation to the OPL chip.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "opt_devfs.h"
+
+#include <dev/sound/midi/midi.h>
+#include <dev/sound/chip.h>
+
+#include <isa/isavar.h>
+
+static devclass_t midi_devclass;
+
+#ifndef DDB
+#undef DDB
+#define DDB(x)
+#endif /* DDB */
+
+/*
+ * The OPL-3 mode is switched on by writing 0x01, to the offset 5
+ * of the right side.
+ *
+ * Another special register at the right side is at offset 4. It contains
+ * a bit mask defining which voices are used as 4 OP voices.
+ *
+ * The percussive mode is implemented in the left side only.
+ *
+ * With the above exeptions the both sides can be operated independently.
+ *
+ * A 4 OP voice can be created by setting the corresponding
+ * bit at offset 4 of the right side.
+ *
+ * For example setting the rightmost bit (0x01) changes the
+ * first voice on the right side to the 4 OP mode. The fourth
+ * voice is made inaccessible.
+ *
+ * If a voice is set to the 2 OP mode, it works like 2 OP modes
+ * of the original YM3812 (AdLib). In addition the voice can
+ * be connected the left, right or both stereo channels. It can
+ * even be left unconnected. This works with 4 OP voices also.
+ *
+ * The stereo connection bits are located in the FEEDBACK_CONNECTION
+ * register of the voice (0xC0-0xC8). In 4 OP voices these bits are
+ * in the second half of the voice.
+ */
+
+/*
+ * Register numbers for the global registers
+ */
+
+#define TEST_REGISTER 0x01
+#define ENABLE_WAVE_SELECT 0x20
+
+#define TIMER1_REGISTER 0x02
+#define TIMER2_REGISTER 0x03
+#define TIMER_CONTROL_REGISTER 0x04 /* Left side */
+#define IRQ_RESET 0x80
+#define TIMER1_MASK 0x40
+#define TIMER2_MASK 0x20
+#define TIMER1_START 0x01
+#define TIMER2_START 0x02
+
+#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */
+#define RIGHT_4OP_0 0x01
+#define RIGHT_4OP_1 0x02
+#define RIGHT_4OP_2 0x04
+#define LEFT_4OP_0 0x08
+#define LEFT_4OP_1 0x10
+#define LEFT_4OP_2 0x20
+
+#define OPL3_MODE_REGISTER 0x05 /* Right side */
+#define OPL3_ENABLE 0x01
+#define OPL4_ENABLE 0x02
+
+#define KBD_SPLIT_REGISTER 0x08 /* Left side */
+#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */
+#define KEYBOARD_SPLIT 0x40
+
+#define PERCUSSION_REGISTER 0xbd /* Left side only */
+#define TREMOLO_DEPTH 0x80
+#define VIBRATO_DEPTH 0x40
+#define PERCUSSION_ENABLE 0x20
+#define BASSDRUM_ON 0x10
+#define SNAREDRUM_ON 0x08
+#define TOMTOM_ON 0x04
+#define CYMBAL_ON 0x02
+#define HIHAT_ON 0x01
+
+/*
+ * Offsets to the register banks for operators. To get the
+ * register number just add the operator offset to the bank offset
+ *
+ * AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
+ */
+#define AM_VIB 0x20
+#define TREMOLO_ON 0x80
+#define VIBRATO_ON 0x40
+#define SUSTAIN_ON 0x20
+#define KSR 0x10 /* Key scaling rate */
+#define MULTIPLE_MASK 0x0f /* Frequency multiplier */
+
+/*
+ * KSL/Total level (0x40 to 0x55)
+ */
+#define KSL_LEVEL 0x40
+#define KSL_MASK 0xc0 /* Envelope scaling bits */
+#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */
+
+/*
+ * Attack / Decay rate (0x60 to 0x75)
+ */
+#define ATTACK_DECAY 0x60
+#define ATTACK_MASK 0xf0
+#define DECAY_MASK 0x0f
+
+/*
+ * Sustain level / Release rate (0x80 to 0x95)
+ */
+#define SUSTAIN_RELEASE 0x80
+#define SUSTAIN_MASK 0xf0
+#define RELEASE_MASK 0x0f
+
+/*
+ * Wave select (0xE0 to 0xF5)
+ */
+#define WAVE_SELECT 0xe0
+
+/*
+ * Offsets to the register banks for voices. Just add to the
+ * voice number to get the register number.
+ *
+ * F-Number low bits (0xA0 to 0xA8).
+ */
+#define FNUM_LOW 0xa0
+
+/*
+ * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
+ */
+#define KEYON_BLOCK 0xb0
+#define KEYON_BIT 0x20
+#define BLOCKNUM_MASK 0x1c
+#define FNUM_HIGH_MASK 0x03
+
+/*
+ * Feedback / Connection (0xc0 to 0xc8)
+ *
+ * These registers have two new bits when the OPL-3 mode
+ * is selected. These bits controls connecting the voice
+ * to the stereo channels. For 4 OP voices this bit is
+ * defined in the second half of the voice (add 3 to the
+ * register offset).
+ *
+ * For 4 OP voices the connection bit is used in the
+ * both halfs (gives 4 ways to connect the operators).
+ */
+#define FEEDBACK_CONNECTION 0xc0
+#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */
+#define CONNECTION_BIT 0x01
+/*
+ * In the 4 OP mode there is four possible configurations how the
+ * operators can be connected together (in 2 OP modes there is just
+ * AM or FM). The 4 OP connection mode is defined by the rightmost
+ * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halfs.
+ *
+ * First half Second half Mode
+ *
+ * +---+
+ * v |
+ * 0 0 >+-1-+--2--3--4-->
+ *
+ *
+ *
+ * +---+
+ * | |
+ * 0 1 >+-1-+--2-+
+ * |->
+ * >--3----4-+
+ *
+ * +---+
+ * | |
+ * 1 0 >+-1-+-----+
+ * |->
+ * >--2--3--4-+
+ *
+ * +---+
+ * | |
+ * 1 1 >+-1-+--+
+ * |
+ * >--2--3-+->
+ * |
+ * >--4----+
+ */
+#define STEREO_BITS 0x30 /* OPL-3 only */
+#define VOICE_TO_LEFT 0x10
+#define VOICE_TO_RIGHT 0x20
+
+/*
+ * Definition table for the physical voices
+ */
+
+struct physical_voice_info {
+ unsigned char voice_num;
+ unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */
+ int ch; /* channel (left=USE_LEFT, right=USE_RIGHT) */
+ unsigned char op[4]; /* Operator offsets */
+};
+
+/*
+ * There is 18 possible 2 OP voices
+ * (9 in the left and 9 in the right).
+ * The first OP is the modulator and 2nd is the carrier.
+ *
+ * The first three voices in the both sides may be connected
+ * with another voice to a 4 OP voice. For example voice 0
+ * can be connected with voice 3. The operators of voice 3 are
+ * used as operators 3 and 4 of the new 4 OP voice.
+ * In this case the 2 OP voice number 0 is the 'first half' and
+ * voice 3 is the second.
+ */
+
+#define USE_LEFT 0
+#define USE_RIGHT 1
+
+static struct physical_voice_info pv_map[18] =
+{
+/* No Mode Side OP1 OP2 OP3 OP4 */
+/* --------------------------------------------------- */
+ { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}},
+ { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}},
+ { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}},
+
+ { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}},
+ { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}},
+ { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}},
+
+ { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */
+ { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */
+ { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */
+
+ { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}},
+ { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}},
+ { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}},
+
+ { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}},
+ { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}},
+ { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}},
+
+ { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}},
+ { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}},
+ { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}}
+};
+
+/* These are the tuning parameters. */
+static unsigned short semitone_tuning[24] =
+{
+/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983,
+/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784,
+/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755
+};
+
+static unsigned short cent_tuning[100] =
+{
+/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041,
+/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087,
+/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134,
+/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181,
+/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228,
+/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275,
+/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323,
+/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371,
+/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419,
+/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467,
+/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515,
+/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564,
+/* 96 */ 10570, 10576, 10582, 10589
+};
+
+/*
+ * The next table looks magical, but it certainly is not. Its values have
+ * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
+ * for i=0. This log-table converts a linear volume-scaling (0..127) to a
+ * logarithmic scaling as present in the FM-synthesizer chips. so : Volume
+ * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative
+ * volume -8 it was implemented as a table because it is only 128 bytes and
+ * it saves a lot of log() calculations. (RH)
+ */
+static char opl_volumetable[128] =
+{
+ -64, -48, -40, -35, -32, -29, -27, -26,
+ -24, -23, -21, -20, -19, -18, -18, -17,
+ -16, -15, -15, -14, -13, -13, -12, -12,
+ -11, -11, -10, -10, -10, -9, -9, -8,
+ -8, -8, -7, -7, -7, -6, -6, -6,
+ -5, -5, -5, -5, -4, -4, -4, -4,
+ -3, -3, -3, -3, -2, -2, -2, -2,
+ -2, -1, -1, -1, -1, 0, 0, 0,
+ 0, 0, 0, 1, 1, 1, 1, 1,
+ 1, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 4,
+ 4, 4, 4, 4, 4, 4, 4, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 8, 8, 8, 8, 8};
+
+#define MAX_VOICE 18
+#define OFFS_4OP 11
+#define SBFM_MAXINSTR 256
+
+/* These are the OPL Models. */
+#define MODEL_NONE 0
+#define MODEL_OPL2 2
+#define MODEL_OPL3 3
+#define MODEL_OPL4 4
+
+/* These are the OPL Voice modes. */
+#define VOICE_NONE 0
+#define VOICE_2OP 2
+#define VOICE_4OP 4
+
+/* PnP IDs */
+static struct isa_pnp_id opl_ids[] = {
+ {0x01200001, "@H@2001 FM Synthesizer"}, /* @H@2001 */
+ {0x01100001, "@H@1001 FM Synthesizer"}, /* @H@1001 */
+#if notdef
+ /* TODO: write bridge drivers for these devices. */
+ {0x0000630e, "CSC0000 FM Synthesizer"}, /* CSC0000 */
+ {0x68187316, "ESS1868 FM Synthesizer"}, /* ESS1868 */
+ {0x79187316, "ESS1879 FM Synthesizer"}, /* ESS1879 */
+ {0x2100a865, "YMH0021 FM Synthesizer"}, /* YMH0021 */
+ {0x80719304, "ADS7180 FM Synthesizer"}, /* ADS7180 */
+ {0x0300561e, "GRV0003 FM Synthesizer"}, /* GRV0003 */
+#endif /* notdef */
+};
+
+/* These are the default io bases. */
+static int opl_defaultiobase[] = {
+ 0x388,
+ 0x380,
+};
+
+/* These are the per-voice information. */
+struct voice_info {
+ u_char keyon_byte;
+ long bender;
+ long bender_range;
+ u_long orig_freq;
+ u_long current_freq;
+ int volume;
+ int mode;
+};
+
+/* These are the synthesizer and the midi device information. */
+static struct synth_info opl_synthinfo = {
+ "OPL FM Synthesizer",
+ 0,
+ SYNTH_TYPE_FM,
+ FM_TYPE_ADLIB,
+ 0,
+ 9,
+ 0,
+ SBFM_MAXINSTR,
+ 0,
+};
+
+static struct midi_info opl_midiinfo = {
+ "OPL FM Synthesizer",
+ 0,
+ 0,
+ 0,
+};
+
+/*
+ * These functions goes into oplsynthdev_op_desc.
+ */
+static mdsy_killnote_t opl_killnote;
+static mdsy_setinstr_t opl_setinstr;
+static mdsy_startnote_t opl_startnote;
+static mdsy_reset_t opl_reset;
+static mdsy_hwcontrol_t opl_hwcontrol;
+static mdsy_loadpatch_t opl_loadpatch;
+static mdsy_panning_t opl_panning;
+static mdsy_aftertouch_t opl_aftertouch;
+static mdsy_controller_t opl_controller;
+static mdsy_patchmgr_t opl_patchmgr;
+static mdsy_bender_t opl_bender;
+static mdsy_allocvoice_t opl_allocvoice;
+static mdsy_setupvoice_t opl_setupvoice;
+static mdsy_sendsysex_t opl_sendsysex;
+static mdsy_prefixcmd_t opl_prefixcmd;
+static mdsy_volumemethod_t opl_volumemethod;
+
+/*
+ * This is the synthdev_info for an OPL3 chip.
+ */
+static synthdev_info oplsynth_op_desc = {
+ opl_killnote,
+ opl_setinstr,
+ opl_startnote,
+ opl_reset,
+ opl_hwcontrol,
+ opl_loadpatch,
+ opl_panning,
+ opl_aftertouch,
+ opl_controller,
+ opl_patchmgr,
+ opl_bender,
+ opl_allocvoice,
+ opl_setupvoice,
+ opl_sendsysex,
+ opl_prefixcmd,
+ opl_volumemethod,
+};
+
+/* Here is the parameter structure per a device. */
+struct opl_softc {
+ device_t dev; /* device information */
+ mididev_info *devinfo; /* midi device information */
+
+ struct resource *io; /* Base of io port */
+ int io_rid; /* Io resource ID */
+
+ int model; /* OPL model */
+ struct synth_info synthinfo; /* Synthesizer information */
+
+ struct sbi_instrument i_map[SBFM_MAXINSTR]; /* Instrument map */
+ struct sbi_instrument *act_i[SBFM_MAXINSTR]; /* Active instruments */
+ struct physical_voice_info pv_map[MAX_VOICE]; /* Physical voice map */
+ int cmask; /* Connection mask */
+ int lv_map[MAX_VOICE]; /* Level map */
+ struct voice_info voc[MAX_VOICE]; /* Voice information */
+};
+
+typedef struct opl_softc *sc_p;
+
+/*
+ * These functions goes into opl_op_desc to get called
+ * from sound.c.
+ */
+
+static int opl_probe(device_t dev);
+static int opl_probe1(sc_p scp);
+static int opl_attach(device_t dev);
+static int oplsbc_probe(device_t dev);
+static int oplsbc_attach(device_t dev);
+
+static d_open_t opl_open;
+static d_close_t opl_close;
+static d_read_t opl_read;
+static d_write_t opl_write;
+static d_ioctl_t opl_ioctl;
+static midi_callback_t opl_callback;
+
+/* These go to snddev_info. */
+static mdsy_readraw_t opl_readraw;
+static mdsy_writeraw_t opl_writeraw;
+
+/* These functions are local. */
+static void opl_startplay(sc_p scp);
+static void opl_command(sc_p scp, int ch, int addr, u_int val);
+static int opl_status(sc_p scp);
+static void opl_enter4opmode(sc_p scp);
+static void opl_storeinstr(sc_p scp, int instr_no, struct sbi_instrument *instr);
+static void opl_calcvol(u_char *regbyte, int volume, int main_vol);
+static void opl_setvoicevolume(sc_p scp, int voice, int volume, int main_vol);
+static void opl_freqtofnum(int freq, int *block, int *fnum);
+static int opl_bendpitch(sc_p scp, int voice, int val);
+static int opl_notetofreq(int note_num);
+static u_long opl_computefinetune(u_long base_freq, int bend, int range);
+static int opl_allocres(sc_p scp, device_t dev);
+static void opl_releaseres(sc_p scp, device_t dev);
+
+/*
+ * This is the device descriptor for the midi device.
+ */
+static mididev_info opl_op_desc = {
+ "OPL FM Synthesizer",
+
+ SNDCARD_OPL,
+
+ opl_open,
+ opl_close,
+ opl_read,
+ opl_write,
+ opl_ioctl,
+ NULL,
+
+ opl_callback,
+
+ MIDI_BUFFSIZE, /* Queue Length */
+
+ 0, /* XXX This is not an *audio* device! */
+};
+
+/*
+ * Here are the main functions to interact to the user process.
+ */
+
+static int
+opl_probe(device_t dev)
+{
+ sc_p scp;
+ int unit, i;
+
+ /* Check isapnp ids */
+ if (isa_get_logicalid(dev) != 0)
+ return (ISA_PNP_PROBE(device_get_parent(dev), dev, opl_ids));
+
+ scp = device_get_softc(dev);
+ unit = device_get_unit(dev);
+
+ device_set_desc(dev, opl_op_desc.name);
+ bzero(scp, sizeof(*scp));
+
+ DEB(printf("opl%d: probing.\n", unit));
+
+ scp->io_rid = 0;
+ scp->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid, 0, ~0, 4, RF_ACTIVE);
+ if (opl_allocres(scp, dev)) {
+ /* We try the defaults in opl_defaultiobase. */
+ DEB(printf("opl%d: port is omitted, trying the defaults.\n", unit));
+ for (i = 0 ; i < sizeof(opl_defaultiobase) / sizeof(*opl_defaultiobase) ; i++) {
+ scp->io_rid = 0;
+ scp->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid, opl_defaultiobase[i], opl_defaultiobase[i] + 1, 4, RF_ACTIVE);
+ if (scp->io != NULL) {
+ if (opl_probe1(scp))
+ opl_releaseres(scp, dev);
+ else
+ break;
+ }
+ }
+ if (scp->io == NULL)
+ return (ENXIO);
+ } else if(opl_probe1(scp)) {
+ opl_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ /* We now have some kind of OPL. */
+
+ DEB(printf("opl%d: probed.\n", unit));
+
+ return (0);
+}
+
+/* We do probe in this function. */
+static int
+opl_probe1(sc_p scp)
+{
+ u_char stat1, stat2;
+
+ /* Reset the timers and the interrupt. */
+ opl_command(scp, USE_LEFT, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
+ opl_command(scp, USE_LEFT, TIMER_CONTROL_REGISTER, IRQ_RESET);
+
+ /* Read the status. */
+ stat1 = opl_status(scp);
+ if ((stat1 & 0xe0) != 0)
+ return (1);
+
+ /* Try firing the timer1. */
+ opl_command(scp, USE_LEFT, TIMER1_REGISTER, 0xff); /* Set the timer value. */
+ opl_command(scp, USE_LEFT, TIMER_CONTROL_REGISTER, TIMER1_START | TIMER2_MASK); /* Start the timer. */
+ DELAY(150); /* Wait for the timer. */
+
+ /* Read the status. */
+ stat2 = opl_status(scp);
+
+ /* Reset the timers and the interrupt. */
+ opl_command(scp, USE_LEFT, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
+ opl_command(scp, USE_LEFT, TIMER_CONTROL_REGISTER, IRQ_RESET);
+
+ if ((stat2 & 0xe0) != 0xc0)
+ return (1);
+
+ return (0);
+}
+
+static int
+oplsbc_probe(device_t dev)
+{
+ char *s;
+ sc_p scp;
+ struct sndcard_func *func;
+
+ /* The parent device has already been probed. */
+
+ func = device_get_ivars(dev);
+ if (func == NULL || func->func != SCF_SYNTH)
+ return (ENXIO);
+
+ s = "SB OPL FM Synthesizer";
+
+ scp = device_get_softc(dev);
+ bzero(scp, sizeof(*scp));
+ scp->io_rid = 2;
+ device_set_desc(dev, s);
+ return (0);
+}
+
+static int
+opl_attach(device_t dev)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit, i, opl4_io, opl4_id;
+ struct resource *opl4;
+ u_char signature, tmp;
+
+ scp = device_get_softc(dev);
+ unit = device_get_unit(dev);
+
+ DEB(printf("opl%d: attaching.\n", unit));
+
+ /* Fill the softc for this unit. */
+ scp->dev = dev;
+ scp->devinfo = devinfo = &midi_info[unit];
+
+ /* Allocate other resources. */
+ if (opl_allocres(scp, dev)) {
+ opl_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ /* Detect the OPL type. */
+ signature = opl_status(scp);
+ if (signature == 0x06)
+ scp->model = MODEL_OPL2;
+ else {
+ /* OPL3 or later, might be OPL4. */
+
+ /* Enable OPL3 and OPL4. */
+ opl_command(scp, USE_RIGHT, OPL3_MODE_REGISTER, 0);
+ opl_command(scp, USE_RIGHT, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE);
+
+ tmp = opl_status(scp);
+ if (tmp != 0x02)
+ scp->model = MODEL_OPL3;
+#if notdef
+ else {
+#endif /* notdef */
+ /* Alloc OPL4 ID register. */
+ opl4_id = 2;
+ opl4_io = rman_get_start(scp->io) - 8;
+ opl4 = bus_alloc_resource(dev, SYS_RES_IOPORT, &opl4_id, opl4_io, opl4_io + 1, 2, RF_ACTIVE);
+ if (opl4 != NULL) {
+ /* Select OPL4 ID register. */
+ bus_space_write_1(rman_get_bustag(opl4), rman_get_bushandle(opl4), 0, 0x02);
+ DELAY(10);
+ tmp = bus_space_read_1(rman_get_bustag(opl4), rman_get_bushandle(opl4), 1);
+ DELAY(10);
+
+ if (tmp != 0x20)
+ scp->model = MODEL_OPL3;
+ else {
+ scp->model = MODEL_OPL4;
+
+ /* Select back OPL4 FM mixer control. */
+ bus_space_write_1(rman_get_bustag(opl4), rman_get_bushandle(opl4), 0, 0xf8);
+ DELAY(10);
+ bus_space_write_1(rman_get_bustag(opl4), rman_get_bushandle(opl4), 1, 0x1b);
+ DELAY(10);
+ }
+ bus_release_resource(dev, SYS_RES_IOPORT, opl4_id, opl4);
+ }
+#if notdef
+ }
+#endif /* notdef */
+ opl_command(scp, USE_RIGHT, OPL3_MODE_REGISTER, 0);
+ }
+
+ /* Kill any previous notes. */
+ for (i = 0 ; i < 9 ; i++)
+ opl_command(scp, USE_RIGHT, KEYON_BLOCK + i, 0);
+
+ /* Select melodic mode. */
+ opl_command(scp, USE_LEFT, TEST_REGISTER, ENABLE_WAVE_SELECT);
+ opl_command(scp, USE_LEFT, PERCUSSION_REGISTER, 0);
+
+ for (i = 0 ; i < SBFM_MAXINSTR ; i++)
+ scp->i_map[i].channel = -1;
+
+ /* Fill the softc. */
+ bcopy(&opl_synthinfo, &scp->synthinfo, sizeof(opl_synthinfo));
+ snprintf(scp->synthinfo.name, 64, "Yamaha OPL%d FM", scp->model);
+ bcopy(pv_map, scp->pv_map, sizeof(pv_map));
+ if (scp->model < MODEL_OPL3) { /* OPL2. */
+ scp->synthinfo.nr_voices = 9;
+ scp->synthinfo.nr_drums = 0;
+ for (i = 0 ; i < MAX_VOICE ; i++)
+ scp->pv_map[i].ch = USE_LEFT;
+ } else { /* OPL3 or later. */
+ scp->synthinfo.capabilities |= SYNTH_CAP_OPL3;
+ scp->synthinfo.nr_voices = 18;
+ scp->synthinfo.nr_drums = 0;
+#if notdef
+ for (i = 0 ; i < MAX_VOICE ; i++) {
+ if (scp->pv_map[i].ch == USE_LEFT)
+ scp->pv_map[i].ch = USE_LEFT;
+ else
+ scp->pv_map[i].ch = USE_RIGHT;
+ }
+#endif /* notdef */
+ opl_command(scp, USE_RIGHT, OPL3_MODE_REGISTER, OPL3_ENABLE);
+ opl_command(scp, USE_RIGHT, CONNECTION_SELECT_REGISTER, 0);
+ }
+
+ /* Fill the midi info. */
+ bcopy(&opl_op_desc, devinfo, sizeof(opl_op_desc));
+ midiinit(devinfo, dev);
+ devinfo->flags = 0;
+ bcopy(&oplsynth_op_desc, &devinfo->synth, sizeof(oplsynth_op_desc));
+ devinfo->synth.readraw = opl_readraw;
+ devinfo->synth.writeraw = opl_writeraw;
+ devinfo->synth.alloc.max_voice = scp->synthinfo.nr_voices;
+ strcpy(devinfo->name, scp->synthinfo.name);
+ snprintf(devinfo->midistat, sizeof(devinfo->midistat), "at 0x%x", (u_int)rman_get_start(scp->io));
+
+ /* Init the queue. */
+ devinfo->midi_dbuf_in.unit_size = devinfo->midi_dbuf_out.unit_size = 1;
+ midibuf_init(&devinfo->midi_dbuf_in);
+ midibuf_init(&devinfo->midi_dbuf_out);
+
+ /* Increase the number of the synthesizers. */
+ nsynth++;
+
+ DEB(printf("opl%d: attached.\n", unit));
+ DEB(printf("opl%d: the chip is OPL%d.\n", unit, scp->model));
+
+ return (0);
+}
+
+static int
+oplsbc_attach(device_t dev)
+{
+ return (opl_attach(dev));
+}
+
+static int
+opl_open(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit, i;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("opl%d: opening.\n", unit));
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("opl_open: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("opl_open: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+
+ if (scp->model < MODEL_OPL3)
+ devinfo->synth.alloc.max_voice = 9;
+ else
+ devinfo->synth.alloc.max_voice = 18;
+ devinfo->synth.alloc.timestamp = 0;
+ for (i = 0 ; i < MAX_VOICE ; i++) {
+ devinfo->synth.alloc.map[i] = 0;
+ devinfo->synth.alloc.alloc_times[i] = 0;
+ }
+ scp->cmask = 0; /* We are in 2 OP mode initially. */
+ if (scp->model >= MODEL_OPL3)
+ opl_command(scp, USE_RIGHT, CONNECTION_SELECT_REGISTER, scp->cmask);
+
+ DEB(printf("opl%d: opened.\n", unit));
+
+ return (0);
+}
+
+static int
+opl_close(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("opl%d: closing.\n", unit));
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("opl_close: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("opl_close: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+
+ if (scp->model < MODEL_OPL3)
+ devinfo->synth.alloc.max_voice = 9;
+ else
+ devinfo->synth.alloc.max_voice = 18;
+
+ /* Stop the OPL. */
+ opl_reset(scp->devinfo);
+
+ DEB(printf("opl%d: closed.\n", unit));
+
+ return (0);
+}
+
+static int
+opl_read(dev_t i_dev, struct uio *buf, int flag)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit, ret;
+
+ unit = MIDIUNIT(i_dev);
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("opl_read: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("opl_read: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+ if ((devinfo->fflags & FREAD) == 0) {
+ DEB(printf("opl_read: unit %d is not for reading.\n", unit));
+ return (EIO);
+ }
+
+ /* Drain the data. */
+ midibuf_init(&devinfo->midi_dbuf_in);
+ ret = 0;
+
+ return (ret);
+}
+
+static int
+opl_write(dev_t i_dev, struct uio *buf, int flag)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit, ret;
+
+ unit = MIDIUNIT(i_dev);
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("opl_write: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("opl_write: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+ if ((devinfo->fflags & FWRITE) == 0) {
+ DEB(printf("opl_write: unit %d is not for writing.\n", unit));
+ return (EIO);
+ }
+
+ /* Drain the data. */
+ midibuf_init(&devinfo->midi_dbuf_out);
+ midibuf_init(&devinfo->midi_dbuf_passthru);
+ ret = 0;
+
+ return (ret);
+}
+
+static int
+opl_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit;
+ struct synth_info *synthinfo;
+ struct midi_info *midiinfo;
+ struct sbi_instrument ins;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("opl%d: ioctlling, cmd 0x%x.\n", unit, (int)cmd));
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("opl_ioctl: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("opl_ioctl: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+
+ switch (cmd) {
+ case SNDCTL_SYNTH_INFO:
+ synthinfo = (struct synth_info *)arg;
+ if (synthinfo->device >= nmidi + nsynth || synthinfo->device != unit)
+ return (ENXIO);
+ bcopy(&scp->synthinfo, synthinfo, sizeof(scp->synthinfo));
+ synthinfo->device = unit;
+ if (devinfo->synth.alloc.max_voice == 12)
+ synthinfo->nr_voices = 6;
+ else
+ synthinfo->nr_voices = devinfo->synth.alloc.max_voice;
+ return (0);
+ break;
+ case SNDCTL_MIDI_INFO:
+ midiinfo = (struct midi_info *)arg;
+ if (midiinfo->device >= nmidi + nsynth || midiinfo->device != unit)
+ return (ENXIO);
+ bcopy(&opl_midiinfo, midiinfo, sizeof(opl_midiinfo));
+ strcpy(midiinfo->name, scp->synthinfo.name);
+ midiinfo->device = unit;
+ return (0);
+ break;
+ case SNDCTL_FM_LOAD_INSTR:
+ bcopy(arg, &ins, sizeof(ins));
+ if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) {
+ printf("opl_ioctl: Instrument number %d is not valid.\n", ins.channel);
+ return (EINVAL);
+ }
+#if notyet
+ pmgr_inform(scp, PM_E_PATCH_LOADED, inc.channel, 0, 0, 0);
+#endif /* notyet */
+ opl_storeinstr(scp, ins.channel, &ins);
+ return (0);
+ break;
+ case SNDCTL_SYNTH_MEMAVL:
+ return 0x7fffffff;
+ break;
+ case SNDCTL_FM_4OP_ENABLE:
+ if (scp->model >= MODEL_OPL3)
+ opl_enter4opmode(scp);
+ return (0);
+ break;
+ default:
+ return (ENOSYS);
+ }
+ /* NOTREACHED */
+ return (EINVAL);
+}
+
+static int
+opl_callback(mididev_info *devinfo, int reason)
+{
+ int unit;
+ sc_p scp;
+
+ if (devinfo == NULL) {
+ DEB(printf("opl_callback: device not configured.\n"));
+ return (ENXIO);
+ }
+
+ unit = devinfo->unit;
+ scp = devinfo->softc;
+
+ DEB(printf("opl%d: callback, reason 0x%x.\n", unit, reason));
+
+ switch (reason & MIDI_CB_REASON_MASK) {
+ case MIDI_CB_START:
+ if ((reason & MIDI_CB_RD) != 0 && (devinfo->flags & MIDI_F_READING) == 0)
+ /* Begin recording. */
+ devinfo->flags |= MIDI_F_READING;
+ if ((reason & MIDI_CB_WR) != 0 && (devinfo->flags & MIDI_F_WRITING) == 0)
+ /* Start playing. */
+ devinfo->flags |= MIDI_F_WRITING;
+ break;
+ case MIDI_CB_STOP:
+ case MIDI_CB_ABORT:
+ if ((reason & MIDI_CB_RD) != 0 && (devinfo->flags & MIDI_F_READING) != 0)
+ /* Stop recording. */
+ devinfo->flags &= ~MIDI_F_READING;
+ if ((reason & MIDI_CB_WR) != 0 && (devinfo->flags & MIDI_F_WRITING) != 0)
+ /* Stop Playing. */
+ devinfo->flags &= ~MIDI_F_WRITING;
+ break;
+ }
+
+ return (0);
+}
+
+static int
+opl_readraw(mididev_info *md, u_char *buf, int len, int nonblock)
+{
+ sc_p scp;
+ int unit;
+
+ if (md == NULL)
+ return (ENXIO);
+ unit = md->unit;
+ scp = md->softc;
+ if ((md->fflags & FREAD) == 0) {
+ DEB(printf("opl_readraw: unit %d is not for reading.\n", unit));
+ return (EIO);
+ }
+
+ /* NOP. */
+ return (0);
+}
+
+static int
+opl_writeraw(mididev_info *md, u_char *buf, int len, int nonblock)
+{
+ sc_p scp;
+ int unit;
+
+ if (md == NULL)
+ return (ENXIO);
+ unit = md->unit;
+ scp = md->softc;
+ if ((md->fflags & FWRITE) == 0) {
+ DEB(printf("opl_writeraw: unit %d is not for writing.\n", unit));
+ return (EIO);
+ }
+
+ /* NOP. */
+ return (0);
+}
+
+/* The functions below here are the synthesizer interfaces. */
+
+static int
+opl_killnote(mididev_info *md, int voice, int note, int vel)
+{
+ int unit;
+ sc_p scp;
+ struct physical_voice_info *map;
+
+ scp = md->softc;
+ unit = md->unit;
+
+ DEB(printf("opl%d: killing a note, voice %d, note %d, vel %d.\n", unit, voice, note, vel));
+
+ if (voice < 0 || voice >= md->synth.alloc.max_voice)
+ return (0);
+
+ md->synth.alloc.map[voice] = 0;
+ map = &scp->pv_map[scp->lv_map[voice]];
+
+ if (map->voice_mode == VOICE_NONE)
+ return (0);
+
+ opl_command(scp, map->ch, KEYON_BLOCK + map->voice_num, scp->voc[voice].keyon_byte & ~0x20);
+
+ scp->voc[voice].keyon_byte = 0;
+ scp->voc[voice].bender = 0;
+ scp->voc[voice].volume = 64;
+ scp->voc[voice].bender_range = 200;
+ scp->voc[voice].orig_freq = 0;
+ scp->voc[voice].current_freq = 0;
+ scp->voc[voice].mode = 0;
+
+ return (0);
+}
+
+static int
+opl_setinstr(mididev_info *md, int voice, int instr_no)
+{
+ int unit;
+ sc_p scp;
+
+ scp = md->softc;
+ unit = md->unit;
+
+ DEB(printf("opl%d: setting an instrument, voice %d, instr_no %d.\n", unit, voice, instr_no));
+
+
+ if (voice < 0 || voice >= md->synth.alloc.max_voice || instr_no < 0 || instr_no >= SBFM_MAXINSTR)
+ return (0);
+
+ scp->act_i[voice] = &scp->i_map[instr_no];
+
+ return (0);
+}
+
+static int
+opl_startnote(mididev_info *md, int voice, int note, int volume)
+{
+ u_char fpc;
+ int unit, block, fnum, freq, voice_mode, voice_shift;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
+ sc_p scp;
+
+ scp = md->softc;
+ unit = md->unit;
+
+ DEB(printf("opl%d: starting a note, voice %d, note %d, volume %d.\n", unit, voice, note, volume));
+
+ if (voice < 0 || voice >= md->synth.alloc.max_voice)
+ return (0);
+
+ map = &scp->pv_map[scp->lv_map[voice]];
+ if (map->voice_mode == VOICE_NONE)
+ return (0);
+
+ if (note == 255) {
+ /* Change the volume. */
+ opl_setvoicevolume(scp, voice, volume, scp->voc[voice].volume);
+ return (0);
+ }
+
+ /* Kill the previous note. */
+ opl_command(scp, map->ch, KSL_LEVEL + map->op[1], 0xff); /* Carrier volume */
+ opl_command(scp, map->ch, KSL_LEVEL + map->op[0], 0xff); /* Modulator volume */
+ if (map->voice_mode == VOICE_4OP) {
+ opl_command(scp, map->ch, KSL_LEVEL + map->op[3], 0xff); /* Carrier volume */
+ opl_command(scp, map->ch, KSL_LEVEL + map->op[2], 0xff); /* Modulator volume */
+ }
+ opl_command(scp, map->ch, KEYON_BLOCK + map->voice_num, 0); /* Note off. */
+
+ instr = scp->act_i[voice];
+ if (instr == NULL)
+ instr = &scp->i_map[0];
+ if (instr->channel < 0) {
+ printf("opl_startnote: the instrument for voice %d is undefined.\n", voice);
+ return (0);
+ }
+ if (map->voice_mode == VOICE_2OP && instr->key == OPL3_PATCH) {
+ printf("opl_startnote: the voice mode %d mismatches the key 0x%x.\n", map->voice_mode, instr->key);
+ return (0);
+ }
+
+ voice_mode = map->voice_mode;
+ if (voice_mode == VOICE_4OP) {
+ if (map->ch == USE_LEFT)
+ voice_shift = 0;
+ else
+ voice_shift = 3;
+ voice_shift += map->voice_num;
+ if (instr->key != OPL3_PATCH) {
+ voice_mode = VOICE_2OP;
+ scp->cmask &= ~(1 << voice_shift);
+ } else
+ scp->cmask |= 1 << voice_shift;
+
+ opl_command(scp, USE_RIGHT, CONNECTION_SELECT_REGISTER, scp->cmask);
+ }
+
+ /* Set the sound characteristics, attack, decay, sustain, release, wave select, feedback, connection. */
+ opl_command(scp, map->ch, AM_VIB + map->op[0], instr->operators[0]); /* Sound characteristics. */
+ opl_command(scp, map->ch, AM_VIB + map->op[1], instr->operators[1]);
+ opl_command(scp, map->ch, ATTACK_DECAY + map->op[0], instr->operators[4]); /* Attack and decay. */
+ opl_command(scp, map->ch, ATTACK_DECAY + map->op[1], instr->operators[5]);
+ opl_command(scp, map->ch, SUSTAIN_RELEASE + map->op[0], instr->operators[6]); /* Sustain and release. */
+ opl_command(scp, map->ch, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);
+ opl_command(scp, map->ch, WAVE_SELECT + map->op[0], instr->operators[8]); /* Wave select. */
+ opl_command(scp, map->ch, WAVE_SELECT + map->op[1], instr->operators[9]);
+ fpc = instr->operators[10];
+ if ((fpc & 0x30) == 0)
+ fpc |= 0x30; /* So that at least one channel is enabled. */
+ opl_command(scp, map->ch, FEEDBACK_CONNECTION + map->voice_num, fpc); /* Feedback and connection. */
+
+ if (voice_mode == VOICE_4OP) {
+ /* Do not forget the operators 3 and 4. */
+ opl_command(scp, map->ch, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]); /* Sound characteristics. */
+ opl_command(scp, map->ch, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);
+ opl_command(scp, map->ch, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]); /* Attack and decay. */
+ opl_command(scp, map->ch, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);
+ opl_command(scp, map->ch, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]); /* Sustain and release. */
+ opl_command(scp, map->ch, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);
+ opl_command(scp, map->ch, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]); /* Wave select. */
+ opl_command(scp, map->ch, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);
+ fpc = instr->operators[OFFS_4OP + 10];
+ if ((fpc & 0x30) == 0)
+ fpc |= 0x30; /* So that at least one channel is enabled. */
+ opl_command(scp, map->ch, FEEDBACK_CONNECTION + map->voice_num + 3, fpc); /* Feedback and connection. */
+ }
+ scp->voc[voice].mode = voice_mode;
+
+ opl_setvoicevolume(scp, voice, volume, scp->voc[voice].volume);
+
+ /* Calcurate the frequency. */
+ scp->voc[voice].orig_freq = opl_notetofreq(note) / 1000;
+ /* Tune for the pitch bend. */
+ freq = scp->voc[voice].current_freq = opl_computefinetune(scp->voc[voice].orig_freq, scp->voc[voice].bender, scp->voc[voice].bender_range);
+ opl_freqtofnum(freq, &block, &fnum);
+
+ /* Now we can play the note. */
+ opl_command(scp, map->ch, FNUM_LOW + map->voice_num, fnum & 0xff);
+ scp->voc[voice].keyon_byte = 0x20 | ((block & 0x07) << 2) | ((fnum >> 8) & 0x03);
+ opl_command(scp, map->ch, KEYON_BLOCK + map->voice_num, scp->voc[voice].keyon_byte);
+ if (voice_mode == VOICE_4OP)
+ opl_command(scp, map->ch, KEYON_BLOCK + map->voice_num + 3, scp->voc[voice].keyon_byte);
+
+ return (0);
+}
+
+static int
+opl_reset(mididev_info *md)
+{
+ int unit, i;
+ sc_p scp;
+
+ scp = md->softc;
+ unit = md->unit;
+
+ DEB(printf("opl%d: resetting.\n", unit));
+
+ for (i = 0 ; i < MAX_VOICE ; i++)
+ scp->lv_map[i] = i;
+
+ for (i = 0 ; i < md->synth.alloc.max_voice ; i++) {
+ opl_command(scp, scp->pv_map[scp->lv_map[i]].ch, KSL_LEVEL + scp->pv_map[scp->lv_map[i]].op[0], 0xff);
+ opl_command(scp, scp->pv_map[scp->lv_map[i]].ch, KSL_LEVEL + scp->pv_map[scp->lv_map[i]].op[1], 0xff);
+ if (scp->pv_map[scp->lv_map[i]].voice_mode == VOICE_4OP) {
+ opl_command(scp, scp->pv_map[scp->lv_map[i]].ch, KSL_LEVEL + scp->pv_map[scp->lv_map[i]].op[2], 0xff);
+ opl_command(scp, scp->pv_map[scp->lv_map[i]].ch, KSL_LEVEL + scp->pv_map[scp->lv_map[i]].op[3], 0xff);
+ }
+ opl_killnote(md, i, 0, 64);
+ }
+ if (scp->model >= MODEL_OPL3) {
+ md->synth.alloc.max_voice = 18;
+ for (i = 0 ; i < MAX_VOICE ; i++)
+ scp->pv_map[i].voice_mode = VOICE_2OP;
+ }
+
+ return (0);
+}
+
+static int
+opl_hwcontrol(mididev_info *md, u_char *event)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+opl_loadpatch(mididev_info *md, int format, struct uio *buf, int offs, int count, int pmgr_flag)
+{
+ int unit;
+ struct sbi_instrument ins;
+ sc_p scp;
+
+ scp = md->softc;
+ unit = md->unit;
+
+ if (count < sizeof(ins)) {
+ printf("opl_loadpatch: The patch record is too short.\n");
+ return (EINVAL);
+ }
+ if (uiomove(&((char *)&ins)[offs], sizeof(ins) - offs, buf) != 0)
+ printf("opl_loadpatch: User memory mangled?\n");
+ if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) {
+ printf("opl_loadpatch: Instrument number %d is not valid.\n", ins.channel);
+ return (EINVAL);
+ }
+ ins.key = format;
+
+ opl_storeinstr(scp, ins.channel, &ins);
+ return (0);
+}
+
+static int
+opl_panning(mididev_info *md, int chn, int pan)
+{
+ /* NOP. */
+ return (0);
+}
+
+#define SET_VIBRATO(cell) { \
+ int tmp; \
+ tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
+ if (press > 110) \
+ tmp |= 0x40; /* Vibrato on */ \
+ opl_command(scp, map->ch, AM_VIB + map->op[cell-1], tmp);}
+
+static int
+opl_aftertouch(mididev_info *md, int voice, int press)
+{
+ int unit, connection;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
+ sc_p scp;
+
+ scp = md->softc;
+ unit = md->unit;
+
+ DEB(printf("opl%d: setting the aftertouch, voice %d, press %d.\n", unit, voice, press));
+
+ if (voice < 0 || voice >= md->synth.alloc.max_voice)
+ return (0);
+
+ map = &scp->pv_map[scp->lv_map[voice]];
+ if (map->voice_mode == VOICE_NONE)
+ return (0);
+
+ /* Adjust the vibrato. */
+ instr = scp->act_i[voice];
+ if (instr == NULL)
+ instr = &scp->i_map[0];
+
+ if (scp->voc[voice].mode == VOICE_4OP) {
+ connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+ switch (connection) {
+ case 0:
+ SET_VIBRATO(4);
+ break;
+ case 1:
+ SET_VIBRATO(2);
+ SET_VIBRATO(4);
+ break;
+ case 2:
+ SET_VIBRATO(1);
+ SET_VIBRATO(4);
+ break;
+ case 3:
+ SET_VIBRATO(1);
+ SET_VIBRATO(3);
+ SET_VIBRATO(4);
+ break;
+ }
+ } else {
+ SET_VIBRATO(1);
+ if ((instr->operators[10] & 0x01))
+ SET_VIBRATO(2);
+ }
+
+ return (0);
+}
+
+static int
+opl_bendpitch(sc_p scp, int voice, int value)
+{
+ int unit, block, fnum, freq;
+ struct physical_voice_info *map;
+ mididev_info *md;
+
+ md = scp->devinfo;
+ unit = md->unit;
+
+ DEB(printf("opl%d: setting the pitch bend, voice %d, value %d.\n", unit, voice, value));
+
+ map = &scp->pv_map[scp->lv_map[voice]];
+ if (map->voice_mode == 0)
+ return (0);
+ scp->voc[voice].bender = value;
+ if (value == 0)
+ return (0);
+ if ((scp->voc[voice].keyon_byte & 0x20) == 0)
+ return (0);
+
+ freq = opl_computefinetune(scp->voc[voice].orig_freq, scp->voc[voice].bender, scp->voc[voice].bender_range);
+ scp->voc[voice].current_freq = freq;
+
+ opl_freqtofnum(freq, &block, &fnum);
+
+ opl_command(scp, map->ch, FNUM_LOW + map->voice_num, fnum & 0xff);
+ scp->voc[voice].keyon_byte = 0x20 | ((block & 0x07) << 2) | ((fnum >> 8) & 0x03);
+ opl_command(scp, map->ch, KEYON_BLOCK + map->voice_num, scp->voc[voice].keyon_byte);
+ if (map->voice_mode == VOICE_4OP)
+ opl_command(scp, map->ch, KEYON_BLOCK + map->voice_num + 3, scp->voc[voice].keyon_byte);
+
+ return (0);
+}
+
+static int
+opl_controller(mididev_info *md, int voice, int ctrlnum, int val)
+{
+ int unit;
+ sc_p scp;
+
+ scp = md->softc;
+ unit = md->unit;
+
+ DEB(printf("opl%d: setting the controller, voice %d, ctrlnum %d, val %d.\n", unit, voice, ctrlnum, val));
+
+ if (voice < 0 || voice >= md->synth.alloc.max_voice)
+ return (0);
+
+ switch (ctrlnum) {
+ case CTRL_PITCH_BENDER:
+ opl_bendpitch(scp, voice, val);
+ break;
+ case CTRL_PITCH_BENDER_RANGE:
+ scp->voc[voice].bender_range = val;
+ break;
+ case CTRL_MAIN_VOLUME:
+ scp->voc[voice].volume = val / 128;
+ break;
+ }
+
+ return (0);
+}
+
+static int
+opl_patchmgr(mididev_info *md, struct patmgr_info *rec)
+{
+ return (EINVAL);
+}
+
+static int
+opl_bender(mididev_info *md, int voice, int val)
+{
+ sc_p scp;
+
+ scp = md->softc;
+
+ if (voice < 0 || voice >= md->synth.alloc.max_voice)
+ return (0);
+
+ return opl_bendpitch(scp, voice, val - 8192);
+}
+
+static int
+opl_allocvoice(mididev_info *md, int chn, int note, struct voice_alloc_info *alloc)
+{
+ int i, p, best, first, avail, best_time, is4op, instr_no;
+ struct sbi_instrument *instr;
+ sc_p scp;
+
+ scp = md->softc;
+
+ DEB(printf("opl%d: allocating a voice, chn %d, note %d.\n", unit, chn, note));
+
+ best_time = 0x7fffffff;
+
+ if (chn < 0 || chn >= 15)
+ instr_no = 0;
+ else
+ instr_no = md->synth.chn_info[chn].pgm_num;
+
+ instr = &scp->i_map[instr_no];
+ if (instr->channel < 0 || md->synth.alloc.max_voice != 12)
+ is4op = 0;
+ else if (md->synth.alloc.max_voice == 12) {
+ if (instr->key == OPL3_PATCH)
+ is4op = 1;
+ else
+ is4op = 0;
+ } else
+ is4op = 0;
+
+ if (is4op) {
+ first = p = 0;
+ avail = 6;
+ } else {
+ if (md->synth.alloc.max_voice == 12)
+ first = p = 6;
+ else
+ first = p = 0;
+ avail = md->synth.alloc.max_voice;
+ }
+
+ /* Look up a free voice. */
+ best = first;
+
+ for (i = 0 ; i < avail ; i++) {
+ if (alloc->map[p] == 0)
+ return (p);
+ }
+ if (alloc->alloc_times[p] < best_time) {
+ best_time = alloc->alloc_times[p];
+ best = p;
+ }
+ p = (p + 1) % avail;
+
+ if (best < 0)
+ best = 0;
+ else if (best > md->synth.alloc.max_voice)
+ best -= md->synth.alloc.max_voice;
+
+ return best;
+}
+
+static int
+opl_setupvoice(mididev_info *md, int voice, int chn)
+{
+ struct channel_info *info;
+ sc_p scp;
+
+ scp = md->softc;
+
+ DEB(printf("opl%d: setting up a voice, voice %d, chn %d.\n", unit, voice, chn));
+
+ info = &md->synth.chn_info[chn];
+
+ opl_setinstr(md, voice, info->pgm_num);
+ scp->voc[voice].bender = info->bender_value;
+ scp->voc[voice].volume = info->controllers[CTL_MAIN_VOLUME];
+
+ return (0);
+}
+
+static int
+opl_sendsysex(mididev_info *md, u_char *sysex, int len)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+opl_prefixcmd(mididev_info *md, int status)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+opl_volumemethod(mididev_info *md, int mode)
+{
+ /* NOP. */
+ return (0);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+/*
+ * Starts to play the data in the output queue.
+ * Call this at >=splmidi.
+ */
+static void
+opl_startplay(sc_p scp)
+{
+ mididev_info *devinfo;
+
+ devinfo = scp->devinfo;
+
+ /* Can we play now? */
+ if (devinfo->midi_dbuf_out.rl == 0)
+ return;
+
+ /* Begin playing. */
+ devinfo->callback(devinfo, MIDI_CB_START | MIDI_CB_WR);
+}
+
+/* Writes a command to the OPL chip. */
+static void
+opl_command(sc_p scp, int ch, int addr, u_int val)
+{
+ int model;
+
+ DEB(printf("opl%d: sending a command, iobase 0x%x, addr 0x%x, val 0x%x.\n", unit, iobase, addr, val));
+
+ model = scp->model;
+
+ /* Write the addr first. */
+ bus_space_write_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), ch * 2, (u_char)(addr & 0xff));
+ if (model < MODEL_OPL3)
+ DELAY(10);
+ else {
+ bus_space_read_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), ch * 2);
+ bus_space_read_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), ch * 2);
+ }
+
+ /* Next write the value. */
+ bus_space_write_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), ch * 2 + 1, (u_char)(val & 0xff));
+ if (model < MODEL_OPL3)
+ DELAY(30);
+ else {
+ bus_space_read_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), ch * 2);
+ bus_space_read_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), ch * 2);
+ }
+}
+
+/* Reads the status of the OPL chip. */
+static int
+opl_status(sc_p scp)
+{
+ DEB(printf("opl%d: reading the status.\n", unit));
+
+ return bus_space_read_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), 0);
+}
+
+static void
+opl_enter4opmode(sc_p scp)
+{
+ int i;
+ mididev_info *devinfo;
+ static int v4op[MAX_VOICE] = {
+ 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17,
+ };
+
+ devinfo = scp->devinfo;
+
+ DEB(printf("opl%d: entering 4 OP mode.\n", unit));
+
+ /* Connect all possible 4 OP voice operators. */
+ scp->cmask = 0x3f;
+ opl_command(scp, USE_RIGHT, CONNECTION_SELECT_REGISTER, scp->cmask);
+
+ for (i = 0 ; i < 3 ; i++)
+ scp->pv_map[i].voice_mode = VOICE_4OP;
+ for (i = 3 ; i < 6 ; i++)
+ scp->pv_map[i].voice_mode = VOICE_NONE;
+ for (i = 9 ; i < 12 ; i++)
+ scp->pv_map[i].voice_mode = VOICE_4OP;
+ for (i = 12 ; i < 15 ; i++)
+ scp->pv_map[i].voice_mode = VOICE_NONE;
+
+ for (i = 0 ; i < 12 ; i++)
+ scp->lv_map[i] = v4op[i];
+ devinfo->synth.alloc.max_voice = 12;
+}
+
+static void
+opl_storeinstr(sc_p scp, int instr_no, struct sbi_instrument *instr)
+{
+ if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || scp->model < MODEL_OPL3))
+ printf("opl_storeinstr: The patch format field 0x%x is not valid.\n", instr->key);
+
+ bcopy(instr, &scp->i_map[instr_no], sizeof(*instr));
+}
+
+static void
+opl_calcvol(u_char *regbyte, int volume, int main_vol)
+{
+ int level;
+
+ level = (~*regbyte & 0x3f);
+
+ if (main_vol > 127)
+ main_vol = 127;
+
+ volume = (volume * main_vol) / 127;
+
+ if (level > 0)
+ level += opl_volumetable[volume];
+
+ RANGE(level, 0, 0x3f);
+
+ *regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
+}
+
+static void
+opl_setvoicevolume(sc_p scp, int voice, int volume, int main_vol)
+{
+ u_char vol1, vol2, vol3, vol4;
+ int connection;
+ struct sbi_instrument *instr;
+ struct physical_voice_info *map;
+ mididev_info *devinfo;
+
+ devinfo = scp->devinfo;
+
+ if (voice < 0 || voice >= devinfo->synth.alloc.max_voice)
+ return;
+
+ map = &scp->pv_map[scp->lv_map[voice]];
+ instr = scp->act_i[voice];
+ if (instr == NULL)
+ instr = &scp->i_map[0];
+
+ if (instr->channel < 0)
+ return;
+ if (scp->voc[voice].mode == VOICE_NONE)
+ return;
+ if (scp->voc[voice].mode == VOICE_2OP) { /* 2 OP mode. */
+ vol1 = instr->operators[2];
+ vol2 = instr->operators[3];
+ if ((instr->operators[10] & 0x01))
+ opl_calcvol(&vol1, volume, main_vol);
+ opl_calcvol(&vol2, volume, main_vol);
+ opl_command(scp, map->ch, KSL_LEVEL + map->op[0], vol1);
+ opl_command(scp, map->ch, KSL_LEVEL + map->op[1], vol2);
+ } else { /* 4 OP mode. */
+ vol1 = instr->operators[2];
+ vol2 = instr->operators[3];
+ vol3 = instr->operators[OFFS_4OP + 2];
+ vol4 = instr->operators[OFFS_4OP + 3];
+ connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
+ switch(connection) {
+ case 0:
+ opl_calcvol(&vol4, volume, main_vol);
+ break;
+ case 1:
+ opl_calcvol(&vol2, volume, main_vol);
+ opl_calcvol(&vol4, volume, main_vol);
+ break;
+ case 2:
+ opl_calcvol(&vol1, volume, main_vol);
+ opl_calcvol(&vol4, volume, main_vol);
+ break;
+ case 3:
+ opl_calcvol(&vol1, volume, main_vol);
+ opl_calcvol(&vol3, volume, main_vol);
+ opl_calcvol(&vol4, volume, main_vol);
+ break;
+ }
+ opl_command(scp, map->ch, KSL_LEVEL + map->op[0], vol1);
+ opl_command(scp, map->ch, KSL_LEVEL + map->op[1], vol2);
+ opl_command(scp, map->ch, KSL_LEVEL + map->op[2], vol3);
+ opl_command(scp, map->ch, KSL_LEVEL + map->op[3], vol4);
+ }
+}
+
+static void
+opl_freqtofnum(int freq, int *block, int *fnum)
+{
+ int f, octave;
+
+ f = freq;
+ octave = 5;
+
+ if (f == 0)
+ octave = 0;
+ else if (f < 261) {
+ while (f < 261) {
+ octave--;
+ f <<= 1;
+ }
+ } else if (f > 493) {
+ while (f > 493) {
+ octave++;
+ f >>= 1;
+ }
+ }
+ if (octave > 7)
+ octave = 7;
+
+ *fnum = freq * (1 << (20 - octave)) / 49716;
+ *block = octave;
+}
+
+static int notes[] =
+{
+ 261632,
+ 277189,
+ 293671,
+ 311132,
+ 329632,
+ 349232,
+ 369998,
+ 391998,
+ 415306,
+ 440000,
+ 466162,
+ 493880
+};
+
+#define BASE_OCTAVE 5
+
+static int
+opl_notetofreq(int note_num)
+{
+ int note, octave, note_freq;
+
+ octave = note_num / 12;
+ note = note_num % 12;
+
+ note_freq = notes[note];
+
+ if (octave < BASE_OCTAVE)
+ note_freq >>= (BASE_OCTAVE - octave);
+ else if (octave > BASE_OCTAVE)
+ note_freq <<= (octave - BASE_OCTAVE);
+
+ return (note_freq);
+}
+
+static u_long
+opl_computefinetune(u_long base_freq, int bend, int range)
+{
+ u_long amount;
+ int negative, semitones, cents, multiplier;
+
+ if (bend == 0 || range == 0 || base_freq == 0)
+ return (base_freq);
+
+ multiplier = 1;
+
+ if (range > 8192)
+ range = 8192;
+
+ bend = bend * range / 8192;
+ if (bend == 0)
+ return (base_freq);
+
+ if (bend < 0) {
+ negative = 1;
+ bend = -bend;
+ }
+ else
+ negative = 0;
+ if (bend > range)
+ bend = range;
+
+ while (bend > 2399) {
+ multiplier *= 4;
+ bend -= 2400;
+ }
+
+ semitones = bend / 100;
+ cents = bend % 100;
+
+ amount = (u_long)(semitone_tuning[semitones] * multiplier * cent_tuning[cents]) / 10000;
+
+ if (negative)
+ return (base_freq * 10000) / amount;
+ else
+ return (base_freq * amount) / 10000;
+}
+
+/* Allocates resources other than IO ports. */
+static int
+opl_allocres(sc_p scp, device_t dev)
+{
+ if (scp->io == NULL) {
+ scp->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid, 0, ~0, 4, RF_ACTIVE);
+ if (scp->io == NULL)
+ return (1);
+ }
+
+ return (0);
+}
+
+/* Releases resources. */
+static void
+opl_releaseres(sc_p scp, device_t dev)
+{
+ if (scp->io != NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, scp->io_rid, scp->io);
+ scp->io = NULL;
+ }
+}
+
+static device_method_t opl_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe , opl_probe ),
+ DEVMETHOD(device_attach, opl_attach),
+
+ { 0, 0 },
+};
+
+static driver_t opl_driver = {
+ "midi",
+ opl_methods,
+ sizeof(struct opl_softc),
+};
+
+DRIVER_MODULE(opl, isa, opl_driver, midi_devclass, 0, 0);
+
+static device_method_t oplsbc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe , oplsbc_probe ),
+ DEVMETHOD(device_attach, oplsbc_attach),
+
+ { 0, 0 },
+};
+
+static driver_t oplsbc_driver = {
+ "midi",
+ oplsbc_methods,
+ sizeof(struct opl_softc),
+};
+
+DRIVER_MODULE(oplsbc, sbc, oplsbc_driver, midi_devclass, 0, 0);
diff --git a/sys/dev/sound/isa/sbc.c b/sys/dev/sound/isa/sbc.c
index efec890..0c2933b 100644
--- a/sys/dev/sound/isa/sbc.c
+++ b/sys/dev/sound/isa/sbc.c
@@ -400,7 +400,6 @@ sbc_attach(device_t dev)
child = device_add_child(dev, "pcm", -1);
device_set_ivars(child, func);
-#if notyet
/* Midi Interface */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL) goto bad;
@@ -416,7 +415,6 @@ sbc_attach(device_t dev)
func->func = SCF_SYNTH;
child = device_add_child(dev, "midi", -1);
device_set_ivars(child, func);
-#endif /* notyet */
/* probe/attach kids */
bus_generic_attach(dev);
diff --git a/sys/dev/sound/isa/uartsio.c b/sys/dev/sound/isa/uartsio.c
new file mode 100644
index 0000000..9b43331
--- /dev/null
+++ b/sys/dev/sound/isa/uartsio.c
@@ -0,0 +1,524 @@
+/*
+ * Copyright by George Hansper 1996
+ *
+ * Tue Jan 23 22:32:10 EST 1996 ghansper@daemon.apana.org.au
+ * added 16450/16550 support for standard serial-port UARTs
+ *
+ * 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.
+ *
+ *
+ * Wed Apl 1 02:25:30 JST 1998 zinnia@jan.ne.jp
+ * ported to FreeBSD 2.2.5R-RELEASE
+ *
+ * Fri Apl 1 21:16:20 JST 1999 zinnia@jan.ne.jp
+ * ported to FreeBSD 3.1-STABLE
+ *
+ *
+ * Ported to the new Audio Driver by Luigi Rizzo:
+ * (C) 1999 Seigo Tanimura
+ *
+ * This is the 16550 midi uart driver for FreeBSD, based on the Luigi Sound Driver.
+ * This handles io against /dev/midi, the midi {in, out}put event queues
+ * and the event/message transmittion to/from a serial port interface.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "opt_devfs.h"
+
+#include <isa/sioreg.h>
+#include <isa/ic/ns16550.h>
+#include <dev/sound/midi/midi.h>
+
+/* XXX What about a PCI uart? */
+#include <isa/isavar.h>
+
+static devclass_t midi_devclass;
+
+#ifndef DDB
+#undef DDB
+#define DDB(x)
+#endif /* DDB */
+
+#define TX_FIFO_SIZE 16
+
+extern synthdev_info midisynth_op_desc;
+
+/* These are the synthesizer and the midi interface information. */
+static struct synth_info uartsio_synthinfo = {
+ "uart16550A MIDI",
+ 0,
+ SYNTH_TYPE_MIDI,
+ 0,
+ 0,
+ 128,
+ 128,
+ 128,
+ SYNTH_CAP_INPUT,
+};
+
+static struct midi_info uartsio_midiinfo = {
+ "uart16550A MIDI",
+ 0,
+ 0,
+ 0,
+};
+
+/*
+ * These functions goes into uartsio_op_desc to get called
+ * from sound.c.
+ */
+
+static int uartsio_probe(device_t dev);
+static int uartsio_attach(device_t dev);
+
+static d_ioctl_t uartsio_ioctl;
+static driver_intr_t uartsio_intr;
+static midi_callback_t uartsio_callback;
+
+/* Here is the parameter structure per a device. */
+struct uartsio_softc {
+ device_t dev; /* device information */
+ mididev_info *devinfo; /* midi device information */
+
+ struct resource *io; /* Base of io port */
+ int io_rid; /* Io resource ID */
+ struct resource *irq; /* Irq */
+ int irq_rid; /* Irq resource ID */
+ void *ih; /* Interrupt cookie */
+
+ int fflags; /* File flags */
+
+ int has_fifo; /* TX/RX fifo in the uart */
+ int tx_size; /* Size of TX on a transmission */
+
+};
+
+typedef struct uartsio_softc *sc_p;
+
+/* These functions are local. */
+static void uartsio_startplay(sc_p scp);
+static int uartsio_xmit(sc_p scp);
+static int uartsio_readport(sc_p scp, int off);
+static void uartsio_writeport(sc_p scp, int off, u_int8_t value);
+static int uartsio_allocres(sc_p scp, device_t dev);
+static void uartsio_releaseres(sc_p scp, device_t dev);
+
+/*
+ * This is the device descriptor for the midi device.
+ */
+static mididev_info uartsio_op_desc = {
+ "16550 uart midi",
+
+ SNDCARD_UART16550,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ uartsio_ioctl,
+ NULL,
+
+ uartsio_callback,
+
+ MIDI_BUFFSIZE, /* Queue Length */
+
+ 0, /* XXX This is not an *audio* device! */
+};
+
+/*
+ * Here are the main functions to interact to the user process.
+ * These are called from snd* functions in sys/i386/isa/snd/sound.c.
+ */
+
+static int
+uartsio_probe(device_t dev)
+{
+ sc_p scp;
+ int unit;
+ u_char c;
+
+ if (isa_get_logicalid(dev) != 0)
+ /* This is NOT a PnP device! */
+ return (ENXIO);
+
+ scp = device_get_softc(dev);
+ unit = device_get_unit(dev);
+
+ device_set_desc(dev, uartsio_op_desc.name);
+ bzero(scp, sizeof(*scp));
+
+ scp->io_rid = 0;
+ scp->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &scp->io_rid, 0, ~0, 8, RF_ACTIVE);
+ if (scp->io == NULL)
+ return (ENXIO);
+
+ DEB(printf("uartsio%d: probing.\n", unit));
+
+/* Read the IER. The upper four bits should all be zero. */
+ c = uartsio_readport(scp, com_ier);
+ if ((c & 0xf0) != 0) {
+ uartsio_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+/* Read the MSR. The upper three bits should all be zero. */
+ c = uartsio_readport(scp, com_mcr);
+ if ((c & 0xe0) != 0) {
+ uartsio_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ /* XXX Do we need a loopback test? */
+
+ DEB(printf("uartsio%d: probed.\n", unit));
+
+ return (0);
+}
+
+static int
+uartsio_attach(device_t dev)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit;
+
+ scp = device_get_softc(dev);
+ unit = device_get_unit(dev);
+
+ DEB(printf("uartsio%d: attaching.\n", unit));
+
+ /* Allocate resources. */
+ if (uartsio_allocres(scp, dev)) {
+ uartsio_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ /* See the size of the tx fifo. */
+ uartsio_writeport(scp, com_fifo, FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_RX_HIGH);
+ if ((uartsio_readport(scp, com_iir) & IIR_FIFO_MASK) == FIFO_RX_HIGH) {
+ scp->has_fifo = 1;
+ scp->tx_size = TX_FIFO_SIZE;
+ DEB(printf("uartsio%d: uart is 16550A, tx size is %d bytes.\n", unit, scp->tx_size));
+ } else {
+ scp->has_fifo = 0;
+ scp->tx_size = 1;
+ DEB(printf("uartsio%d: uart is not 16550A.\n", unit));
+ }
+
+ /* Fill the softc. */
+ scp->dev = dev;
+ scp->devinfo = devinfo = &midi_info[unit];
+
+ /* Fill the midi info. */
+ bcopy(&uartsio_op_desc, devinfo, sizeof(uartsio_op_desc));
+ midiinit(devinfo, dev);
+ devinfo->flags = 0;
+ bcopy(&midisynth_op_desc, &devinfo->synth, sizeof(midisynth_op_desc));
+ snprintf(devinfo->midistat, sizeof(devinfo->midistat), "at 0x%x irq %d",
+ (u_int)rman_get_start(scp->io), (int)rman_get_start(scp->irq));
+
+ /* Init the queue. */
+ devinfo->midi_dbuf_in.unit_size = devinfo->midi_dbuf_out.unit_size = 1;
+ midibuf_init(&devinfo->midi_dbuf_in);
+ midibuf_init(&devinfo->midi_dbuf_out);
+ midibuf_init(&devinfo->midi_dbuf_passthru);
+
+ /* Configure the uart. */
+ uartsio_writeport(scp, com_cfcr, CFCR_DLAB); /* Latch the divisor. */
+ uartsio_writeport(scp, com_dlbl, 0x03);
+ uartsio_writeport(scp, com_dlbh, 0x00); /* We want a bitrate of 38.4kbps. */
+ uartsio_writeport(scp, com_cfcr, CFCR_8BITS); /* We want 8bits, 1 stop bit, no parity. */
+ uartsio_writeport(scp, com_mcr, MCR_IENABLE | MCR_RTS | MCR_DTR); /* Enable interrupt, set RTS and DTR. */
+ uartsio_writeport(scp, com_ier, IER_ERXRDY | IER_ETXRDY | IER_EMSC | IER_ERLS); /* Give us an interrupt on RXRDY, TXRDY, MSC and RLS. */
+ if (scp->has_fifo)
+ uartsio_writeport(scp, com_fifo, FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_RX_LOW); /* We use the fifo. */
+ else
+ uartsio_writeport(scp, com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | FIFO_RX_LOW); /* We do not use the fifo. */
+
+ /* Clear the gabage. */
+ uartsio_readport(scp, com_lsr);
+ uartsio_readport(scp, com_lsr);
+ uartsio_readport(scp, com_iir);
+ uartsio_readport(scp, com_data);
+
+ /* Increase the number of midi devices. */
+ nmidi++;
+
+ /* Now we can handle the interrupts. */
+ bus_setup_intr(dev, scp->irq, INTR_TYPE_TTY, uartsio_intr, scp, &scp->ih);
+
+ DEB(printf("uartsio%d: attached.\n", unit));
+
+ return (0);
+}
+
+static int
+uartsio_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit;
+ struct synth_info *synthinfo;
+ struct midi_info *midiinfo;
+
+ unit = MIDIUNIT(i_dev);
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("uartsio_ioctl: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("uartsio_ioctl: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+
+ switch (cmd) {
+ case SNDCTL_SYNTH_INFO:
+ synthinfo = (struct synth_info *)arg;
+ if (synthinfo->device > nmidi + nsynth || synthinfo->device != unit)
+ return (ENXIO);
+ bcopy(&uartsio_synthinfo, synthinfo, sizeof(uartsio_synthinfo));
+ synthinfo->device = unit;
+ return (0);
+ break;
+ case SNDCTL_MIDI_INFO:
+ midiinfo = (struct midi_info *)arg;
+ if (midiinfo->device > nmidi + nsynth || midiinfo->device != unit)
+ return (ENXIO);
+ bcopy(&uartsio_midiinfo, midiinfo, sizeof(uartsio_midiinfo));
+ midiinfo->device = unit;
+ return (0);
+ break;
+ default:
+ return (ENOSYS);
+ }
+ /* NOTREACHED */
+ return (EINVAL);
+}
+
+static void
+uartsio_intr(void *arg)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+
+ scp = (sc_p)arg;
+ devinfo = scp->devinfo;
+
+ uartsio_xmit(scp);
+
+ /* Invoke the upper layer. */
+ midi_intr(devinfo);
+}
+
+static int
+uartsio_callback(mididev_info *d, int reason)
+{
+ int unit;
+ sc_p scp;
+
+ if (d == NULL) {
+ DEB(printf("uartsio_callback: device not configured.\n"));
+ return (ENXIO);
+ }
+
+ unit = d->unit;
+ scp = d->softc;
+
+ switch (reason & MIDI_CB_REASON_MASK) {
+ case MIDI_CB_START:
+ if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) == 0)
+ /* Begin recording. */
+ d->flags |= MIDI_F_READING;
+ if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) == 0)
+ /* Start playing. */
+ uartsio_startplay(scp);
+ break;
+ case MIDI_CB_STOP:
+ case MIDI_CB_ABORT:
+ if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) != 0)
+ /* Stop recording. */
+ d->flags &= ~MIDI_F_READING;
+ if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) != 0)
+ /* Stop Playing. */
+ d->flags &= ~MIDI_F_WRITING;
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+/*
+ * Starts to play the data in the output queue.
+ * Call this at >=splmidi.
+ */
+static void
+uartsio_startplay(sc_p scp)
+{
+ mididev_info *devinfo;
+
+ devinfo = scp->devinfo;
+
+ /* Can we play now? */
+ if (devinfo->midi_dbuf_out.rl == 0)
+ return;
+
+ devinfo->flags |= MIDI_F_WRITING;
+ uartsio_xmit(scp);
+}
+
+static int
+uartsio_xmit(sc_p scp)
+{
+ mididev_info *devinfo;
+ midi_dbuf *dbuf;
+ int lsr, msr, iir, i, txsize;
+ u_char c[TX_FIFO_SIZE];
+
+ devinfo = scp->devinfo;
+
+ do {
+ /* Read the received data. */
+ while (((lsr = uartsio_readport(scp, com_lsr)) & LSR_RCV_MASK) != 0) {
+ /* Is this a data or an error/break? */
+ if ((lsr & LSR_RXRDY) == 0)
+ printf("uartsio_xmit: receive error or break in unit %d.\n", devinfo->unit);
+ else {
+ /* Receive the data. */
+ c[0] = uartsio_readport(scp, com_data);
+ /* Queue into the passthru buffer and start transmitting if we can. */
+ if ((devinfo->flags & MIDI_F_PASSTHRU) != 0 && ((devinfo->flags & MIDI_F_BUSY) == 0 || (devinfo->fflags & FWRITE) == 0)) {
+ midibuf_input_intr(&devinfo->midi_dbuf_passthru, &c[0], sizeof(c[0]));
+ devinfo->flags |= MIDI_F_WRITING;
+ }
+ /* Queue if we are reading. Discard an active sensing. */
+ if ((devinfo->flags & MIDI_F_READING) != 0 && c[0] != 0xfe)
+ midibuf_input_intr(&devinfo->midi_dbuf_in, &c[0], sizeof(c[0]));
+ }
+ }
+
+ /* Read MSR. */
+ msr = uartsio_readport(scp, com_msr);
+
+ /* See which source to use. */
+ if ((devinfo->flags & MIDI_F_PASSTHRU) == 0 || ((devinfo->flags & MIDI_F_BUSY) != 0 && (devinfo->fflags & FWRITE) != 0))
+ dbuf = &devinfo->midi_dbuf_out;
+ else
+ dbuf = &devinfo->midi_dbuf_passthru;
+
+ /* Transmit the data in the queue. */
+ if ((devinfo->flags & MIDI_F_WRITING) != 0 && (lsr & LSR_TXRDY) != 0 && (msr & MSR_CTS) != 0) {
+
+ /* Do we have the data to transmit? */
+ if (dbuf->rl == 0) {
+ /* Stop playing. */
+ devinfo->flags &= ~MIDI_F_WRITING;
+ } else {
+ /* send the data. */
+ txsize = scp->tx_size;
+ if (dbuf->rl < txsize)
+ txsize = dbuf->rl;
+ midibuf_output_intr(dbuf, c, txsize);
+ for (i = 0 ; i < txsize ; i++)
+ uartsio_writeport(scp, com_data, c[i]);
+ /* We are playing now. */
+ devinfo->flags |= MIDI_F_WRITING;
+ }
+ } else {
+ /* Do we have the data to transmit? */
+ if (dbuf->rl > 0)
+ /* Wait for the next interrupt. */
+ devinfo->flags |= MIDI_F_WRITING;
+ }
+ } while (((iir = uartsio_readport(scp, com_iir)) & IIR_IMASK) != IIR_NOPEND);
+
+ return (0);
+}
+
+/* Reads from a port. */
+static int
+uartsio_readport(sc_p scp, int off)
+{
+ return bus_space_read_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), off);
+}
+
+/* Writes to a port. */
+static void
+uartsio_writeport(sc_p scp, int off, u_int8_t value)
+{
+ return bus_space_write_1(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), off, value);
+}
+
+/* Allocates resources other than IO ports. */
+static int
+uartsio_allocres(sc_p scp, device_t dev)
+{
+ if (scp->irq == NULL) {
+ scp->irq_rid = 0;
+ scp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &scp->irq_rid, 0, ~0, 1, RF_ACTIVE);
+ }
+ if (scp->irq == NULL)
+ return (1);
+
+ return (0);
+}
+
+/* Releases resources. */
+static void
+uartsio_releaseres(sc_p scp, device_t dev)
+{
+ if (scp->irq != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, scp->irq_rid, scp->irq);
+ scp->irq = NULL;
+ }
+ if (scp->io != NULL) {
+ bus_release_resource(dev, SYS_RES_IOPORT, scp->io_rid, scp->io);
+ scp->io = NULL;
+ }
+}
+
+static device_method_t uartsio_methods[] = {
+/* Device interface */
+ DEVMETHOD(device_probe , uartsio_probe ),
+ DEVMETHOD(device_attach, uartsio_attach),
+
+ { 0, 0 },
+};
+
+static driver_t uartsio_driver = {
+ "midi",
+ uartsio_methods,
+ sizeof(struct uartsio_softc),
+};
+
+DRIVER_MODULE(uartsio, isa, uartsio_driver, midi_devclass, 0, 0);
diff --git a/sys/dev/sound/midi/midi.c b/sys/dev/sound/midi/midi.c
new file mode 100644
index 0000000..83152cf
--- /dev/null
+++ b/sys/dev/sound/midi/midi.c
@@ -0,0 +1,702 @@
+/*
+ * Main midi driver for FreeBSD. This file provides the main
+ * entry points for probe/attach and all i/o demultiplexing, including
+ * default routines for generic devices.
+ *
+ * (C) 1999 Seigo Tanimura
+ *
+ * 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.
+ *
+ *
+ * For each card type a template "mididev_info" structure contains
+ * all the relevant parameters, both for configuration and runtime.
+ *
+ * In this file we build tables of pointers to the descriptors for
+ * the various supported cards. The generic probe routine scans
+ * the table(s) looking for a matching entry, then invokes the
+ * board-specific probe routine. If successful, a pointer to the
+ * correct mididev_info is stored in mididev_last_probed, for subsequent
+ * use in the attach routine. The generic attach routine copies
+ * the template to a permanent descriptor (midi_info[unit] and
+ * friends), initializes all generic parameters, and calls the
+ * board-specific attach routine.
+ *
+ * On device calls, the generic routines do the checks on unit and
+ * device parameters, then call the board-specific routines if
+ * available, or try to perform the task using the default code.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "opt_devfs.h"
+
+#include <dev/sound/midi/midi.h>
+
+static devclass_t midi_devclass;
+
+static d_open_t midiopen;
+static d_close_t midiclose;
+static d_ioctl_t midiioctl;
+static d_read_t midiread;
+static d_write_t midiwrite;
+static d_poll_t midipoll;
+
+/* These functions are local. */
+static d_open_t midistat_open;
+static d_close_t midistat_close;
+static d_read_t midistat_read;
+static int midi_initstatus(char *buf, int size);
+static int midi_readstatus(char *buf, int *ptr, struct uio *uio);
+
+#define CDEV_MAJOR MIDI_CDEV_MAJOR
+static struct cdevsw midi_cdevsw = {
+ /* open */ midiopen,
+ /* close */ midiclose,
+ /* read */ midiread,
+ /* write */ midiwrite,
+ /* ioctl */ midiioctl,
+ /* poll */ midipoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "midi",
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+ /* bmaj */ -1
+};
+
+/*
+ * descriptors for active devices. also used as the public softc
+ * of a device.
+ */
+mididev_info midi_info[NMIDI_MAX];
+
+u_long nmidi; /* total number of midi devices, filled in by the driver */
+u_long nsynth; /* total number of synthesizers, filled in by the driver */
+
+/* These make the buffer for /dev/midistat */
+static int midistatbusy;
+static char midistatbuf[4096];
+static int midistatptr;
+
+/*
+ * This is the generic init routine
+ */
+int
+midiinit(mididev_info *d, device_t dev)
+{
+ int unit;
+
+ if (midi_devclass == NULL) {
+ midi_devclass = device_get_devclass(dev);
+ make_dev(&midi_cdevsw, MIDIMKMINOR(0, MIDI_DEV_STATUS),
+ UID_ROOT, GID_WHEEL, 0444, "midistat");
+ }
+
+ unit = device_get_unit(dev);
+ make_dev(&midi_cdevsw, MIDIMKMINOR(unit, MIDI_DEV_MIDIN),
+ UID_ROOT, GID_WHEEL, 0666, "midi%d", unit);
+
+ /*
+ * initialize standard parameters for the device. This can be
+ * overridden by device-specific configurations but better do
+ * here the generic things.
+ */
+
+ d->unit = device_get_unit(dev);
+ d->softc = device_get_softc(dev);
+ d->dev = dev;
+ d->magic = MAGIC(d->unit); /* debugging... */
+
+ return 0 ;
+}
+
+/*
+ * a small utility function which, given a device number, returns
+ * a pointer to the associated mididev_info struct, and sets the unit
+ * number.
+ */
+mididev_info *
+get_mididev_info(dev_t i_dev, int *unit)
+{
+ int u;
+ mididev_info *d = NULL;
+
+ if (MIDIDEV(i_dev) != MIDI_DEV_MIDIN)
+ return NULL;
+ u = MIDIUNIT(i_dev);
+ if (unit)
+ *unit = u;
+
+ if (u >= nmidi + nsynth) {
+ DEB(printf("get_mididev_info: unit %d is not configured.\n", u));
+ return NULL;
+ }
+ d = &midi_info[u];
+
+ return d;
+}
+
+/*
+ * here are the switches for the main functions. The switches do
+ * all necessary checks on the device number to make sure
+ * that the device is configured. They also provide some default
+ * functionalities so that device-specific drivers have to deal
+ * only with special cases.
+ */
+
+static int
+midiopen(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ return midi_open(i_dev, flags, mode, p);
+ case MIDI_DEV_STATUS:
+ return midistat_open(i_dev, flags, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+midiclose(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ return midi_close(i_dev, flags, mode, p);
+ case MIDI_DEV_STATUS:
+ return midistat_close(i_dev, flags, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+midiread(dev_t i_dev, struct uio * buf, int flag)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ return midi_read(i_dev, buf, flag);
+ case MIDI_DEV_STATUS:
+ return midistat_read(i_dev, buf, flag);
+ }
+
+ return (ENXIO);
+}
+
+static int
+midiwrite(dev_t i_dev, struct uio * buf, int flag)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ return midi_write(i_dev, buf, flag);
+ }
+
+ return (ENXIO);
+}
+
+static int
+midiioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ return midi_ioctl(i_dev, cmd, arg, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+midipoll(dev_t i_dev, int events, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_MIDIN:
+ return midi_poll(i_dev, events, p);
+ }
+
+ return (ENXIO);
+}
+
+/*
+ * Followings are the generic methods in midi drivers.
+ */
+
+int
+midi_open(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ int dev, unit, s, ret;
+ mididev_info *d;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ DEB(printf("open midi%d subdev %d flags 0x%08x mode 0x%08x\n",
+ unit, dev & 0xf, flags, mode));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ s = splmidi();
+
+ /* Mark this device busy. */
+ device_busy(d->dev);
+ if ((d->flags & MIDI_F_BUSY) != 0) {
+ splx(s);
+ DEB(printf("opl_open: unit %d is busy.\n", unit));
+ return (EBUSY);
+ }
+ d->flags |= MIDI_F_BUSY;
+ d->flags &= ~(MIDI_F_READING | MIDI_F_WRITING);
+ d->fflags = flags;
+
+ /* Init the queue. */
+ if ((d->fflags & FREAD) != 0)
+ midibuf_init(&d->midi_dbuf_in);
+ if ((d->fflags & FWRITE) != 0) {
+ midibuf_init(&d->midi_dbuf_out);
+ midibuf_init(&d->midi_dbuf_passthru);
+ }
+
+ if (d->open == NULL)
+ ret = 0;
+ else
+ ret = d->open(i_dev, flags, mode, p);
+
+ splx(s);
+
+ return (ret);
+}
+
+int
+midi_close(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ int dev, unit, s, ret;
+ mididev_info *d;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ DEB(printf("close midi%d subdev %d\n", unit, dev & 0xf));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ s = splmidi();
+
+ /* Clear the queues. */
+ if ((d->fflags & FREAD) != 0)
+ midibuf_init(&d->midi_dbuf_in);
+ if ((d->fflags & FWRITE) != 0) {
+ midibuf_init(&d->midi_dbuf_out);
+ midibuf_init(&d->midi_dbuf_passthru);
+ }
+
+ /* Stop playing and unmark this device busy. */
+ d->flags &= ~MIDI_F_BUSY;
+ d->fflags = 0;
+
+ device_unbusy(d->dev);
+
+ if (d->close == NULL)
+ ret = 0;
+ else
+ ret = d->close(i_dev, flags, mode, p);
+
+ splx(s);
+
+ return (ret);
+}
+
+int
+midi_read(dev_t i_dev, struct uio * buf, int flag)
+{
+ int dev, unit, s, len, ret;
+ mididev_info *d ;
+
+ dev = minor(i_dev);
+
+ d = get_mididev_info(i_dev, &unit);
+ DEB(printf("read midi%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ ret = 0;
+ s = splmidi();
+
+ /* Begin recording. */
+ d->callback(d, MIDI_CB_START | MIDI_CB_RD);
+
+ /* Have we got the data to read? */
+ if ((d->flags & MIDI_F_NBIO) != 0 && d->midi_dbuf_in.rl == 0)
+ ret = EAGAIN;
+ else {
+ len = buf->uio_resid;
+ ret = midibuf_uioread(&d->midi_dbuf_in, buf, len);
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+ }
+
+ if (ret == 0 && d->read != NULL)
+ ret = d->read(i_dev, buf, flag);
+
+ splx(s);
+
+ return (ret);
+}
+
+int
+midi_write(dev_t i_dev, struct uio * buf, int flag)
+{
+ int dev, unit, s, len, ret;
+ mididev_info *d;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ DEB(printf("write midi%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag));
+
+ if (d == NULL)
+ return (ENXIO);
+
+ ret = 0;
+ s = splmidi();
+
+ /* Begin playing. */
+ d->callback(d, MIDI_CB_START | MIDI_CB_WR);
+
+ /* Have we got the data to write? */
+ if ((d->flags & MIDI_F_NBIO) != 0 && d->midi_dbuf_out.fl == 0)
+ ret = EAGAIN;
+ else {
+ len = buf->uio_resid;
+ if (len > d->midi_dbuf_out.fl &&
+ (d->flags & MIDI_F_NBIO))
+ len = d->midi_dbuf_out.fl;
+ ret = midibuf_uiowrite(&d->midi_dbuf_out, buf, len);
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+ }
+
+ /* Begin playing. */
+ d->callback(d, MIDI_CB_START | MIDI_CB_WR);
+
+ if (ret == 0 && d->write != NULL)
+ ret = d->write(i_dev, buf, flag);
+
+ splx(s);
+
+ return (ret);
+}
+
+/*
+ * generic midi ioctl. Functions of the default driver can be
+ * overridden by the device-specific ioctl call.
+ * If a device-specific call returns ENOSYS (Function not implemented),
+ * the default driver is called. Otherwise, the returned value
+ * is passed up.
+ *
+ * The default handler, for many parameters, sets the value in the
+ * descriptor, sets MIDI_F_INIT, and calls the callback function with
+ * reason INIT. If successful, the callback returns 1 and the caller
+ * can update the parameter.
+ */
+
+int
+midi_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
+{
+ int ret = ENOSYS, dev, unit;
+ mididev_info *d;
+ struct snd_size *sndsize;
+ u_long s;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ if (d == NULL)
+ return (ENXIO);
+
+ if (d->ioctl)
+ ret = d->ioctl(i_dev, cmd, arg, mode, p);
+ if (ret != ENOSYS)
+ return ret;
+
+ /*
+ * pass control to the default ioctl handler. Set ret to 0 now.
+ */
+ ret = 0;
+
+ /*
+ * all routines are called with int. blocked. Make sure that
+ * ints are re-enabled when calling slow or blocking functions!
+ */
+ s = splmidi();
+ switch(cmd) {
+
+ /*
+ * we start with the new ioctl interface.
+ */
+ case AIONWRITE: /* how many bytes can write ? */
+ *(int *)arg = d->midi_dbuf_out.fl;
+ break;
+
+ case AIOSSIZE: /* set the current blocksize */
+ sndsize = (struct snd_size *)arg;
+ if (sndsize->play_size <= d->midi_dbuf_out.unit_size && sndsize->rec_size <= d->midi_dbuf_in.unit_size) {
+ d->flags &= ~MIDI_F_HAS_SIZE;
+ d->midi_dbuf_out.blocksize = d->midi_dbuf_out.unit_size;
+ d->midi_dbuf_in.blocksize = d->midi_dbuf_in.unit_size;
+ }
+ else {
+ if (sndsize->play_size > d->midi_dbuf_out.bufsize / 4)
+ sndsize->play_size = d->midi_dbuf_out.bufsize / 4;
+ if (sndsize->rec_size > d->midi_dbuf_in.bufsize / 4)
+ sndsize->rec_size = d->midi_dbuf_in.bufsize / 4;
+ /* Round up the size to the multiple of EV_SZ. */
+ d->midi_dbuf_out.blocksize =
+ ((sndsize->play_size + d->midi_dbuf_out.unit_size - 1)
+ / d->midi_dbuf_out.unit_size) * d->midi_dbuf_out.unit_size;
+ d->midi_dbuf_in.blocksize =
+ ((sndsize->rec_size + d->midi_dbuf_in.unit_size - 1)
+ / d->midi_dbuf_in.unit_size) * d->midi_dbuf_in.unit_size;
+ d->flags |= MIDI_F_HAS_SIZE;
+ }
+ /* FALLTHROUGH */
+ case AIOGSIZE: /* get the current blocksize */
+ sndsize = (struct snd_size *)arg;
+ sndsize->play_size = d->midi_dbuf_out.blocksize;
+ sndsize->rec_size = d->midi_dbuf_in.blocksize;
+
+ ret = 0;
+ break;
+
+ case AIOSTOP:
+ if (*(int *)arg == AIOSYNC_PLAY) /* play */
+ *(int *)arg = d->callback(d, MIDI_CB_STOP | MIDI_CB_WR);
+ else if (*(int *)arg == AIOSYNC_CAPTURE)
+ *(int *)arg = d->callback(d, MIDI_CB_STOP | MIDI_CB_RD);
+ else {
+ splx(s);
+ DEB(printf("AIOSTOP: bad channel 0x%x\n", *(int *)arg));
+ *(int *)arg = 0 ;
+ }
+ break ;
+
+ case AIOSYNC:
+ DEB(printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n",
+ ((snd_sync_parm *)arg)->chan,
+ ((snd_sync_parm *)arg)->pos));
+ break;
+ /*
+ * here follow the standard ioctls (filio.h etc.)
+ */
+ case FIONREAD: /* get # bytes to read */
+ *(int *)arg = d->midi_dbuf_in.rl;
+ break;
+
+ case FIOASYNC: /*set/clear async i/o */
+ DEB( printf("FIOASYNC\n") ; )
+ break;
+
+ case FIONBIO: /* set/clear non-blocking i/o */
+ if ( *(int *)arg == 0 )
+ d->flags &= ~MIDI_F_NBIO ;
+ else
+ d->flags |= MIDI_F_NBIO ;
+ break ;
+
+ case MIOSPASSTHRU: /* set/clear passthru */
+ if ( *(int *)arg == 0 )
+ d->flags &= ~MIDI_F_PASSTHRU ;
+ else
+ d->flags |= MIDI_F_PASSTHRU ;
+
+ /* Init the queue. */
+ midibuf_init(&d->midi_dbuf_passthru);
+
+ /* FALLTHROUGH */
+ case MIOGPASSTHRU: /* get passthru */
+ if ((d->flags & MIDI_F_PASSTHRU) != 0)
+ (int *)arg = 1;
+ else
+ (int *)arg = 0;
+ break ;
+
+ default:
+ DEB(printf("default ioctl midi%d subdev %d fn 0x%08x fail\n",
+ unit, dev & 0xf, cmd));
+ ret = EINVAL;
+ break ;
+ }
+ splx(s);
+ return ret ;
+}
+
+int
+midi_poll(dev_t i_dev, int events, struct proc * p)
+{
+ int unit, dev, ret, s, lim;
+ mididev_info *d;
+
+ dev = minor(i_dev);
+ d = get_mididev_info(i_dev, &unit);
+
+ if (d == NULL)
+ return (ENXIO);
+
+ if (d->poll)
+ ret = d->poll(i_dev, events, p);
+
+ ret = 0;
+ s = splmidi();
+
+ /* Look up the apropriate queue and select it. */
+ if ((events & (POLLOUT | POLLWRNORM)) != 0) {
+ /* Start playing. */
+ d->callback(d, MIDI_CB_START | MIDI_CB_WR);
+
+ /* Find out the boundary. */
+ if ((d->flags & MIDI_F_HAS_SIZE) != 0)
+ lim = d->midi_dbuf_out.blocksize;
+ else
+ lim = d->midi_dbuf_out.unit_size;
+ if (d->midi_dbuf_out.fl < lim)
+ /* No enough space, record select. */
+ selrecord(p, &d->midi_dbuf_out.sel);
+ else
+ /* We can write now. */
+ ret |= events & (POLLOUT | POLLWRNORM);
+ }
+ if ((events & (POLLIN | POLLRDNORM)) != 0) {
+ /* Start recording. */
+ d->callback(d, MIDI_CB_START | MIDI_CB_RD);
+
+ /* Find out the boundary. */
+ if ((d->flags & MIDI_F_HAS_SIZE) != 0)
+ lim = d->midi_dbuf_in.blocksize;
+ else
+ lim = d->midi_dbuf_in.unit_size;
+ if (d->midi_dbuf_in.rl < lim)
+ /* No data ready, record select. */
+ selrecord(p, &d->midi_dbuf_in.sel);
+ else
+ /* We can write now. */
+ ret |= events & (POLLIN | POLLRDNORM);
+ }
+ splx(s);
+
+ return (ret);
+}
+
+void
+midi_intr(mididev_info *d)
+{
+ if (d->intr != NULL)
+ d->intr(d->intrarg, d);
+}
+
+/*
+ * These handle the status message of the midi drivers.
+ */
+
+int
+midistat_open(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ if (midistatbusy)
+ return (EBUSY);
+
+ bzero(midistatbuf, sizeof(midistatbuf));
+ midistatptr = 0;
+ if (midi_initstatus(midistatbuf, sizeof(midistatbuf) - 1))
+ return (ENOMEM);
+
+ midistatbusy = 1;
+
+ return (0);
+}
+
+int
+midistat_close(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ midistatbusy = 0;
+
+ return (0);
+}
+
+int
+midistat_read(dev_t i_dev, struct uio * buf, int flag)
+{
+ return midi_readstatus(midistatbuf, &midistatptr, buf);
+}
+
+/*
+ * finally, some "libraries"
+ */
+
+/* Inits the buffer for /dev/midistat. */
+static int
+midi_initstatus(char *buf, int size)
+{
+ int i, p;
+ device_t dev;
+ mididev_info *md;
+
+ p = 0;
+ p += snprintf(buf, size, "FreeBSD Midi Driver (newmidi) %s %s\nInstalled devices:\n", __DATE__, __TIME__);
+ for (i = 0 ; i < NMIDI_MAX ; i++) {
+ md = &midi_info[i];
+ if (!MIDICONFED(md))
+ continue;
+ dev = devclass_get_device(midi_devclass, i);
+ if (p < size)
+ p += snprintf(&buf[p], size - p, "midi%d: <%s> %s\n", i, device_get_desc(dev), md->midistat);
+ else
+ return (1);
+ }
+
+ return (0);
+}
+
+/* Reads the status message. */
+static int
+midi_readstatus(char *buf, int *ptr, struct uio *uio)
+{
+ int s, len;
+
+ s = splmidi();
+ len = min(uio->uio_resid, strlen(&buf[*ptr]));
+ if (len > 0) {
+ uiomove(&buf[*ptr], len, uio);
+ *ptr += len;
+ }
+ splx(s);
+
+ return (0);
+}
diff --git a/sys/dev/sound/midi/midi.h b/sys/dev/sound/midi/midi.h
new file mode 100644
index 0000000..2e36ed0
--- /dev/null
+++ b/sys/dev/sound/midi/midi.h
@@ -0,0 +1,294 @@
+/*
+ * Include file for midi driver.
+ *
+ * Copyright by Seigo Tanimura 1999.
+ *
+ * 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$
+ *
+ */
+
+/*
+ * first, include kernel header files.
+ */
+
+#ifndef _MIDI_H_
+#define _MIDI_H_
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioccom.h>
+
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#include <sys/fcntl.h>
+#include <sys/tty.h>
+#include <sys/proc.h>
+
+#include <sys/kernel.h> /* for DATA_SET */
+
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+#include <machine/clock.h> /* for DELAY */
+#include <machine/resource.h>
+#include <machine/bus_memio.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/clock.h> /* for DELAY */
+#include <sys/soundcard.h>
+#include <sys/rman.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+
+#include <dev/sound/midi/miditypes.h>
+#include <dev/sound/midi/midibuf.h>
+#include <dev/sound/midi/midisynth.h>
+
+#define MIDI_CDEV_MAJOR 30
+
+/*
+ * descriptor of midi operations ...
+ *
+ */
+
+struct _mididev_info {
+
+ /*
+ * the first part of the descriptor is filled up from a
+ * template.
+ */
+ char name[64];
+
+ int type;
+
+ d_open_t *open;
+ d_close_t *close;
+ d_read_t *read;
+ d_write_t *write;
+ d_ioctl_t *ioctl;
+ d_poll_t *poll;
+ midi_callback_t *callback;
+
+ /*
+ * combinations of the following flags are used as second argument in
+ * the callback from the dma module to the device-specific routines.
+ */
+
+#define MIDI_CB_RD 0x100 /* read callback */
+#define MIDI_CB_WR 0x200 /* write callback */
+#define MIDI_CB_REASON_MASK 0xff
+#define MIDI_CB_START 0x01 /* start dma op */
+#define MIDI_CB_STOP 0x03 /* stop dma op */
+#define MIDI_CB_ABORT 0x04 /* abort dma op */
+#define MIDI_CB_INIT 0x05 /* init board parameters */
+
+ /*
+ * callback extensions
+ */
+#define MIDI_CB_DMADONE 0x10
+#define MIDI_CB_DMAUPDATE 0x11
+#define MIDI_CB_DMASTOP 0x12
+
+ /* init can only be called with int enabled and
+ * no pending DMA activity.
+ */
+
+ /*
+ * whereas from here, parameters are set at runtime.
+ * resources are stored in the softc of the device,
+ * not in the common structure.
+ */
+
+ int unit; /* unit number of the device */
+ void *softc; /* softc for the device */
+ device_t dev; /* device_t for the device */
+
+ int bd_id ; /* used to hold board-id info, eg. sb version,
+ * mss codec type, etc. etc.
+ */
+
+ midi_dbuf midi_dbuf_in; /* midi input event/message queue */
+ midi_dbuf midi_dbuf_out; /* midi output event/message queue */
+ midi_dbuf midi_dbuf_passthru; /* midi passthru event/message queue */
+
+ /*
+ * these parameters describe the operation of the board.
+ * Generic things like busy flag, speed, etc are here.
+ */
+
+ volatile u_long flags ; /* 32 bits, used for various purposes. */
+ int fflags; /* file flag */
+
+ /*
+ * we have separate flags for read and write, although in some
+ * cases this is probably not necessary (e.g. because we cannot
+ * know how many processes are using the device, we cannot
+ * distinguish if open, close, abort are for a write or for a
+ * read).
+ */
+
+ /*
+ * the following flag is used by open-close routines
+ * to mark the status of the device.
+ */
+#define MIDI_F_BUSY 0x0001 /* has been opened */
+ /*
+ * the next two are used to allow only one pending operation of
+ * each type.
+ */
+#define MIDI_F_READING 0x0004 /* have a pending read */
+#define MIDI_F_WRITING 0x0008 /* have a pending write */
+
+ /*
+ * flag used to mark a pending close.
+ */
+#define MIDI_F_CLOSING 0x0040 /* a pending close */
+
+ /*
+ * if user has not set block size, then make it adaptive
+ * (0.25s, or the perhaps last read/write ?)
+ */
+#define MIDI_F_HAS_SIZE 0x0080 /* user set block size */
+ /*
+ * assorted flags related to operating mode.
+ */
+#define MIDI_F_STEREO 0x0100 /* doing stereo */
+#define MIDI_F_NBIO 0x0200 /* do non-blocking i/o */
+#define MIDI_F_PASSTHRU 0x0400 /* pass received data to output port */
+
+ /*
+ * these flags mark a pending abort on a r/w operation.
+ */
+#define MIDI_F_ABORTING 0x1000 /* a pending abort */
+
+ /*
+ * this is used to mark that board initialization is needed, e.g.
+ * because of a change in sampling rate, format, etc. -- It will
+ * be done at the next convenient time.
+ */
+#define MIDI_F_INIT 0x4000 /* changed parameters. need init */
+
+ int play_blocksize, rec_blocksize; /* blocksize for io and dma ops */
+
+#define mwsel midi_dbuf_out.sel
+#define mrsel midi_dbuf_in.sel
+ u_long interrupts; /* counter of interrupts */
+ u_long magic;
+#define MAGIC(unit) ( 0xa4d10de0 + unit )
+ void *device_data ; /* just in case it is needed...*/
+
+ midi_intr_t *intr; /* interrupt handler of the upper layer (ie sequencer) */
+ void *intrarg; /* argument to interrupt handler */
+
+ /* The following is the interface from a midi sequencer to a midi device. */
+ synthdev_info synth;
+
+ /* This is the status message to display via /dev/midistat */
+ char midistat[128];
+} ;
+
+/*
+ * then ioctls and other stuff
+ */
+
+#define NMIDI_MAX 64 /* Number of supported devices */
+
+/*
+ * 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))
+
+/*
+ * convert dev_t to unit and dev
+ */
+#define MIDIMINOR(x) (minor(x))
+#define MIDIUNIT(x) ((MIDIMINOR(x) & 0x000000f0) >> 4)
+#define MIDIDEV(x) (MIDIMINOR(x) & 0x0000000f)
+#define MIDIMKMINOR(u, d) (((u) & 0x0f) << 4 | ((d) & 0x0f))
+#define MIDIMKDEV(m, u, d) (makedev((m), MIDIMKMINOR((u), (d))))
+
+/*
+ * see if the device is configured
+ */
+#define MIDICONFED(x) ((x)->ioctl != NULL)
+
+/*
+ * finally, all default parameters
+ */
+#define MIDI_BUFFSIZE (4 * 1024) /* XXX */
+
+/*
+ * some macros for debugging purposes
+ * DDB/DEB to enable/disable debugging stuff
+ * BVDDB to enable debugging when bootverbose
+ */
+#define DDB(x) x /* XXX */
+#define BVDDB(x) if (bootverbose) x
+
+#ifndef DEB
+#define DEB(x)
+#endif
+
+ extern mididev_info midi_info[NMIDI_MAX];
+
+ extern u_long nmidi;
+ extern u_long nsynth;
+
+/* This is the generic midi drvier initializer. */
+ int midiinit(mididev_info *d, device_t dev);
+
+/* This provides an access to the mididev_info. */
+ mididev_info *get_mididev_info(dev_t i_dev, int *unit);
+
+/* These are the generic methods for a midi driver. */
+ d_open_t midi_open;
+ d_close_t midi_close;
+ d_ioctl_t midi_ioctl;
+ d_read_t midi_read;
+ d_write_t midi_write;
+ d_poll_t midi_poll;
+
+/* Common interrupt handler */
+void midi_intr(mididev_info *);
+
+/*
+ * library functions (in midi.c)
+ */
+#define splmidi() spltty()
+
+/*
+ * Minor numbers for the midi driver.
+ */
+
+#define MIDI_DEV_MIDIN 2 /* Raw midi access */
+#define MIDI_DEV_STATUS 11 /* /dev/midistat */
+
+#endif /* _MIDI_H_ */
diff --git a/sys/dev/sound/midi/midibuf.c b/sys/dev/sound/midi/midibuf.c
new file mode 100644
index 0000000..4e23800
--- /dev/null
+++ b/sys/dev/sound/midi/midibuf.c
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 1999 Seigo Tanimura
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS
+ * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * This file implements a midi event/message queue. A midi
+ * event/message queue holds midi events and messages to
+ * transmit to or received from a midi interface.
+ */
+
+#include "opt_devfs.h"
+
+#include <dev/sound/midi/midi.h>
+
+/* Some macros to handle the queue. */
+#define DATA_AVAIL(dbuf) ((dbuf)->rl)
+#define SPACE_AVAIL(dbuf) ((dbuf)->fl)
+
+static void queuerawdata(midi_dbuf *dbuf, char *data, int len);
+static void queueuiodata(midi_dbuf *dbuf, struct uio *buf, int len);
+static void dequeuerawdata(midi_dbuf *dbuf, char *data, int len);
+static void copyrawdata(midi_dbuf *dbuf, char *data, int len);
+static void dequeueuiodata(midi_dbuf *dbuf, struct uio *buf, int len);
+
+/*
+ * Here are the functions to interact to the midi device drivers.
+ * These are called from midi device driver functions under sys/i386/isa/snd.
+ */
+
+int
+midibuf_init(midi_dbuf *dbuf)
+{
+ if (dbuf->buf != NULL)
+ free(dbuf->buf, M_DEVBUF);
+ dbuf->buf = malloc(MIDI_BUFFSIZE, M_DEVBUF, M_NOWAIT);
+ bzero(dbuf->buf, MIDI_BUFFSIZE);
+ dbuf->bufsize = MIDI_BUFFSIZE;
+ dbuf->rp = dbuf->fp = 0;
+ dbuf->dl = 0;
+ dbuf->rl = 0;
+ dbuf->fl = dbuf->bufsize;
+ dbuf->int_count = 0;
+ dbuf->chan = 0;
+ /*dbuf->unit_size = 1;*/ /* The drivers are responsible. */
+ bzero(&dbuf->sel, sizeof(dbuf->sel));
+ dbuf->total = 0;
+ dbuf->prev_total = 0;
+ dbuf->blocksize = dbuf->bufsize / 4;
+
+ return (0);
+}
+
+/* The sequencer calls this function to queue data. */
+int
+midibuf_seqwrite(midi_dbuf *dbuf, u_char* data, int len)
+{
+ int i, lwrt, lwritten;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ lwritten = 0;
+ /* Write down every single byte. */
+ while (len > 0) {
+ /* Find out the number of bytes to write. */
+ lwrt = SPACE_AVAIL(dbuf);
+ if (lwrt > len)
+ lwrt = len;
+ if (lwrt > 0) {
+ /* We can write some now. Queue the data. */
+ queuerawdata(dbuf, data, lwrt);
+
+ lwritten += lwrt;
+ len -= lwrt;
+ data += lwrt;
+ }
+
+ /* Have we got still more data to write? */
+ if (len > 0) {
+ /* Yes, sleep until we have enough space. */
+ i = tsleep((void *)&dbuf->tsleep_out, PRIBIO | PCATCH, "mbsqwt", 0);
+ if (i == EINTR || i == ERESTART)
+ return (-i);
+ }
+ }
+
+ return (lwritten);
+}
+
+/* sndwrite calls this function to queue data. */
+int
+midibuf_uiowrite(midi_dbuf *dbuf, struct uio *buf, int len)
+{
+ int i, lwrt, lwritten;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ lwritten = 0;
+ /* Write down every single byte. */
+ while (len > 0) {
+ /* Find out the number of bytes to write. */
+ lwrt = SPACE_AVAIL(dbuf);
+ if (lwrt > len)
+ lwrt = len;
+ if (lwrt > 0) {
+ /* We can write some now. Queue the data. */
+ queueuiodata(dbuf, buf, lwrt);
+
+ lwritten += lwrt;
+ len -= lwrt;
+ }
+
+ /* Have we got still more data to write? */
+ if (len > 0) {
+ /* Yes, sleep until we have enough space. */
+ i = tsleep(&dbuf->tsleep_out, PRIBIO | PCATCH, "mbuiwt", 0);
+ if (i == EINTR || i == ERESTART)
+ return (-i);
+ }
+ }
+
+ return (lwritten);
+}
+
+int
+midibuf_output_intr(midi_dbuf *dbuf, u_char *data, int len)
+{
+ int lrd;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ /* Have we got any data in the queue? */
+ if ((lrd = DATA_AVAIL(dbuf)) == 0)
+ return (0);
+
+ /* Dequeue the data. */
+ if (lrd > len)
+ lrd = len;
+ dequeuerawdata(dbuf, data, lrd);
+
+ return (lrd);
+}
+
+int
+midibuf_input_intr(midi_dbuf *dbuf, u_char *data, int len)
+{
+ int lwritten;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ lwritten = 0;
+
+ /* Have we got any data to write? */
+ if (len == 0)
+ return (0);
+ /* Can we write now? */
+ if (SPACE_AVAIL(dbuf) < len)
+ return (-EAGAIN);
+
+ /* We can write some now. Queue the data. */
+ queuerawdata(dbuf, data, len);
+ lwritten = len;
+
+ /* Have we managed to write the whole data? */
+ if (lwritten < len)
+ printf("midibuf_input_intr: queue did not have enough space, discarded %d bytes out of %d bytes.\n", len - lwritten, len);
+
+ return (lwritten);
+}
+
+/* The sequencer calls this function to dequeue data. */
+int
+midibuf_seqread(midi_dbuf *dbuf, u_char* data, int len)
+{
+ int i, lrd, lread;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ lread = 0;
+ /* Write down every single byte. */
+ while (len > 0) {
+ /* Have we got data to read? */
+ if ((lrd = DATA_AVAIL(dbuf)) == 0) {
+ /* No, sleep until we have data ready to read. */
+ i = tsleep(&dbuf->tsleep_in, PRIBIO | PCATCH, "mbsqrd", 0);
+ if (i == EINTR || i == ERESTART)
+ return (-i);
+ if (i == EWOULDBLOCK)
+ continue;
+ /* Find out the number of bytes to read. */
+ lrd = DATA_AVAIL(dbuf);
+ }
+
+ if (lrd > len)
+ lrd = len;
+ if (lrd > 0) {
+ /* We can read some data now. Dequeue the data. */
+ dequeuerawdata(dbuf, data, lrd);
+
+ lread += lrd;
+ len -= lrd;
+ data += lrd;
+ }
+ }
+
+ return (lread);
+}
+
+/* The sequencer calls this function to copy data without dequeueing. */
+int
+midibuf_seqcopy(midi_dbuf *dbuf, u_char* data, int len)
+{
+ int i, lrd, lread;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ lread = 0;
+ /* Write down every single byte. */
+ while (len > 0) {
+ /* Have we got data to read? */
+ if ((lrd = DATA_AVAIL(dbuf)) == 0) {
+ /* No, sleep until we have data ready to read. */
+ i = tsleep(&dbuf->tsleep_in, PRIBIO | PCATCH, "mbsqrd", 0);
+ if (i == EINTR || i == ERESTART)
+ return (-i);
+ if (i == EWOULDBLOCK)
+ continue;
+ /* Find out the number of bytes to read. */
+ lrd = DATA_AVAIL(dbuf);
+ }
+
+ if (lrd > len)
+ lrd = len;
+ if (lrd > 0) {
+ /* We can read some data now. Copy the data. */
+ copyrawdata(dbuf, data, lrd);
+
+ lread += lrd;
+ len -= lrd;
+ data += lrd;
+ }
+ }
+
+ return (lread);
+}
+
+/* sndread calls this function to dequeue data. */
+int
+midibuf_uioread(midi_dbuf *dbuf, struct uio *buf, int len)
+{
+ int i, lrd, lread;
+
+ /* Is this a real queue? */
+ if (dbuf == (midi_dbuf *)NULL)
+ return (0);
+
+ lread = 0;
+ while (len > 0 && lread == 0) {
+ /* Have we got data to read? */
+ if ((lrd = DATA_AVAIL(dbuf)) == 0) {
+ /* No, sleep until we have data ready to read. */
+ i = tsleep(&dbuf->tsleep_in, PRIBIO | PCATCH, "mbuird", 0);
+ if (i == EINTR || i == ERESTART)
+ return (-i);
+ if (i == EWOULDBLOCK)
+ continue;
+ /* Find out the number of bytes to read. */
+ lrd = DATA_AVAIL(dbuf);
+ }
+
+ if (lrd > len)
+ lrd = len;
+ if (lrd > 0) {
+ /* We can read some data now. Dequeue the data. */
+ dequeueuiodata(dbuf, buf, lrd);
+
+ lread += lrd;
+ len -= lrd;
+ }
+ }
+
+ return (lread);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+static void
+queuerawdata(midi_dbuf *dbuf, char *data, int len)
+{
+ /* dbuf->fp might wrap around dbuf->bufsize. */
+ if (dbuf->bufsize - dbuf->fp < len) {
+ /* The new data wraps, copy them twice. */
+ memcpy(dbuf->buf + dbuf->fp, data, dbuf->bufsize - dbuf->fp);
+ memcpy(dbuf->buf, data + dbuf->bufsize - dbuf->fp, len - (dbuf->bufsize - dbuf->fp));
+ } else
+ /* The new data do not wrap, once is enough. */
+ memcpy(dbuf->buf + dbuf->fp, data, len);
+
+ /* Adjust the pointer and the length counters. */
+ dbuf->fp = (dbuf->fp + len) % dbuf->bufsize;
+ dbuf->fl -= len;
+ dbuf->rl += len;
+
+ /* Wake up the processes sleeping on input data. */
+ wakeup(&dbuf->tsleep_in);
+ if (dbuf->sel.si_pid && dbuf->rl >= dbuf->blocksize)
+ selwakeup(&dbuf->sel);
+}
+
+static void
+queueuiodata(midi_dbuf *dbuf, struct uio *buf, int len)
+{
+ /* dbuf->fp might wrap around dbuf->bufsize. */
+ if (dbuf->bufsize - dbuf->fp < len) {
+ /* The new data wraps, copy them twice. */
+ uiomove((caddr_t)(dbuf->buf + dbuf->fp), dbuf->bufsize - dbuf->fp, buf);
+ uiomove((caddr_t)(dbuf->buf), len - (dbuf->bufsize - dbuf->fp), buf);
+ } else
+ /* The new data do not wrap, once is enough. */
+ uiomove((caddr_t)(dbuf->buf + dbuf->fp), len, buf);
+
+ /* Adjust the pointer and the length counters. */
+ dbuf->fp = (dbuf->fp + len) % dbuf->bufsize;
+ dbuf->fl -= len;
+ dbuf->rl += len;
+
+ /* Wake up the processes sleeping on queueing. */
+ wakeup(&dbuf->tsleep_in);
+ if (dbuf->sel.si_pid && dbuf->rl >= dbuf->blocksize)
+ selwakeup(&dbuf->sel);
+}
+
+static void
+dequeuerawdata(midi_dbuf *dbuf, char *data, int len)
+{
+ /* Copy the data. */
+ copyrawdata(dbuf, data, len);
+
+ /* Adjust the pointer and the length counters. */
+ dbuf->rp = (dbuf->rp + len) % dbuf->bufsize;
+ dbuf->rl -= len;
+ dbuf->fl += len;
+
+ /* Wake up the processes sleeping on queueing. */
+ wakeup(&dbuf->tsleep_out);
+ if (dbuf->sel.si_pid && dbuf->fl >= dbuf->blocksize)
+ selwakeup(&dbuf->sel);
+}
+
+static void
+copyrawdata(midi_dbuf *dbuf, char *data, int len)
+{
+ /* dbuf->rp might wrap around dbuf->bufsize. */
+ if (dbuf->bufsize - dbuf->rp < len) {
+ /* The data to be read wraps, copy them twice. */
+ memcpy(data, dbuf->buf + dbuf->rp, dbuf->bufsize - dbuf->rp);
+ memcpy(data + dbuf->bufsize - dbuf->rp, dbuf->buf, len - (dbuf->bufsize - dbuf->rp));
+ } else
+ /* The new data do not wrap, once is enough. */
+ memcpy(data, dbuf->buf + dbuf->rp, len);
+}
+
+static void
+dequeueuiodata(midi_dbuf *dbuf, struct uio *buf, int len)
+{
+ /* dbuf->rp might wrap around dbuf->bufsize. */
+ if (dbuf->bufsize - dbuf->rp < len) {
+ /* The new data wraps, copy them twice. */
+ uiomove((caddr_t)(dbuf->buf + dbuf->rp), dbuf->bufsize - dbuf->rp, buf);
+ uiomove((caddr_t)(dbuf->buf), len - (dbuf->bufsize - dbuf->rp), buf);
+ } else
+ /* The new data do not wrap, once is enough. */
+ uiomove((caddr_t)(dbuf->buf + dbuf->rp), len, buf);
+
+ /* Adjust the pointer and the length counters. */
+ dbuf->rp = (dbuf->rp + len) % dbuf->bufsize;
+ dbuf->rl -= len;
+ dbuf->fl += len;
+
+ /* Wake up the processes sleeping on queueing. */
+ wakeup(&dbuf->tsleep_out);
+ if (dbuf->sel.si_pid && dbuf->fl >= dbuf->blocksize)
+ selwakeup(&dbuf->sel);
+}
diff --git a/sys/dev/sound/midi/midibuf.h b/sys/dev/sound/midi/midibuf.h
new file mode 100644
index 0000000..d40019d
--- /dev/null
+++ b/sys/dev/sound/midi/midibuf.h
@@ -0,0 +1,64 @@
+/*
+ * Include file for midi buffer.
+ *
+ * Copyright by Seigo Tanimura 1999.
+ *
+ * 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$
+ *
+ */
+
+/*
+ * descriptor of a midi buffer. See midibuf.c for documentation.
+ * (rp,rl) and (fp,fl) identify the READY and FREE regions of the
+ * buffer. dl contains the length used for dma transfer, dl>0 also
+ * means that the channel is busy and there is a DMA transfer in progress.
+ */
+
+typedef struct _midi_dbuf {
+ char *buf;
+ int bufsize ;
+ volatile int rp, fp; /* pointers to the ready and free area */
+ volatile int dl; /* transfer size */
+ volatile int rl, fl; /* length of ready and free areas. */
+ int int_count;
+ int chan; /* dma channel */
+ int unit_size ; /* unit size */
+ struct selinfo sel;
+ u_long total; /* total bytes processed */
+ u_long prev_total; /* copy of the above when GETxPTR called */
+ int tsleep_in, tsleep_out; /* pillows to tsleep on */
+ int blocksize; /* block size */
+} midi_dbuf ;
+
+/*
+ * These are the midi buffer methods, used in midi interface devices.
+ */
+int midibuf_init(midi_dbuf *dbuf);
+int midibuf_seqwrite(midi_dbuf *dbuf, u_char* data, int len);
+int midibuf_uiowrite(midi_dbuf *dbuf, struct uio *buf, int len);
+int midibuf_output_intr(midi_dbuf *dbuf, u_char *data, int len);
+int midibuf_input_intr(midi_dbuf *dbuf, u_char *data, int len);
+int midibuf_seqread(midi_dbuf *dbuf, u_char* data, int len);
+int midibuf_seqcopy(midi_dbuf *dbuf, u_char* data, int len);
+int midibuf_uioread(midi_dbuf *dbuf, struct uio *buf, int len);
diff --git a/sys/dev/sound/midi/midisynth.c b/sys/dev/sound/midi/midisynth.c
new file mode 100644
index 0000000..7d668fd
--- /dev/null
+++ b/sys/dev/sound/midi/midisynth.c
@@ -0,0 +1,632 @@
+/*
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS
+ * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * This is the interface for a sequencer to interact a midi driver.
+ * This interface translates the sequencer operations to the corresponding
+ * midi messages, and vice versa.
+ */
+
+#include "opt_devfs.h"
+
+#include <stddef.h>
+#include <dev/sound/midi/midi.h>
+
+#define TYPEDRANGE(type, x, lower, upper) \
+{ \
+ type tl, tu; \
+ tl = (lower); \
+ tu = (upper); \
+ if (x < tl) { \
+ x = tl; \
+ } else if(x > tu) { \
+ x = tu; \
+ } \
+}
+
+/*
+ * These functions goes into midisynthdev_op_desc.
+ */
+static mdsy_killnote_t synth_killnote;
+static mdsy_setinstr_t synth_setinstr;
+static mdsy_startnote_t synth_startnote;
+static mdsy_reset_t synth_reset;
+static mdsy_hwcontrol_t synth_hwcontrol;
+static mdsy_loadpatch_t synth_loadpatch;
+static mdsy_panning_t synth_panning;
+static mdsy_aftertouch_t synth_aftertouch;
+static mdsy_controller_t synth_controller;
+static mdsy_patchmgr_t synth_patchmgr;
+static mdsy_bender_t synth_bender;
+static mdsy_allocvoice_t synth_allocvoice;
+static mdsy_setupvoice_t synth_setupvoice;
+static mdsy_sendsysex_t synth_sendsysex;
+static mdsy_prefixcmd_t synth_prefixcmd;
+static mdsy_volumemethod_t synth_volumemethod;
+static mdsy_readraw_t synth_readraw;
+static mdsy_writeraw_t synth_writeraw;
+
+/*
+ * This is the synthdev_info for a midi interface device.
+ * You may have to replace a few of functions for an internal
+ * synthesizer.
+ */
+synthdev_info midisynth_op_desc = {
+ synth_killnote,
+ synth_setinstr,
+ synth_startnote,
+ synth_reset,
+ synth_hwcontrol,
+ synth_loadpatch,
+ synth_panning,
+ synth_aftertouch,
+ synth_controller,
+ synth_patchmgr,
+ synth_bender,
+ synth_allocvoice,
+ synth_setupvoice,
+ synth_sendsysex,
+ synth_prefixcmd,
+ synth_volumemethod,
+ synth_readraw,
+ synth_writeraw,
+};
+
+/* The following functions are local. */
+static int synth_leavesysex(mididev_info *md);
+
+/*
+ * Here are the main functions to interact to the midi sequencer.
+ * These are called from the sequencer functions in sys/i386/isa/snd/sequencer.c.
+ */
+
+static int
+synth_killnote(mididev_info *md, int chn, int note, int vel)
+{
+ int unit, msg, chp;
+ synthdev_info *sd;
+ u_char c[3];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (note < 0 || note > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+ TYPEDRANGE(int, vel, 0, 127);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ msg = sd->prev_out_status & 0xf0;
+ chp = sd->prev_out_status & 0x0f;
+
+ if (chp == chn && ((msg == 0x90 && vel == 64) || msg == 0x80)) {
+ /* Use running status. */
+ c[0] = (u_char)note;
+ if (msg == 0x90)
+ /* The note was on. */
+ c[1] = 0;
+ else
+ c[1] = (u_char)vel;
+
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
+ return (EAGAIN);
+ } else {
+ 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;
+ }
+
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
+ return EAGAIN;
+ /* Update the status. */
+ sd->prev_out_status = c[0];
+ }
+
+ return (0);
+}
+
+static int
+synth_setinstr(mididev_info *md, int chn, int instr)
+{
+ int unit;
+ synthdev_info *sd;
+ u_char c[2];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (instr < 0 || instr > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ c[0] = 0xc0 | (chn & 0x0f); /* Progamme change. */
+ c[1] = (u_char)instr;
+ if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
+ return (EAGAIN);
+ /* Update the status. */
+ sd->prev_out_status = c[0];
+
+ return (0);
+}
+
+static int
+synth_startnote(mididev_info *md, int chn, int note, int vel)
+{
+ int unit, msg, chp;
+ synthdev_info *sd;
+ u_char c[3];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (note < 0 || note > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+ TYPEDRANGE(int, vel, 0, 127);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ msg = sd->prev_out_status & 0xf0;
+ chp = sd->prev_out_status & 0x0f;
+
+ if (chp == chn && msg == 0x90) {
+ /* Use running status. */
+ c[0] = (u_char)note;
+ c[1] = (u_char)vel;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
+ return (EAGAIN);
+ } else {
+ c[0] = 0x90 | (chn & 0x0f); /* Note on. */
+ c[1] = (u_char)note;
+ c[2] = (u_char)vel;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
+ return (EAGAIN);
+ /* Update the status. */
+ sd->prev_out_status = c[0];
+ }
+
+ return (0);
+}
+
+static int
+synth_reset(mididev_info *md)
+{
+ synth_leavesysex(md);
+ return (0);
+}
+
+static int
+synth_hwcontrol(mididev_info *md, u_char *event)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_loadpatch(mididev_info *md, int format, struct uio *buf, int offs, int count, int pmgr_flag)
+{
+ struct sysex_info sysex;
+ synthdev_info *sd;
+ int unit, i, eox_seen, first_byte, left, src_offs, hdr_size;
+ u_char c[count];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ eox_seen = 0;
+ first_byte = 1;
+ hdr_size = offsetof(struct sysex_info, data);
+
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ if (synth_prefixcmd(md, 0xf0))
+ return (0);
+ if (format != SYSEX_PATCH) {
+ printf("synth_loadpatch: patch format 0x%x is invalid.\n", format);
+ return (EINVAL);
+ }
+ if (count < hdr_size) {
+ printf("synth_loadpatch: patch header is too short.\n");
+ return (EINVAL);
+ }
+ count -= hdr_size;
+
+ /* Copy the patch data. */
+ if (uiomove((caddr_t)&((char *)&sysex)[offs], hdr_size - offs, buf))
+ printf("synth_loadpatch: memory mangled?\n");
+
+ if (count < sysex.len) {
+ sysex.len = (long)count;
+ printf("synth_loadpatch: sysex record of %d bytes is too long, adjusted to %d bytes.\n", (int)sysex.len, count);
+ }
+ left = sysex.len;
+ src_offs = 0;
+
+ for (i = 0 ; i < left ; i++) {
+ uiomove((caddr_t)&c[i], 1, buf);
+ eox_seen = i > 0 && (c[i] & 0x80) != 0;
+ if (eox_seen && c[i] != 0xf7)
+ c[i] = 0xf7;
+ if (i == 0 && c[i] != 0x80) {
+ printf("synth_loadpatch: sysex does not begin with the status.\n");
+ return (EINVAL);
+ }
+ if (!first_byte && (c[i] & 0x80) != 0) {
+ md->synth.writeraw(md, c, i + 1, 0);
+ /* Update the status. */
+ sd->prev_out_status = c[i];
+ return (0);
+ }
+ first_byte = 0;
+ }
+
+ if (!eox_seen) {
+ c[0] = 0xf7;
+ md->synth.writeraw(md, c, 1, 0);
+ sd->prev_out_status = c[0];
+ }
+
+ return (0);
+}
+
+static int
+synth_panning(mididev_info *md, int chn, int pan)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_aftertouch(mididev_info *md, int chn, int press)
+{
+ int unit, msg, chp;
+ synthdev_info *sd;
+ u_char c[2];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (press < 0 || press > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ msg = sd->prev_out_status & 0xf0;
+ chp = sd->prev_out_status & 0x0f;
+
+ if (chp == chn && msg == 0xd0) {
+ /* Use running status. */
+ c[0] = (u_char)press;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 1, 1) == EAGAIN)
+ return (EAGAIN);
+ } else {
+ c[0] = 0xd0 | (chn & 0x0f); /* Channel Pressure. */
+ c[1] = (u_char)press;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
+ return (EAGAIN);
+ /* Update the status. */
+ sd->prev_out_status = c[0];
+ }
+
+ return (0);
+}
+
+static int
+synth_controller(mididev_info *md, int chn, int ctrlnum, int val)
+{
+ int unit, msg, chp;
+ synthdev_info *sd;
+ u_char c[3];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (ctrlnum < 1 || ctrlnum > 127 || chn < 0 || chn > 15)
+ return (EINVAL);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ msg = sd->prev_out_status & 0xf0;
+ chp = sd->prev_out_status & 0x0f;
+
+ if (chp == chn && msg == 0xb0) {
+ /* Use running status. */
+ c[0] = (u_char)ctrlnum;
+ c[1] = (u_char)val & 0x7f;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
+ return (EAGAIN);
+ } else {
+ c[0] = 0xb0 | (chn & 0x0f); /* Control Message. */
+ c[1] = (u_char)ctrlnum;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
+ return (EAGAIN);
+ /* Update the status. */
+ sd->prev_out_status = c[0];
+ }
+
+ return (0);
+}
+
+static int
+synth_patchmgr(mididev_info *md, struct patmgr_info *rec)
+{
+ return (EINVAL);
+}
+
+static int
+synth_bender(mididev_info *md, int chn, int val)
+{
+ int unit, msg, chp;
+ synthdev_info *sd;
+ u_char c[3];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (val < 0 || val > 16383 || chn < 0 || chn > 15)
+ return (EINVAL);
+ if (synth_leavesysex(md) == EAGAIN)
+ return (EAGAIN);
+
+ msg = sd->prev_out_status & 0xf0;
+ chp = sd->prev_out_status & 0x0f;
+
+ if (chp == chn && msg == 0xe0) {
+ /* Use running status. */
+ c[0] = (u_char)val & 0x7f;
+ c[1] = (u_char)(val >> 7) & 0x7f;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 2, 1) == EAGAIN)
+ return (EAGAIN);
+ } else {
+ c[0] = 0xe0 | (chn & 0x0f); /* Pitch bend. */
+ c[1] = (u_char)val & 0x7f;
+ c[2] = (u_char)(val >> 7) & 0x7f;
+ if (synth_prefixcmd(md, c[0]))
+ return (0);
+ if (md->synth.writeraw(md, c, 3, 1) == EAGAIN)
+ return (EAGAIN);
+ /* Update the status. */
+ sd->prev_out_status = c[0];
+ }
+
+ return (0);
+}
+
+static int
+synth_allocvoice(mididev_info *md, int chn, int note, struct voice_alloc_info *alloc)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_setupvoice(mididev_info *md, int voice, int chn)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_sendsysex(mididev_info *md, u_char *sysex, int len)
+{
+ int unit, i, j;
+ synthdev_info *sd;
+ u_char c[len];
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ for (i = 0 ; i < len ; i++) {
+ switch (sysex[i]) {
+ case 0xf0:
+ /* Sysex begins. */
+ if (synth_prefixcmd(md, 0xf0))
+ return (0);
+ sd->sysex_state = 1;
+ break;
+ case 0xf7:
+ /* Sysex ends. */
+ if (!sd->sysex_state)
+ return (0);
+ sd->sysex_state = 0;
+ break;
+ default:
+ if (!sd->sysex_state)
+ return (0);
+ if ((sysex[i] & 0x80) != 0) {
+ /* A status in a sysex? */
+ sysex[i] = 0xf7;
+ sd->sysex_state = 0;
+ }
+ break;
+ }
+ c[i] = sysex[i];
+ if (!sd->sysex_state)
+ break;
+ }
+ if (md->synth.writeraw(md, c, i, 1) == EAGAIN)
+ return (EAGAIN);
+
+ /* Update the status. */
+ for (j = i - 1 ; j >= 0 ; j--)
+ if ((c[j] & 0x80) != 0) {
+ sd->prev_out_status = c[j];
+ break;
+ }
+
+ return (0);
+}
+
+static int
+synth_prefixcmd(mididev_info *md, int status)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_volumemethod(mididev_info *md, int mode)
+{
+ /* NOP. */
+ return (0);
+}
+
+static int
+synth_readraw(mididev_info *md, u_char *buf, int len, int nonblock)
+{
+ int unit, ret, s;
+
+ if (md == NULL)
+ return (ENXIO);
+
+ unit = md->unit;
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("synth_readraw: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+ if ((md->fflags & FREAD) == 0) {
+ DEB(printf("mpu_readraw: unit %d is not for reading.\n", unit));
+ return (EIO);
+ }
+
+ s = splmidi();
+
+ /* Begin recording. */
+ md->callback(md, MIDI_CB_START | MIDI_CB_RD);
+
+ if (nonblock) {
+ /* Have we got enough data to read? */
+ if (md->midi_dbuf_in.rl < len)
+ return (EAGAIN);
+ }
+
+ ret = midibuf_seqread(&md->midi_dbuf_in, buf, len);
+
+ splx(s);
+
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+
+ return (ret);
+}
+
+static int
+synth_writeraw(mididev_info *md, u_char *buf, int len, int nonblock)
+{
+ int unit, ret, s;
+
+ if (md == NULL)
+ return (ENXIO);
+
+ unit = md->unit;
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("synth_writeraw: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+ if ((md->fflags & FWRITE) == 0) {
+ DEB(printf("synth_writeraw: unit %d is not for writing.\n", unit));
+ return (EIO);
+ }
+
+ /* For nonblocking, have we got enough space to write? */
+ if (nonblock && md->midi_dbuf_out.fl < len)
+ return (EAGAIN);
+
+ s = splmidi();
+
+ ret = midibuf_seqwrite(&md->midi_dbuf_out, buf, len);
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+
+ /* Begin playing. */
+ md->callback(md, MIDI_CB_START | MIDI_CB_WR);
+
+ splx(s);
+
+ return (ret);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+static int
+synth_leavesysex(mididev_info *md)
+{
+ int unit;
+ synthdev_info *sd;
+ u_char c;
+
+ unit = md->unit;
+ sd = &md->synth;
+
+ if (!sd->sysex_state)
+ return (0);
+
+ sd->sysex_state = 0;
+ c = 0xf7;
+ if (md->synth.writeraw(md, &c, sizeof(c), 1) == EAGAIN)
+ return (EAGAIN);
+ sd->sysex_state = 0;
+ /* Update the status. */
+ sd->prev_out_status = c;
+
+ return (0);
+}
diff --git a/sys/dev/sound/midi/midisynth.h b/sys/dev/sound/midi/midisynth.h
new file mode 100644
index 0000000..d27e62b
--- /dev/null
+++ b/sys/dev/sound/midi/midisynth.h
@@ -0,0 +1,97 @@
+/*
+ * include file for midi synthesizer interface.
+ *
+ * Copyright by Seigo Tanimura 1999.
+ *
+ * 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$
+ *
+ */
+
+#define SYNTH_MAX_VOICES 32
+
+/* This is the voice allocation state for a synthesizer. */
+struct voice_alloc_info {
+ int max_voice;
+ int used_voices;
+ int ptr; /* For device specific use */
+ u_short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */
+ int timestamp;
+ int alloc_times[SYNTH_MAX_VOICES];
+};
+
+/* This is the channel information for a synthesizer. */
+struct channel_info {
+ int pgm_num;
+ int bender_value;
+ u_char controllers[128];
+};
+
+/* These are the function types for a midi synthesizer interface. */
+typedef int (mdsy_killnote_t)(mididev_info *md, int chn, int note, int vel);
+typedef int (mdsy_setinstr_t)(mididev_info *md, int chn, int instr);
+typedef int (mdsy_startnote_t)(mididev_info *md, int chn, int note, int vel);
+typedef int (mdsy_reset_t)(mididev_info *md);
+typedef int (mdsy_hwcontrol_t)(mididev_info *md, u_char *event);
+typedef int (mdsy_loadpatch_t)(mididev_info *md, int format, struct uio *buf, int offs, int count, int pmgr_flag);
+typedef int (mdsy_panning_t)(mididev_info *md, int chn, int pan);
+typedef int (mdsy_aftertouch_t)(mididev_info *md, int chn, int press);
+typedef int (mdsy_controller_t)(mididev_info *md, int chn, int ctrlnum, int val);
+typedef int (mdsy_patchmgr_t)(mididev_info *md, struct patmgr_info *rec);
+typedef int (mdsy_bender_t)(mididev_info *md, int chn, int val);
+typedef int (mdsy_allocvoice_t)(mididev_info *md, int chn, int note, struct voice_alloc_info *alloc);
+typedef int (mdsy_setupvoice_t)(mididev_info *md, int voice, int chn);
+typedef int (mdsy_sendsysex_t)(mididev_info *md, u_char *sysex, int len);
+typedef int (mdsy_prefixcmd_t)(mididev_info *md, int status);
+typedef int (mdsy_volumemethod_t)(mididev_info *md, int mode);
+typedef int (mdsy_readraw_t)(mididev_info *md, u_char *buf, int len, int nonblock);
+typedef int (mdsy_writeraw_t)(mididev_info *md, u_char *buf, int len, int nonblock);
+
+/* This is a midi synthesizer interface and state. */
+struct _synthdev_info {
+ mdsy_killnote_t *killnote;
+ mdsy_setinstr_t *setinstr;
+ mdsy_startnote_t *startnote;
+ mdsy_reset_t *reset;
+ mdsy_hwcontrol_t *hwcontrol;
+ mdsy_loadpatch_t *loadpatch;
+ mdsy_panning_t *panning;
+ mdsy_aftertouch_t *aftertouch;
+ mdsy_controller_t *controller;
+ mdsy_patchmgr_t *patchmgr;
+ mdsy_bender_t *bender;
+ mdsy_allocvoice_t *allocvoice;
+ mdsy_setupvoice_t *setupvoice;
+ mdsy_sendsysex_t *sendsysex;
+ mdsy_prefixcmd_t *prefixcmd;
+ mdsy_volumemethod_t *volumemethod;
+ mdsy_readraw_t *readraw;
+ mdsy_writeraw_t *writeraw;
+
+ struct voice_alloc_info alloc; /* Voice allocation. */
+ struct channel_info chn_info[16]; /* Channel information. */
+
+ u_char prev_out_status; /* Previous status. */
+ int sysex_state; /* State of sysex transmission. */
+};
+typedef struct _synthdev_info synthdev_info;
diff --git a/sys/dev/sound/midi/miditypes.h b/sys/dev/sound/midi/miditypes.h
new file mode 100644
index 0000000..c0dfdf6
--- /dev/null
+++ b/sys/dev/sound/midi/miditypes.h
@@ -0,0 +1,34 @@
+/*
+ * Include file for type definitions in midi driver.
+ *
+ * Copyright by Seigo Tanimura 1999.
+ *
+ * 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$
+ *
+ */
+
+typedef struct _mididev_info mididev_info;
+
+typedef int (midi_callback_t)(mididev_info *d, int reason);
+typedef void (midi_intr_t)(void *p, mididev_info *md);
diff --git a/sys/dev/sound/midi/sequencer.c b/sys/dev/sound/midi/sequencer.c
new file mode 100644
index 0000000..ba6a21d
--- /dev/null
+++ b/sys/dev/sound/midi/sequencer.c
@@ -0,0 +1,2040 @@
+/*
+ * The sequencer personality manager.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * This is the newmidi sequencer driver. This driver handles io against
+ * /dev/sequencer, midi input and output event queues and event transmittion
+ * to and from a midi device or synthesizer.
+ */
+
+#include "opt_devfs.h"
+
+#include <dev/sound/midi/midi.h>
+#include <dev/sound/midi/sequencer.h>
+
+#ifndef DDB
+#define DDB(x)
+#endif /* DDB */
+
+#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
+ synthesizer and MIDI output) */
+#define SND_DEV_MIDIN 2 /* Raw midi access */
+#define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */
+
+#define MIDIDEV_MODE 0x2000
+
+/* Length of a sequencer event. */
+#define EV_SZ 8
+#define IEV_SZ 8
+
+/* Return value from seq_playevent and the helpers. */
+enum {
+ MORE,
+ TIMERARMED,
+ QUEUEFULL
+};
+
+/*
+ * These functions goes into seq_op_desc to get called
+ * from sound.c.
+ */
+
+static midi_intr_t seq_intr;
+static seq_callback_t seq_callback;
+
+/* 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;
+
+/*
+ * This is the device descriptor for the midi sequencer.
+ */
+seqdev_info seq_op_desc = {
+ "midi sequencer",
+
+ 0,
+
+ seq_open,
+ seq_close,
+ seq_read,
+ seq_write,
+ seq_ioctl,
+ seq_poll,
+
+ seq_callback,
+
+ SEQ_BUFFSIZE, /* Queue Length */
+
+ 0, /* XXX This is not an *audio* device! */
+};
+
+/* Here is the parameter structure per a device. */
+struct seq_softc {
+ seqdev_info *devinfo; /* sequencer device information */
+
+ int fflags; /* Access mode */
+
+ u_long seq_time; /* The beggining time of this sequence */
+ u_long prev_event_time; /* The time of the previous event output */
+ u_long prev_input_time; /* The time of the previous event input */
+ u_long prev_wakeup_time; /* The time of the previous wakeup */
+ struct callout_handle timeout_ch; /* Timer callout handler */
+ long timer_current; /* Current timer value */
+ int timer_running; /* State of timer */
+ int midi_open[NMIDI_MAX]; /* State of midi devices. */
+ int pending_timer; /* Timer change operation */
+ int output_threshould; /* Sequence output threshould */
+ int pre_event_timeout; /* Time to wait event input */
+ int queueout_pending; /* Pending for the output queue */
+ snd_sync_parm sync_parm; /* AIOSYNC parameter set */
+ struct proc *sync_proc; /* AIOSYNCing process */
+};
+
+typedef struct seq_softc *sc_p;
+
+static d_open_t seqopen;
+static d_close_t seqclose;
+static d_ioctl_t seqioctl;
+static d_read_t seqread;
+static d_write_t seqwrite;
+static d_poll_t seqpoll;
+
+#define CDEV_MAJOR SEQ_CDEV_MAJOR
+static struct cdevsw seq_cdevsw = {
+ /* open */ seqopen,
+ /* close */ seqclose,
+ /* read */ seqread,
+ /* write */ seqwrite,
+ /* ioctl */ seqioctl,
+ /* poll */ seqpoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "midi", /* XXX */
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+ /* bmaj */ -1
+};
+
+seqdev_info seq_info[NSEQ_MAX] ;
+static int seq_info_inited;
+u_long nseq = NSEQ_MAX; /* total number of sequencers */
+
+/* The followings are the local function. */
+static int seq_init(void);
+static int seq_queue(sc_p scp, u_char *note);
+static void seq_startplay(sc_p scp);
+static int seq_playevent(sc_p scp, u_char *event);
+static u_long seq_gettime(void);
+static int seq_requesttimer(sc_p scp, int delay);
+static void seq_stoptimer(sc_p scp);
+static void seq_midiinput(sc_p scp, mididev_info *md);
+static int seq_copytoinput(sc_p scp, u_char *event, int len);
+static int seq_extended(sc_p scp, u_char *event);
+static int seq_chnvoice(sc_p scp, u_char *event);
+static int seq_findvoice(mididev_info *md, int chn, int note);
+static int seq_allocvoice(mididev_info *md, int chn, int note);
+static int seq_chncommon(sc_p scp, u_char *event);
+static int seq_timing(sc_p scp, u_char *event);
+static int seq_local(sc_p scp, u_char *event);
+static int seq_sysex(sc_p scp, u_char *event);
+static void seq_timer(void *arg);
+static int seq_reset(sc_p scp);
+static int seq_openmidi(sc_p scp, mididev_info *md, int flags, int mode, struct proc *p);
+static int seq_closemidi(sc_p scp, mididev_info *md, int flags, int mode, struct proc *p);
+static void seq_panic(sc_p scp);
+static int seq_sync(sc_p scp);
+
+static seqdev_info * get_seqdev_info(dev_t i_dev, int *unit);
+
+/*
+ * Here are the main functions to interact to the user process.
+ * These are called from snd* functions in sys/i386/isa/snd/sound.c.
+ */
+
+static int
+seq_init(void)
+{
+ int unit;
+ sc_p scp;
+ seqdev_info *devinfo;
+
+ DEB(printf("seq: initing.\n"));
+
+ /* Have we already inited? */
+ if (seq_info_inited)
+ return (1);
+
+ for (unit = 0 ; unit < nseq ; unit++) {
+ /* Allocate the softc. */
+ scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT);
+ if (scp == (sc_p)NULL) {
+ printf("seq%d: softc allocation failed.\n", unit);
+ return (1);
+ }
+ bzero(scp, sizeof(*scp));
+
+ /* Fill the softc and the seq_info for this unit. */
+ scp->seq_time = seq_gettime();
+ scp->prev_event_time = 0;
+ scp->prev_input_time = 0;
+ scp->prev_wakeup_time = scp->seq_time;
+ callout_handle_init(&scp->timeout_ch);
+ scp->timer_current = 0;
+ scp->timer_running = 0;
+ scp->queueout_pending = 0;
+
+ scp->devinfo = devinfo = &seq_info[unit];
+ bcopy(&seq_op_desc, devinfo, sizeof(seq_op_desc));
+ devinfo->unit = unit;
+ devinfo->softc = scp;
+ devinfo->flags = 0;
+ devinfo->midi_dbuf_in.unit_size = devinfo->midi_dbuf_out.unit_size = EV_SZ;
+ midibuf_init(&devinfo->midi_dbuf_in);
+ midibuf_init(&devinfo->midi_dbuf_out);
+
+ make_dev(&seq_cdevsw, MIDIMKMINOR(unit, SND_DEV_SEQ),
+ UID_ROOT, GID_WHEEL, 0666, "sequencer%d", unit);
+ }
+
+ /* We have inited. */
+ seq_info_inited = 1;
+
+ if (nseq == 1)
+ printf("seq0: Midi sequencer.\n");
+ else
+ printf("seq0-%lu: Midi sequencers.\n", nseq - 1);
+
+ DEB(printf("seq: inited.\n"));
+
+ return (0);
+}
+
+int
+seq_open(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ int unit, s, midiunit;
+ sc_p scp;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("seq%d: opening.\n", unit));
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_open: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_open: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+
+ s = splmidi();
+ /* Mark this device busy. */
+ if ((sd->flags & SEQ_F_BUSY) != 0) {
+ splx(s);
+ DEB(printf("seq_open: unit %d is busy.\n", unit));
+ return (EBUSY);
+ }
+ sd->flags |= SEQ_F_BUSY;
+ sd->flags &= ~(SEQ_F_READING | SEQ_F_WRITING);
+ scp->fflags = flags;
+
+ /* Init the queue. */
+ midibuf_init(&sd->midi_dbuf_in);
+ midibuf_init(&sd->midi_dbuf_out);
+
+ /* Init timestamp. */
+ scp->seq_time = seq_gettime();
+ scp->prev_event_time = 0;
+ scp->prev_input_time = 0;
+ scp->prev_wakeup_time = scp->seq_time;
+
+ splx(s);
+
+ /* Open midi devices. */
+ for (midiunit = 0 ; midiunit < nmidi + nsynth ; midiunit++) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md))
+ seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, p);
+ }
+
+ DEB(printf("seq%d: opened.\n", unit));
+
+ return (0);
+}
+
+int
+seq_close(dev_t i_dev, int flags, int mode, struct proc *p)
+{
+ int unit, s, i;
+ sc_p scp;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("seq%d: closing.\n", unit));
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_close: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_close: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+
+ s = splmidi();
+
+ if (!(sd->flags & MIDI_F_NBIO))
+ seq_sync(scp);
+
+ /* Stop the timer. */
+ seq_stoptimer(scp);
+
+ /* Reset the sequencer. */
+ seq_reset(scp);
+ seq_sync(scp);
+
+ /* Clean up the midi device. */
+ for (i = 0 ; i < nmidi + nsynth ; i++) {
+ md = &midi_info[i];
+ if (MIDICONFED(md))
+ seq_closemidi(scp, md, scp->fflags, MIDIDEV_MODE, p);
+ }
+
+ /* Stop playing and unmark this device busy. */
+ sd->flags &= ~(SEQ_F_BUSY | SEQ_F_READING | SEQ_F_WRITING);
+
+ splx(s);
+
+ DEB(printf("seq%d: closed.\n", unit));
+
+ return (0);
+}
+
+int
+seq_read(dev_t i_dev, struct uio *buf, int flag)
+{
+ int unit, ret, s, len;
+ sc_p scp;
+ seqdev_info *sd;
+
+ unit = MIDIUNIT(i_dev);
+
+ /*DEB(printf("seq%d: reading.\n", unit));*/
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_read: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_read: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+ if ((scp->fflags & FREAD) == 0) {
+ DEB(printf("seq_read: unit %d is not for reading.\n", unit));
+ return (EIO);
+ }
+
+ s = splmidi();
+
+ /* Begin recording. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_RD);
+
+ /* Have we got the data to read? */
+ if ((sd->flags & SEQ_F_NBIO) != 0 && sd->midi_dbuf_in.rl == 0)
+ ret = EAGAIN;
+ else {
+ len = buf->uio_resid;
+ ret = midibuf_uioread(&sd->midi_dbuf_in, buf, len);
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+ }
+ splx(s);
+
+ return (ret);
+}
+
+int
+seq_write(dev_t i_dev, struct uio *buf, int flag)
+{
+ u_char event[EV_SZ], ev_code;
+ int unit, count, countorg, midiunit, ev_size, p, ret, s;
+ sc_p scp;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ unit = MIDIUNIT(i_dev);
+
+ /*DEB(printf("seq%d: writing.\n", unit));*/
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_write: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_write: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+ if ((scp->fflags & FWRITE) == 0) {
+ DEB(printf("seq_write: unit %d is not for writing.\n", unit));
+ return (EIO);
+ }
+
+ p = 0;
+ countorg = buf->uio_resid;
+ count = countorg;
+
+ s = splmidi();
+ /* Begin playing. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ splx(s);
+
+ /* Pick up an event. */
+ while (count >= 4) {
+ if (uiomove((caddr_t)event, 4, buf))
+ printf("seq_write: user memory mangled?\n");
+ ev_code = event[0];
+
+ /* Have a look at the event code. */
+ if (ev_code == SEQ_FULLSIZE) {
+
+ /* A long event, these are the patches/samples for a synthesizer. */
+ midiunit = *(u_short *)&event[2];
+ if (midiunit < 0 || midiunit >= nmidi + nsynth)
+ return (ENXIO);
+ md = &midi_info[midiunit];
+ if (!MIDICONFED(md))
+ return (ENXIO);
+ s = splmidi();
+ if ((md->flags & MIDI_F_BUSY) == 0
+ && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0) {
+ splx(s);
+ return (ENXIO);
+ }
+
+ DEB(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);
+ splx(s);
+ return (ret);
+ }
+
+ if (ev_code >= 128) {
+
+ /* Some sort of an extended event. The size is eight bytes. */
+#if notyet
+ if (scp->seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED) {
+ printf("seq%d: invalid level two event %x.\n", unit, ev_code);
+ return (EINVAL);
+ }
+#endif /* notyet */
+ ev_size = 8;
+
+ if (count < ev_size) {
+ /* No more data. Start playing now. */
+ s = splmidi();
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ splx(s);
+
+ return (0);
+ }
+ if (uiomove((caddr_t)&event[4], 4, buf))
+ printf("seq_write: user memory mangled?\n");
+ } else {
+
+ /* Not an extended event. The size is four bytes. */
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ printf("seq%d: four byte event in level two mode.\n", unit);
+ return (EINVAL);
+ }
+#endif /* notyet */
+ ev_size = 4;
+ }
+ if (ev_code == SEQ_MIDIPUTC) {
+ /* An event passed to the midi device itself. */
+ midiunit = event[2];
+ if (midiunit < 0 || midiunit >= nmidi + nsynth)
+ return (ENXIO);
+ md = &midi_info[midiunit];
+ if (!MIDICONFED(md))
+ return (ENXIO);
+ if ((md->flags & MIDI_F_BUSY) == 0
+ && (ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc)) != 0) {
+ seq_reset(scp);
+ return (ret);
+ }
+ }
+
+ /*DEB(printf("seq_write: queueing event %d.\n", event[0]));*/
+ /* Now we queue the event. */
+ switch (seq_queue(scp, event)) {
+ case EAGAIN:
+ s = splmidi();
+ /* The queue is full. Start playing now. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ splx(s);
+ return (0);
+ case EINTR:
+ return (EINTR);
+ case ERESTART:
+ return (ERESTART);
+ }
+ p += ev_size;
+ count -= ev_size;
+ }
+
+ /* We have written every single data. Start playing now. */
+ s = splmidi();
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ splx(s);
+
+ return (0);
+}
+
+int
+seq_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p)
+{
+ int unit, midiunit, ret, tmp, s, arg2;
+ sc_p scp;
+ seqdev_info *sd;
+ mididev_info *md;
+ struct synth_info *synthinfo;
+ struct midi_info *midiinfo;
+ struct patmgr_info *patinfo;
+ snd_sync_parm *syncparm;
+ struct seq_event_rec *event;
+ struct snd_size *sndsize;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("seq%d: ioctlling, cmd 0x%x.\n", unit, (int)cmd));
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_ioctl: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_ioctl: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+
+ ret = 0;
+
+ switch (cmd) {
+
+ /*
+ * we start with the new ioctl interface.
+ */
+ case AIONWRITE: /* how many bytes can be written ? */
+ *(int *)arg = sd->midi_dbuf_out.fl;
+ break;
+
+ case AIOSSIZE: /* set the current blocksize */
+ sndsize = (struct snd_size *)arg;
+ if (sndsize->play_size <= sd->midi_dbuf_out.unit_size && sndsize->rec_size <= sd->midi_dbuf_in.unit_size) {
+ sd->flags &= ~MIDI_F_HAS_SIZE;
+ sd->midi_dbuf_out.blocksize = sd->midi_dbuf_out.unit_size;
+ sd->midi_dbuf_in.blocksize = sd->midi_dbuf_in.unit_size;
+ }
+ else {
+ if (sndsize->play_size > sd->midi_dbuf_out.bufsize / 4)
+ sndsize->play_size = sd->midi_dbuf_out.bufsize / 4;
+ if (sndsize->rec_size > sd->midi_dbuf_in.bufsize / 4)
+ sndsize->rec_size = sd->midi_dbuf_in.bufsize / 4;
+ /* Round up the size to the multiple of EV_SZ. */
+ sd->midi_dbuf_out.blocksize =
+ ((sndsize->play_size + sd->midi_dbuf_out.unit_size - 1)
+ / sd->midi_dbuf_out.unit_size) * sd->midi_dbuf_out.unit_size;
+ sd->midi_dbuf_in.blocksize =
+ ((sndsize->rec_size + sd->midi_dbuf_in.unit_size - 1)
+ / sd->midi_dbuf_in.unit_size) * sd->midi_dbuf_in.unit_size;
+ sd->flags |= MIDI_F_HAS_SIZE;
+ }
+ /* FALLTHROUGH */
+ case AIOGSIZE: /* get the current blocksize */
+ sndsize = (struct snd_size *)arg;
+ sndsize->play_size = sd->midi_dbuf_out.blocksize;
+ sndsize->rec_size = sd->midi_dbuf_in.blocksize;
+
+ ret = 0;
+ break;
+
+ case AIOSTOP:
+ if (*(int *)arg == AIOSYNC_PLAY) {
+ s = splmidi();
+
+ /* Stop writing. */
+ sd->callback(sd, SEQ_CB_ABORT | SEQ_CB_WR);
+
+ /* Pass the ioctl to the midi devices. */
+ for (midiunit = 0 ; midiunit < nmidi + nsynth ; midiunit++) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md) && scp->midi_open[midiunit] && (md->flags & MIDI_F_WRITING) != 0) {
+ arg2 = *(int *)arg;
+ midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, (caddr_t)&arg2, mode, p);
+ }
+ }
+
+ *(int *)arg = sd->midi_dbuf_out.rl;
+ splx(s);
+ }
+ else if (*(int *)arg == AIOSYNC_CAPTURE) {
+ s = splmidi();
+
+ /* Stop reading. */
+ sd->callback(sd, SEQ_CB_ABORT | SEQ_CB_RD);
+
+ /* Pass the ioctl to the midi devices. */
+ for (midiunit = 0 ; midiunit < nmidi + nsynth ; midiunit++) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md) && scp->midi_open[midiunit] && (md->flags & MIDI_F_WRITING) != 0) {
+ arg2 = *(int *)arg;
+ midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, (caddr_t)&arg2, mode, p);
+ }
+ }
+
+ *(int *)arg = sd->midi_dbuf_in.rl;
+ splx(s);
+ }
+
+ ret = 0;
+ break;
+
+ case AIOSYNC:
+ syncparm = (snd_sync_parm *)arg;
+ scp->sync_parm = *syncparm;
+
+ /* XXX Should select(2) against us watch the blocksize, or sync_parm? */
+
+ ret = 0;
+ break;
+
+ case SNDCTL_TMR_TIMEBASE:
+ case SNDCTL_TMR_TEMPO:
+ case SNDCTL_TMR_START:
+ case SNDCTL_TMR_STOP:
+ case SNDCTL_TMR_CONTINUE:
+ case SNDCTL_TMR_METRONOME:
+ case SNDCTL_TMR_SOURCE:
+#if notyet
+ if (scp->seq_mode != SEQ_2) {
+ ret = EINVAL;
+ break;
+ }
+ ret = tmr->ioctl(tmr_no, cmd, arg);
+#endif /* notyet */
+ break;
+ case SNDCTL_TMR_SELECT:
+#if notyet
+ if (scp->seq_mode != SEQ_2) {
+ ret = EINVAL;
+ break;
+ }
+#endif /* notyet */
+ scp->pending_timer = *(int *)arg;
+ if (scp->pending_timer < 0 || scp->pending_timer >= /*NTIMER*/1) {
+ scp->pending_timer = -1;
+ ret = EINVAL;
+ break;
+ }
+ *(int *)arg = scp->pending_timer;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_PANIC:
+ seq_panic(scp);
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_SYNC:
+ if (mode == O_RDONLY) {
+ ret = 0;
+ break;
+ }
+ ret = seq_sync(scp);
+ break;
+ case SNDCTL_SEQ_RESET:
+ seq_reset(scp);
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_TESTMIDI:
+ midiunit = *(int *)arg;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md) && !scp->midi_open[midiunit]) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ break;
+ }
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_GETINCOUNT:
+ if (mode == O_WRONLY)
+ *(int *)arg = 0;
+ else
+ *(int *)arg = sd->midi_dbuf_in.rl;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_GETOUTCOUNT:
+ if (mode == O_RDONLY)
+ *(int *)arg = 0;
+ else
+ *(int *)arg = sd->midi_dbuf_out.fl;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_CTRLRATE:
+#if notyet
+ if (scp->seq_mode != SEQ_2) {
+ ret = tmr->ioctl(tmr_no, cmd, arg);
+ break;
+ }
+#endif /* notyet */
+ if (*(int *)arg != 0) {
+ ret = EINVAL;
+ break;
+ }
+ *(int *)arg = hz;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_RESETSAMPLES:
+ midiunit = *(int *)arg;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_SEQ_NRSYNTHS:
+ *(int *)arg = nmidi + nsynth;
+ ret = 0;
+ break;
+ case SNDCTL_SEQ_NRMIDIS:
+ *(int *)arg = nmidi + nsynth;
+ ret = 0;
+ break;
+ case SNDCTL_SYNTH_MEMAVL:
+ midiunit = *(int *)arg;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_FM_4OP_ENABLE:
+ midiunit = *(int *)arg;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_SYNTH_INFO:
+ synthinfo = (struct synth_info *)arg;
+ midiunit = synthinfo->device;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_SEQ_OUTOFBAND:
+ event = (struct seq_event_rec *)arg;
+ s = splmidi();
+ ret = seq_playevent(scp, event->arr);
+ splx(s);
+ break;
+ case SNDCTL_MIDI_INFO:
+ midiinfo = (struct midi_info *)arg;
+ midiunit = midiinfo->device;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_PMGR_IFACE:
+ patinfo = (struct patmgr_info *)arg;
+ midiunit = patinfo->device;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ ret = EBUSY;
+ break;
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_PMGR_ACCESS:
+ patinfo = (struct patmgr_info *)arg;
+ midiunit = patinfo->device;
+ if (midiunit >= nmidi + nsynth) {
+ ret = ENXIO;
+ break;
+ }
+ if (!scp->midi_open[midiunit]) {
+ md = &midi_info[midiunit];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), midiunit, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ case SNDCTL_SEQ_THRESHOLD:
+ tmp = *(int *)arg;
+ RANGE(tmp, 1, sd->midi_dbuf_out.bufsize - 1);
+ scp->output_threshould = tmp;
+ ret = 0;
+ break;
+ case SNDCTL_MIDI_PRETIME:
+ tmp = *(int *)arg;
+ if (tmp < 0)
+ tmp = 0;
+ tmp = (hz * tmp) / 10;
+ scp->pre_event_timeout = tmp;
+ ret = 0;
+ break;
+ default:
+ if (scp->fflags == O_RDONLY) {
+ ret = EIO;
+ break;
+ }
+ if (!scp->midi_open[0]) {
+ md = &midi_info[0];
+ if (MIDICONFED(md)) {
+ ret = seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ if (ret != 0)
+ break;
+ } else {
+ ret = EBUSY;
+ break;
+ }
+ }
+ ret = midi_ioctl(MIDIMKDEV(major(i_dev), 0, SND_DEV_MIDIN), cmd, arg, mode, p);
+ break;
+ }
+
+ return (ret);
+}
+
+int
+seq_poll(dev_t i_dev, int events, struct proc *p)
+{
+ int unit, ret, s, lim;
+ sc_p scp;
+ seqdev_info *sd;
+
+ unit = MIDIUNIT(i_dev);
+
+ DEB(printf("seq%d: polling.\n", unit));
+
+ if (unit >= NSEQ_MAX) {
+ DEB(printf("seq_poll: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+ sd = get_seqdev_info(i_dev, &unit);
+ if (sd == NULL) {
+ DEB(printf("seq_poll: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+
+ ret = 0;
+ s = splmidi();
+
+ /* Look up the apropriate queue and select it. */
+ if ((events & (POLLOUT | POLLWRNORM)) != 0) {
+ /* Start playing. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+
+ /* Find out the boundary. */
+ if ((sd->flags & SEQ_F_HAS_SIZE) != 0)
+ lim = sd->midi_dbuf_out.blocksize;
+ else
+ lim = sd->midi_dbuf_out.unit_size;
+ if (sd->midi_dbuf_out.fl < lim)
+ /* No enough space, record select. */
+ selrecord(p, &sd->midi_dbuf_out.sel);
+ else
+ /* We can write now. */
+ ret |= events & (POLLOUT | POLLWRNORM);
+ }
+ if ((events & (POLLIN | POLLRDNORM)) != 0) {
+ /* Start recording. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_RD);
+
+ /* Find out the boundary. */
+ if ((sd->flags & SEQ_F_HAS_SIZE) != 0)
+ lim = sd->midi_dbuf_in.blocksize;
+ else
+ lim = sd->midi_dbuf_in.unit_size;
+ if (sd->midi_dbuf_in.rl < lim)
+ /* No data ready, record select. */
+ selrecord(p, &sd->midi_dbuf_in.sel);
+ else
+ /* We can write now. */
+ ret |= events & (POLLIN | POLLRDNORM);
+ }
+ splx(s);
+
+ return (ret);
+}
+
+static void
+seq_intr(void *p, mididev_info *md)
+{
+ sc_p scp;
+ seqdev_info *sd;
+
+ sd = (seqdev_info *)p;
+ scp = sd->softc;
+
+ /* Restart playing if we have the data to output. */
+ if (scp->queueout_pending)
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ /* Check the midi device if we are reading. */
+ if ((sd->flags & SEQ_F_READING) != 0)
+ seq_midiinput(scp, md);
+}
+
+static int
+seq_callback(seqdev_info *sd, int reason)
+{
+ int unit;
+ sc_p scp;
+
+ /*DEB(printf("seq_callback: reason 0x%x.\n", reason));*/
+
+ if (sd == NULL) {
+ DEB(printf("seq_callback: device not configured.\n"));
+ return (ENXIO);
+ }
+ scp = sd->softc;
+ unit = sd->unit;
+
+ switch (reason & SEQ_CB_REASON_MASK) {
+ case SEQ_CB_START:
+ if ((reason & SEQ_CB_RD) != 0 && (sd->flags & SEQ_F_READING) == 0)
+ /* Begin recording. */
+ sd->flags |= SEQ_F_READING;
+ if ((reason & SEQ_CB_WR) != 0 && (sd->flags & SEQ_F_WRITING) == 0)
+ /* Start playing. */
+ seq_startplay(scp);
+ break;
+ case SEQ_CB_STOP:
+ case SEQ_CB_ABORT:
+ if ((reason & SEQ_CB_RD) != 0 && (sd->flags & SEQ_F_READING) != 0) {
+ /* Stop the timer. */
+ scp->seq_time = seq_gettime();
+ scp->prev_input_time = 0;
+
+ /* Stop recording. */
+ sd->flags &= ~SEQ_F_READING;
+ }
+ if ((reason & SEQ_CB_WR) != 0 && (sd->flags & SEQ_F_WRITING) != 0) {
+ /* Stop the timer. */
+ seq_stoptimer(scp);
+ scp->seq_time = seq_gettime();
+ scp->prev_event_time = 0;
+
+ /* Stop Playing. */
+ sd->flags &= ~SEQ_F_WRITING;
+ scp->queueout_pending = 0;
+ }
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+static int
+seq_queue(sc_p scp, u_char *note)
+{
+ int unit, err, s;
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ /*DEB(printf("seq%d: queueing.\n", unit));*/
+
+ s = splmidi();
+
+ /* Start playing if we have some data in the queue. */
+ if (sd->midi_dbuf_out.rl >= EV_SZ)
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+
+ if (sd->midi_dbuf_out.fl < EV_SZ) {
+ /* We have no space. Start playing if not yet. */
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+ if ((sd->flags & SEQ_F_NBIO) != 0 && sd->midi_dbuf_out.fl < EV_SZ) {
+ /* We would block. */
+ splx(s);
+ return (EAGAIN);
+ } else
+ while (sd->midi_dbuf_out.fl < EV_SZ) {
+ /* We have no space. Good night. */
+ err = tsleep(&sd->midi_dbuf_out.tsleep_out, PRIBIO | PCATCH, "seqque", 0);
+ if (err == EINTR)
+ sd->callback(sd, SEQ_CB_STOP | SEQ_CB_WR);
+ if (err == EINTR || err == ERESTART) {
+ splx(s);
+ return (err);
+ }
+ }
+ }
+
+ /* We now have enough space to write. */
+ err = midibuf_seqwrite(&sd->midi_dbuf_out, note, EV_SZ);
+
+ splx(s);
+
+ if (err < 0)
+ err = -err;
+ else
+ err = 0;
+
+ return (err);
+}
+
+static void
+seq_startplay(sc_p scp)
+{
+ int unit;
+ u_char event[EV_SZ];
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ /* Dequeue the events to play. */
+ while (sd->midi_dbuf_out.rl >= EV_SZ) {
+
+ /* We are playing now. */
+ sd->flags |= SEQ_F_WRITING;
+
+ /* We only copy the event, not dequeue. */
+ midibuf_seqcopy(&sd->midi_dbuf_out, event, EV_SZ);
+
+ switch (seq_playevent(scp, event)) {
+ case TIMERARMED:
+ /* Dequeue the event. */
+ midibuf_seqread(&sd->midi_dbuf_out, event, EV_SZ);
+ /* FALLTHRU */
+ case QUEUEFULL:
+ /* We cannot play further. */
+ return;
+ case MORE:
+ /* Dequeue the event. */
+ midibuf_seqread(&sd->midi_dbuf_out, event, EV_SZ);
+ break;
+ }
+ }
+
+ /* Played every event in the queue. */
+ sd->flags &= ~SEQ_F_WRITING;
+}
+
+static int
+seq_playevent(sc_p scp, u_char *event)
+{
+ int unit, ret;
+ long *delay;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ md = &midi_info[0];
+ if (!MIDICONFED(md))
+ return (MORE);
+
+ switch(event[0]) {
+ case SEQ_NOTEOFF:
+ if ((md->flags & MIDI_F_BUSY) != 0 || seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) == 0)
+ if (md->synth.killnote(md, event[1], 255, event[3]) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ ret = MORE;
+ break;
+ case SEQ_NOTEON:
+ if (((md->flags & MIDI_F_BUSY) != 0 || seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) == 0)
+ && (event[4] < 128 || event[4] == 255))
+ if (md->synth.startnote(md, event[1], event[2], event[3]) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ ret = MORE;
+ break;
+ case SEQ_WAIT:
+
+ /* Extract the delay. */
+ delay = (long *)event;
+ *delay = (*delay >> 8) & 0xffffff;
+ if (*delay > 0) {
+ /* Arm the timer. */
+ sd->flags |= SEQ_F_WRITING;
+ scp->prev_event_time = *delay;
+ if (seq_requesttimer(scp, *delay)) {
+ ret = TIMERARMED;
+ break;
+ }
+ }
+ ret = MORE;
+ break;
+ case SEQ_PGMCHANGE:
+ if ((md->flags & MIDI_F_BUSY) != 0 || seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) == 0)
+ if (md->synth.setinstr(md, event[1], event[2]) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ ret = MORE;
+ break;
+ case SEQ_SYNCTIMER:
+ /* Reset the timer. */
+ scp->seq_time = seq_gettime();
+ scp->prev_input_time = 0;
+ scp->prev_event_time = 0;
+ scp->prev_wakeup_time = scp->seq_time;
+ ret = MORE;
+ break;
+ case SEQ_MIDIPUTC:
+ /* Pass through to the midi device. */
+ if (event[2] < nmidi + nsynth) {
+ md = &midi_info[event[2]];
+ if (MIDICONFED(md) && ((md->flags & MIDI_F_BUSY) != 0 || seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) == 0)) {
+ if (md->synth.writeraw(md, &event[1], sizeof(event[1]), 1) == EAGAIN) {
+ /* The queue was full. Try again later. */
+ ret = QUEUEFULL;
+ break;
+ }
+ }
+ }
+ ret = MORE;
+ break;
+ case SEQ_ECHO:
+ /* Echo this event back. */
+ if (seq_copytoinput(scp, event, 4) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ ret = MORE;
+ break;
+ case SEQ_PRIVATE:
+ if (event[1] < nmidi + nsynth) {
+ md = &midi_info[event[1]];
+ if (MIDICONFED(md) && md->synth.hwcontrol(md, event) == EAGAIN) {
+ ret = QUEUEFULL;
+ break;
+ }
+ }
+ ret = MORE;
+ break;
+ case SEQ_EXTENDED:
+ ret = seq_extended(scp, event);
+ break;
+ case EV_CHN_VOICE:
+ ret = seq_chnvoice(scp, event);
+ break;
+ case EV_CHN_COMMON:
+ ret = seq_chncommon(scp, event);
+ break;
+ case EV_TIMING:
+ ret = seq_timing(scp, event);
+ break;
+ case EV_SEQ_LOCAL:
+ ret = seq_local(scp, event);
+ break;
+ case EV_SYSEX:
+ ret = seq_sysex(scp, event);
+ break;
+ default:
+ ret = MORE;
+ break;
+ }
+
+ switch (ret) {
+ case QUEUEFULL:
+ /*DEB(printf("seq_playevent: the queue is full.\n"));*/
+ /* The queue was full. Try again on the interrupt by the midi device. */
+ sd->flags |= SEQ_F_WRITING;
+ scp->queueout_pending = 1;
+ break;
+ case TIMERARMED:
+ sd->flags |= SEQ_F_WRITING;
+ /* FALLTHRU */
+ case MORE:
+ scp->queueout_pending = 0;
+ break;
+ }
+
+ return (ret);
+}
+
+static u_long
+seq_gettime(void)
+{
+ struct timeval timecopy;
+
+ getmicrotime(&timecopy);
+ return timecopy.tv_usec / (1000000 / hz) + (u_long) timecopy.tv_sec * hz;
+}
+
+static int
+seq_requesttimer(sc_p scp, int delay)
+{
+ u_long cur_time, rel_base;
+
+ /*DEB(printf("seq%d: requested timer at delay of %d.\n", unit, delay));*/
+
+ cur_time = seq_gettime();
+
+ if (delay < 0)
+ /* Request a new timer. */
+ delay = -delay;
+ else {
+ rel_base = cur_time - scp->seq_time;
+ if (delay <= rel_base)
+ return 0;
+ delay -= rel_base;
+ }
+
+#if notdef
+ /*
+ * Compensate the delay of midi message transmission.
+ * XXX Do we have to consider the accumulation of errors
+ * less than 1/hz second?
+ */
+ delay -= (cur_time - scp->prev_wakeup_time);
+ if (delay < 1) {
+ printf("sequencer: prev = %lu, cur = %lu, delay = %d, skip sleeping.\n",
+ scp->prev_wakeup_time, cur_time, delay);
+ seq_stoptimer(scp);
+ return 0;
+ }
+#endif /* notdef */
+
+ scp->timeout_ch = timeout(seq_timer, (void *)scp, delay);
+ scp->timer_running = 1;
+ return 1;
+}
+
+static void
+seq_stoptimer(sc_p scp)
+{
+ /*DEB(printf("seq%d: stopping timer.\n", unit));*/
+
+ if (scp->timer_running) {
+ untimeout(seq_timer, (void *)scp, scp->timeout_ch);
+ scp->timer_running = 0;
+ }
+}
+
+static void
+seq_midiinput(sc_p scp, mididev_info *md)
+{
+ int unit, midiunit;
+ u_long tstamp;
+ u_char event[4];
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ /* Can this midi device interrupt for input? */
+ midiunit = md->unit;
+ if (scp->midi_open[midiunit]
+ && (md->flags & MIDI_F_READING) != 0
+ && md->intrarg == sd)
+ /* Read the input data. */
+ while (md->synth.readraw(md, &event[1], sizeof(event[1]), 1) == 0) {
+ tstamp = seq_gettime() - scp->seq_time;
+ if (tstamp != scp->prev_input_time) {
+ /* Insert a wait between events. */
+ tstamp = (tstamp << 8) | SEQ_WAIT;
+ seq_copytoinput(scp, (u_char *)&tstamp, 4);
+ scp->prev_input_time = tstamp;
+ }
+ event[0] = SEQ_MIDIPUTC;
+ event[2] = midiunit;
+ event[3] = 0;
+ seq_copytoinput(scp, event, sizeof(event));
+ }
+}
+
+static int
+seq_copytoinput(sc_p scp, u_char *event, int len)
+{
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+
+ if (midibuf_input_intr(&sd->midi_dbuf_in, event, len) == -EAGAIN)
+ return (EAGAIN);
+
+ return (0);
+}
+
+static int
+seq_extended(sc_p scp, u_char *event)
+{
+ int unit;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ if (event[2] >= nmidi + nsynth)
+ return (MORE);
+ md = &midi_info[event[2]];
+ if (!MIDICONFED(md) && (md->flags & MIDI_F_BUSY) == 0 && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0)
+ return (MORE);
+
+ switch (event[1]) {
+ case SEQ_NOTEOFF:
+ if (md->synth.killnote(md, event[3], event[4], event[5]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_NOTEON:
+ if (event[4] < 128 || event[4] == 255)
+ if (md->synth.startnote(md, event[3], event[4], event[5]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_PGMCHANGE:
+ if (md->synth.setinstr(md, event[3], event[4]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_AFTERTOUCH:
+ if (md->synth.aftertouch(md, event[3], event[4]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_BALANCE:
+ if (md->synth.panning(md, event[3], (char)event[4]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_CONTROLLER:
+ if (md->synth.controller(md, event[3], event[4], *(short *)&event[5]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case SEQ_VOLMODE:
+ if (md->synth.volumemethod != NULL)
+ if (md->synth.volumemethod(md, event[3]) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_chnvoice(sc_p scp, u_char *event)
+{
+ int voice;
+ seqdev_info *sd;
+ mididev_info *md;
+ u_char dev, cmd, chn, note, parm;
+
+ voice = -1;
+ dev = event[1];
+ cmd = event[2];
+ chn = event[3];
+ note = event[4];
+ parm = event[5];
+
+ sd = scp->devinfo;
+
+ if (dev >= nmidi + nsynth)
+ return (MORE);
+ md = &midi_info[dev];
+ if (!MIDICONFED(md) && (md->flags & MIDI_F_BUSY) == 0 && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0)
+ return (MORE);
+
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ if (md->synth.allocvoice)
+ voice = seq_allocvoice(md, chn, note);
+ }
+#endif /* notyet */
+ switch (cmd) {
+ case MIDI_NOTEON:
+ if (note < 128 || note == 255) {
+#if notyet
+ if (voice == -1 && scp->seq_mode == SEQ_2 && md->synth.allocvoice)
+ /* This is an internal synthesizer. (FM, GUS, etc) */
+ if ((voice = seq_allocvoice(md, chn, note)) == -EAGAIN)
+ return (QUEUEFULL);
+#endif /* notyet */
+ if (voice == -1)
+ voice = chn;
+
+#if notyet
+ if (scp->seq_mode == SEQ_2 && dev < nmidi + nsynth && chn == 9) {
+ /* This channel is a percussion. The note number is the patch number. */
+ if (md->synth.setinstr(md, voice, 128 + note) == EAGAIN)
+ return (QUEUEFULL);
+ note = 60; /* Middle C. */
+ }
+ if (scp->seq_mode == SEQ_2)
+ if (md->synth.setupvoice(md, voice, chn) == EAGAIN)
+ return (QUEUEFULL);
+#endif /* notyet */
+ if (md->synth.startnote(md, voice, note, parm) == EAGAIN)
+ return (QUEUEFULL);
+ }
+ break;
+ case MIDI_NOTEOFF:
+ if (voice == -1)
+ voice = chn;
+ if (md->synth.killnote(md, voice, note, parm) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case MIDI_KEY_PRESSURE:
+ if (voice == -1)
+ voice = chn;
+ if (md->synth.aftertouch(md, voice, parm) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_findvoice(mididev_info *md, int chn, int note)
+{
+ int i;
+ u_short key;
+
+ key = (chn << 8) | (note + 1);
+
+ for (i = 0 ; i < md->synth.alloc.max_voice ; i++)
+ if (md->synth.alloc.map[i] == key)
+ return (i);
+
+ return (-1);
+}
+
+static int
+seq_allocvoice(mididev_info *md, int chn, int note)
+{
+ int voice;
+ u_short key;
+
+ key = (chn << 8) | (note + 1);
+
+ if ((voice = md->synth.allocvoice(md, chn, note, &md->synth.alloc)) == -EAGAIN)
+ return (-EAGAIN);
+ md->synth.alloc.map[voice] = key;
+ md->synth.alloc.alloc_times[voice] = md->synth.alloc.timestamp++;
+
+ return (voice);
+}
+
+static int
+seq_chncommon(sc_p scp, u_char *event)
+{
+ int unit/*, i, val, key*/;
+ u_short w14;
+ u_char dev, cmd, chn, p1;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ dev = event[1];
+ cmd = event[2];
+ chn = event[3];
+ p1 = event[4];
+ w14 = *(u_short *)&event[6];
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ if (dev >= nmidi + nsynth)
+ return (MORE);
+ md = &midi_info[dev];
+ if (!MIDICONFED(md) && (md->flags & MIDI_F_BUSY) == 0 && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0)
+ return (MORE);
+
+ switch (cmd) {
+ case MIDI_PGM_CHANGE:
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ md->synth.chn_info[chn].pgm_num = p1;
+ if (dev < nmidi + nsynth)
+ if (md->synth.setinstr(md, chn, p1) == EAGAIN)
+ return (QUEUEFULL);
+ } else
+#endif /* notyet */
+ /* For Mode 1. */
+ if (md->synth.setinstr(md, chn, p1) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case MIDI_CTL_CHANGE:
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ if (chn < 16 && p1 < 128) {
+ md->synth.chn_info[chn].controllers[p1] = w14 & 0x7f;
+ if (p1 < 32)
+ /* We have set the MSB, clear the LSB. */
+ md->synth.chn_info[chn].controllers[p1 + 32] = 0;
+ if (dev < nmidi + nsynth) {
+ val = w14 & 0x7f;
+ if (p1 < 64) {
+ /* Combine the MSB and the LSB. */
+ val = ((md->synth.chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7)
+ | (md->synth.chn_info[chn].controllers[p1 | 32] & 0x7f);
+ p1 &= ~32;
+ }
+ /* 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)
+ if (md->synth.controller(md, i, p1, val) == EAGAIN)
+ return (QUEUEFULL);
+ } else
+ if (md->synth.controller(md, chn, p1, w14) == EAGAIN)
+ return (QUEUEFULL);
+ }
+ } else
+#endif /* notyet */
+ /* For Mode 1. */
+ if (md->synth.controller(md, chn, p1, w14) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ case MIDI_PITCH_BEND:
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ md->synth.chn_info[chn].bender_value = w14;
+ if (dev < nmidi + nsynth) {
+ /* 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)
+ if (md->synth.bender(md, i, w14) == EAGAIN)
+ return (QUEUEFULL);
+ } else
+ if (md->synth.bender(md, chn, w14) == EAGAIN)
+ return (QUEUEFULL);
+ } else
+#endif /* notyet */
+ /* For Mode 1. */
+ if (md->synth.bender(md, chn, w14) == EAGAIN)
+ return (QUEUEFULL);
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_timing(sc_p scp, u_char *event)
+{
+ int unit/*, ret*/;
+ long parm;
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ parm = *(long *)&event[4];
+
+#if notyet
+ if (scp->seq_mode == SEQ_2 && (ret = tmr->event(tmr_no, event)) == TIMERARMED)
+ return (ret);
+#endif /* notyet */
+ switch (event[1]) {
+ case TMR_WAIT_REL:
+ parm += scp->prev_event_time;
+ /* FALLTHRU */
+ case TMR_WAIT_ABS:
+ if (parm > 0) {
+ sd->flags |= SEQ_F_WRITING;
+ scp->prev_event_time = parm;
+ if (seq_requesttimer(scp, parm))
+ return (TIMERARMED);
+ }
+ break;
+ case TMR_START:
+ scp->seq_time = seq_gettime();
+ scp->prev_input_time = 0;
+ scp->prev_event_time = 0;
+ scp->prev_wakeup_time = scp->seq_time;
+ break;
+ case TMR_STOP:
+ break;
+ case TMR_CONTINUE:
+ break;
+ case TMR_TEMPO:
+ break;
+ case TMR_ECHO:
+#if notyet
+ if (scp->seq_mode == SEQ_2)
+ seq_copytoinput(scp, event, 8);
+ else {
+#endif /* notyet */
+ parm = (parm << 8 | SEQ_ECHO);
+ seq_copytoinput(scp, (u_char *)&parm, 4);
+#if notyet
+ }
+#endif /* notyet */
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_local(sc_p scp, u_char *event)
+{
+ int unit;
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ switch (event[1]) {
+ case LOCL_STARTAUDIO:
+#if notyet
+ DMAbuf_start_devices(*(u_int *)&event[4]);
+#endif /* notyet */
+ break;
+ }
+
+ return (MORE);
+}
+
+static int
+seq_sysex(sc_p scp, u_char *event)
+{
+ int unit, i, l;
+ seqdev_info *sd;
+ mididev_info *md;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ if (event[1] >= nmidi + nsynth)
+ return (MORE);
+ md = &midi_info[event[1]];
+ if (!MIDICONFED(md) || md->synth.sendsysex == NULL
+ || ((md->flags & MIDI_F_BUSY) == 0 && seq_openmidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc) != 0))
+ return (MORE);
+
+ l = 0;
+ for (i = 0 ; i < 6 && event[i + 2] != 0xff ; i++)
+ l = i + 1;
+ if (l > 0)
+ if (md->synth.sendsysex(md, &event[2], l) == EAGAIN)
+ return (QUEUEFULL);
+
+ return (MORE);
+}
+
+static void
+seq_timer(void *arg)
+{
+ sc_p scp;
+
+ scp = arg;
+
+ /*DEB(printf("seq_timer: timer fired.\n"));*/
+
+ /* Record the current timestamp. */
+ scp->prev_wakeup_time = seq_gettime();
+
+ seq_startplay(scp);
+}
+
+static int
+seq_openmidi(sc_p scp, mididev_info *md, int flags, int mode, struct proc *p)
+{
+ int midiunit, s, err;
+
+ if (md == NULL || !MIDICONFED(md)) {
+ DEB(printf("seq_openmidi: midi device does not exist.\n"));
+ return (ENXIO);
+ }
+ midiunit = md->unit;
+
+ DEB(printf("seq_openmidi: opening midi unit %d.\n", midiunit));
+
+ if (!scp->midi_open[midiunit]) {
+ err = midi_open(MIDIMKDEV(MIDI_CDEV_MAJOR, midiunit, SND_DEV_MIDIN), flags, mode, p);
+ if (err != 0) {
+ printf("seq_openmidi: failed to open midi device %d.\n", midiunit);
+ return (err);
+ }
+ s = splmidi();
+ scp->midi_open[midiunit] = 1;
+ md->intr = seq_intr;
+ md->intrarg = scp->devinfo;
+ md->synth.prev_out_status = 0;
+ md->synth.sysex_state = 0;
+ splx(s);
+ }
+
+ return (0);
+}
+
+static int
+seq_closemidi(sc_p scp, mididev_info *md, int flags, int mode, struct proc *p)
+{
+ int midiunit, s;
+
+ if (md == NULL || !MIDICONFED(md)) {
+ DEB(printf("seq_closemidi: midi device does not exist.\n"));
+ return (ENXIO);
+ }
+ midiunit = md->unit;
+
+ DEB(printf("seq_closemidi: closing midi unit %d.\n", midiunit));
+
+ if (scp->midi_open[midiunit]) {
+ midi_close(MIDIMKDEV(MIDI_CDEV_MAJOR, midiunit, SND_DEV_MIDIN), flags, mode, p);
+ s = splmidi();
+ scp->midi_open[midiunit] = 0;
+ md->intr = NULL;
+ md->intrarg = NULL;
+ splx(s);
+ }
+
+ return (0);
+}
+
+static void
+seq_panic(sc_p scp)
+{
+ seq_reset(scp);
+}
+
+static int
+seq_reset(sc_p scp)
+{
+ int unit, i, s, chn;
+ seqdev_info *sd;
+ mididev_info *md;
+ u_char c[3];
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ s = splmidi();
+
+ /* Stop reading and writing. */
+ sd->callback(sd, SEQ_CB_ABORT | SEQ_CB_RD | SEQ_CB_WR);
+
+ /* Clear the queues. */
+ midibuf_init(&sd->midi_dbuf_in);
+ midibuf_init(&sd->midi_dbuf_out);
+
+#if notyet
+ /* Reset the synthesizers. */
+ for (i = 0 ; i < nmidi + nsynth ; i++) {
+ md = &midi_info[i];
+ if (MIDICONFED(md) && scp->midi_open[i])
+ md->synth.reset(md);
+ }
+#endif /* notyet */
+
+#if notyet
+ if (scp->seq_mode == SEQ_2) {
+ for (chn = 0 ; chn < 16 ; chn++)
+ for (i = 0 ; i < nmidi + nsynth ; i++)
+ if (midi_open[i]) {
+ md = &midi_info[i];
+ if (!MIDICONFED(md))
+ continue;
+ if (md->synth.controller(md, chn, 123, 0) == EAGAIN /* All notes off. */
+ || md->synth.controller(md, chn, 121, 0) == EAGAIN /* Reset all controllers. */
+ || md->synth.bender(md, chn, 1 << 13) == EAGAIN) { /* Reset pitch bend. */
+ splx(s);
+ return (EAGAIN);
+ }
+ }
+ splx(s);
+ } else {
+#endif /* notyet */
+ splx(s);
+ for (i = 0 ; i < nmidi + nsynth ; i++) {
+ md = &midi_info[i];
+ if (!MIDICONFED(md))
+ continue;
+
+ /* Send active sensing. */
+ c[0] = 0xfe; /* Active Sensing. */
+ if (md->synth.writeraw(md, c, 1, 0) == EAGAIN)
+ return (EAGAIN);
+ /*
+ * We need a sleep to reset a midi device using an active sensing.
+ * SC-88 resets after 420ms...
+ */
+ tsleep(md, PRIBIO, "seqrst", 500 * hz / 1000);
+ for (chn = 0 ; chn < 16 ; chn++) {
+ c[0] = 0xb0 | (chn & 0x0f);
+ c[1] = (u_char)0x78; /* All sound off */
+ c[2] = (u_char)0;
+ if (md->synth.writeraw(md, c, 3, 0) == EAGAIN)
+ return (EAGAIN);
+ c[1] = (u_char)0x7b; /* All note off */
+ if (md->synth.writeraw(md, c, 3, 0) == EAGAIN)
+ return (EAGAIN);
+ c[1] = (u_char)0x79; /* Reset all controller */
+ if (md->synth.writeraw(md, c, 3, 0) == EAGAIN)
+ return (EAGAIN);
+ }
+ }
+ for (i = 0 ; i < nmidi + nsynth ; i++){
+ md = &midi_info[i];
+ if (MIDICONFED(md))
+ seq_closemidi(scp, md, scp->fflags, MIDIDEV_MODE, curproc);
+ }
+#if notyet
+ }
+#endif /* notyet */
+
+ return (0);
+}
+
+static int
+seq_sync(sc_p scp)
+{
+ int unit, s, i;
+ seqdev_info *sd;
+
+ sd = scp->devinfo;
+ unit = sd->unit;
+
+ s = splmidi();
+
+ if (sd->midi_dbuf_out.rl >= EV_SZ)
+ sd->callback(sd, SEQ_CB_START | SEQ_CB_WR);
+
+ while ((sd->flags & SEQ_F_WRITING) != 0 && sd->midi_dbuf_out.rl >= EV_SZ) {
+ i = tsleep(&sd->midi_dbuf_out.tsleep_out, PRIBIO | PCATCH, "seqsnc", 0);
+ if (i == EINTR)
+ sd->callback(sd, SEQ_CB_STOP | SEQ_CB_WR);
+ if (i == EINTR || i == ERESTART) {
+ splx(s);
+ return (i);
+ }
+ }
+ splx(s);
+
+ return (0);
+}
+
+/*
+ * a small utility function which, given a device number, returns
+ * a pointer to the associated seqdev_info struct, and sets the unit
+ * number.
+ */
+static seqdev_info *
+get_seqdev_info(dev_t i_dev, int *unit)
+{
+ int u;
+ seqdev_info *d = NULL ;
+
+ if (MIDIDEV(i_dev) != SND_DEV_SEQ && MIDIDEV(i_dev) != SND_DEV_SEQ2)
+ return NULL;
+ u = MIDIUNIT(i_dev);
+ if (unit)
+ *unit = u ;
+
+ if (u >= NSEQ_MAX) {
+ DEB(printf("get_seqdev_info: unit %d is not configured.\n", u));
+ return NULL;
+ }
+ d = &seq_info[u];
+
+ return d ;
+}
+
+/* XXX These functions are actually redundant. */
+static int
+seqopen(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_open(i_dev, flags, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqclose(dev_t i_dev, int flags, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_close(i_dev, flags, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqread(dev_t i_dev, struct uio * buf, int flag)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_read(i_dev, buf, flag);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqwrite(dev_t i_dev, struct uio * buf, int flag)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_write(i_dev, buf, flag);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_ioctl(i_dev, cmd, arg, mode, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seqpoll(dev_t i_dev, int events, struct proc * p)
+{
+ switch (MIDIDEV(i_dev)) {
+ case MIDI_DEV_SEQ:
+ return seq_poll(i_dev, events, p);
+ }
+
+ return (ENXIO);
+}
+
+static int
+seq_modevent(module_t mod, int type, void *data)
+{
+ int retval;
+
+ retval = 0;
+
+ switch (type) {
+ case MOD_LOAD:
+ seq_init();
+ break;
+
+ case MOD_UNLOAD:
+ printf("sequencer: unload not supported yet.\n");
+ retval = EOPNOTSUPP;
+ break;
+
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+DEV_MODULE(seq, seq_modevent, NULL);
diff --git a/sys/dev/sound/midi/sequencer.h b/sys/dev/sound/midi/sequencer.h
new file mode 100644
index 0000000..7275b77
--- /dev/null
+++ b/sys/dev/sound/midi/sequencer.h
@@ -0,0 +1,234 @@
+/*
+ * Include file for midi sequencer driver.
+ *
+ * Copyright by Seigo Tanimura 1999.
+ *
+ * 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$
+ *
+ */
+
+/*
+ * first, include kernel header files.
+ */
+
+#ifndef _SEQUENCER_H_
+#define _SEQUENCER_H_
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ioccom.h>
+
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#include <sys/fcntl.h>
+#include <sys/tty.h>
+#include <sys/proc.h>
+
+#include <sys/kernel.h> /* for DATA_SET */
+
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/syslog.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <machine/clock.h> /* for DELAY */
+#include <sys/soundcard.h>
+
+#define SEQ_CDEV_MAJOR MIDI_CDEV_MAJOR
+
+/*
+ * the following assumes that FreeBSD 3.X uses poll(2) instead of select(2).
+ * This change dates to late 1997.
+ */
+#include <sys/poll.h>
+#define d_select_t d_poll_t
+
+typedef struct _seqdev_info seqdev_info;
+
+typedef int (seq_callback_t)(seqdev_info *sd, int reason);
+
+/*
+ * descriptor of sequencer operations ...
+ *
+ */
+
+struct _seqdev_info {
+
+ /*
+ * the first part of the descriptor is filled up from a
+ * template.
+ */
+ char name[64];
+
+ int type ;
+
+ d_open_t *open;
+ d_close_t *close;
+ d_read_t *read;
+ d_write_t *write;
+ d_ioctl_t *ioctl;
+ d_poll_t *poll;
+ seq_callback_t *callback;
+
+ /*
+ * combinations of the following flags are used as second argument in
+ * the callback from the dma module to the device-specific routines.
+ */
+
+#define SEQ_CB_RD 0x100 /* read callback */
+#define SEQ_CB_WR 0x200 /* write callback */
+#define SEQ_CB_REASON_MASK 0xff
+#define SEQ_CB_START 0x01 /* start dma op */
+#define SEQ_CB_STOP 0x03 /* stop dma op */
+#define SEQ_CB_ABORT 0x04 /* abort dma op */
+#define SEQ_CB_INIT 0x05 /* init board parameters */
+
+ /*
+ * callback extensions
+ */
+#define SEQ_CB_DMADONE 0x10
+#define SEQ_CB_DMAUPDATE 0x11
+#define SEQ_CB_DMASTOP 0x12
+
+ /* init can only be called with int enabled and
+ * no pending DMA activity.
+ */
+
+ /*
+ * whereas from here, parameters are set at runtime.
+ * io_base == 0 means that the board is not configured.
+ */
+
+ int unit; /* unit number of the device */
+ void *softc; /* softc for a device */
+
+ int bd_id ; /* used to hold board-id info, eg. sb version,
+ * mss codec type, etc. etc.
+ */
+
+ midi_dbuf midi_dbuf_in; /* midi input event/message queue */
+ midi_dbuf midi_dbuf_out; /* midi output event/message queue */
+
+ /*
+ * these parameters describe the operation of the board.
+ * Generic things like busy flag, speed, etc are here.
+ */
+
+ volatile u_long flags ; /* 32 bits, used for various purposes. */
+
+ /*
+ * we have separate flags for read and write, although in some
+ * cases this is probably not necessary (e.g. because we cannot
+ * know how many processes are using the device, we cannot
+ * distinguish if open, close, abort are for a write or for a
+ * read).
+ */
+
+ /*
+ * the following flag is used by open-close routines
+ * to mark the status of the device.
+ */
+#define SEQ_F_BUSY 0x0001 /* has been opened */
+ /*
+ * the next two are used to allow only one pending operation of
+ * each type.
+ */
+#define SEQ_F_READING 0x0004 /* have a pending read */
+#define SEQ_F_WRITING 0x0008 /* have a pending write */
+
+ /*
+ * flag used to mark a pending close.
+ */
+#define SEQ_F_CLOSING 0x0040 /* a pending close */
+
+ /*
+ * if user has not set block size, then make it adaptive
+ * (0.25s, or the perhaps last read/write ?)
+ */
+#define SEQ_F_HAS_SIZE 0x0080 /* user set block size */
+ /*
+ * assorted flags related to operating mode.
+ */
+#define SEQ_F_STEREO 0x0100 /* doing stereo */
+#define SEQ_F_NBIO 0x0200 /* do non-blocking i/o */
+
+ /*
+ * these flags mark a pending abort on a r/w operation.
+ */
+#define SEQ_F_ABORTING 0x1000 /* a pending abort */
+
+ /*
+ * this is used to mark that board initialization is needed, e.g.
+ * because of a change in sampling rate, format, etc. -- It will
+ * be done at the next convenient time.
+ */
+#define SEQ_F_INIT 0x4000 /* changed parameters. need init */
+
+ int play_blocksize, rec_blocksize; /* blocksize for io and dma ops */
+
+#define swsel midi_dbuf_out.sel
+#define srsel midi_dbuf_in.sel
+ u_long interrupts; /* counter of interrupts */
+ u_long magic;
+#define MAGIC(unit) ( 0xa4d10de0 + unit )
+ void *device_data ; /* just in case it is needed...*/
+} ;
+
+
+/*
+ * then ioctls and other stuff
+ */
+#define NSEQ_MAX 64 /* Number of supported devices */
+
+/*
+ * 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))
+
+/*
+ * finally, all default parameters
+ */
+#define SEQ_BUFFSIZE (4 * 1024) /* XXX */
+
+/*
+ * some macros for debugging purposes
+ * DDB/DEB to enable/disable debugging stuff
+ * BVDDB to enable debugging when bootverbose
+ */
+#define DDB(x) x /* XXX */
+#define BVDDB(x) if (bootverbose) x
+
+#ifndef DEB
+#define DEB(x)
+#endif
+
+ extern seqdev_info seq_info[NSEQ_MAX] ;
+
+#define MIDI_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
+ synthesizer and MIDI output) */
+
+#endif /* _SEQUENCER_H_ */
diff --git a/sys/dev/sound/pci/csa.c b/sys/dev/sound/pci/csa.c
index b474574..7d51311 100644
--- a/sys/dev/sound/pci/csa.c
+++ b/sys/dev/sound/pci/csa.c
@@ -59,11 +59,9 @@ struct csa_softc {
device_t pcm; /* pcm device */
driver_intr_t* pcmintr; /* pcm intr */
void *pcmintr_arg; /* pcm intr arg */
-#if notyet
device_t midi; /* midi device */
driver_intr_t* midiintr; /* midi intr */
void *midiintr_arg; /* midi intr arg */
-#endif /* notyet */
void *ih; /* cookie */
struct csa_bridgeinfo binfo; /* The state of this bridge. */
@@ -198,7 +196,6 @@ csa_attach(device_t dev)
scp->pcm = device_add_child(dev, "pcm", -1);
device_set_ivars(scp->pcm, func);
-#if notyet
/* Midi Interface */
func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
if (func == NULL)
@@ -208,7 +205,6 @@ csa_attach(device_t dev)
func->func = SCF_MIDI;
scp->midi = device_add_child(dev, "midi", -1);
device_set_ivars(scp->midi, func);
-#endif /* notyet */
bus_generic_attach(dev);
@@ -292,12 +288,10 @@ csa_setup_intr(device_t bus, device_t child,
scp->pcmintr_arg = arg;
break;
-#if notyet
case SCF_MIDI:
scp->midiintr = intr;
scp->midiintr_arg = arg;
break;
-#endif /* notyet */
default:
return (EINVAL);
@@ -334,12 +328,10 @@ csa_teardown_intr(device_t bus, device_t child,
scp->pcmintr_arg = NULL;
break;
-#if notyet
case SCF_MIDI:
scp->midiintr = NULL;
scp->midiintr_arg = NULL;
break;
-#endif /* notyet */
default:
return (EINVAL);
@@ -375,10 +367,8 @@ csa_intr(void *arg)
/* Invoke the handlers of the children. */
if ((hisr & (HISR_VC0 | HISR_VC1)) != 0 && scp->pcmintr != NULL)
scp->pcmintr(scp->pcmintr_arg);
-#if notyet
if ((hisr & HISR_MIDI) != 0 && scp->midiintr != NULL)
scp->midiintr(scp->midiintr_arg);
-#endif /* notyet */
/* Throw an eoi. */
csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM);
diff --git a/sys/dev/sound/pci/csamidi.c b/sys/dev/sound/pci/csamidi.c
new file mode 100644
index 0000000..39ecda6
--- /dev/null
+++ b/sys/dev/sound/pci/csamidi.c
@@ -0,0 +1,554 @@
+/*-
+ * 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$
+ */
+
+#include "opt_devfs.h"
+
+#include <dev/sound/midi/midi.h>
+#include <dev/sound/chip.h>
+#include <dev/sound/pci/csareg.h>
+#include <machine/cpufunc.h>
+
+static devclass_t midi_devclass;
+
+#ifndef DDB
+#undef DDB
+#define DDB(x)
+#endif /* DDB */
+
+#define CSAMIDI_RESET 0xff
+#define CSAMIDI_UART 0x3f
+#define CSAMIDI_ACK 0xfe
+
+#define CSAMIDI_STATMASK 0xc0
+#define CSAMIDI_OUTPUTBUSY 0x40
+#define CSAMIDI_INPUTBUSY 0x80
+
+#define CSAMIDI_TRYDATA 50
+#define CSAMIDI_DELAY 25000
+
+extern synthdev_info midisynth_op_desc;
+
+/* These are the synthesizer and the midi interface information. */
+static struct synth_info csamidi_synthinfo = {
+ "CS461x MIDI",
+ 0,
+ SYNTH_TYPE_MIDI,
+ 0,
+ 0,
+ 128,
+ 128,
+ 128,
+ SYNTH_CAP_INPUT,
+};
+
+static struct midi_info csamidi_midiinfo = {
+ "CS461x MIDI",
+ 0,
+ 0,
+ 0,
+};
+
+/*
+ * These functions goes into csamidi_op_desc to get called
+ * from sound.c.
+ */
+
+static int csamidi_probe(device_t dev);
+static int csamidi_attach(device_t dev);
+
+static d_ioctl_t csamidi_ioctl;
+static driver_intr_t csamidi_intr;
+static midi_callback_t csamidi_callback;
+
+/* Here is the parameter structure per a device. */
+struct csamidi_softc {
+ device_t dev; /* device information */
+ mididev_info *devinfo; /* midi device information */
+ struct csa_bridgeinfo *binfo; /* The state of the parent. */
+
+ struct resource *io; /* Base of io map */
+ int io_rid; /* Io map resource ID */
+ struct resource *mem; /* Base of memory map */
+ int mem_rid; /* Memory map resource ID */
+ struct resource *irq; /* Irq */
+ int irq_rid; /* Irq resource ID */
+ void *ih; /* Interrupt cookie */
+
+ int fflags; /* File flags */
+};
+
+typedef struct csamidi_softc *sc_p;
+
+/* These functions are local. */
+static void csamidi_startplay(sc_p scp);
+static void csamidi_xmit(sc_p scp);
+static int csamidi_reset(sc_p scp);
+static int csamidi_status(sc_p scp);
+static int csamidi_command(sc_p scp, u_int32_t value);
+static int csamidi_readdata(sc_p scp);
+static int csamidi_writedata(sc_p scp, u_int32_t value);
+static u_int32_t csamidi_readio(sc_p scp, u_long offset);
+static void csamidi_writeio(sc_p scp, u_long offset, u_int32_t data);
+static u_int32_t csamidi_readmem(sc_p scp, u_long offset);
+static void csamidi_writemem(sc_p scp, u_long offset, u_int32_t data);
+static int csamidi_allocres(sc_p scp, device_t dev);
+static void csamidi_releaseres(sc_p scp, device_t dev);
+
+/*
+ * This is the device descriptor for the midi device.
+ */
+static mididev_info csamidi_op_desc = {
+ "CS461x midi",
+
+ SNDCARD_MPU401,
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ csamidi_ioctl,
+ NULL,
+
+ csamidi_callback,
+
+ MIDI_BUFFSIZE, /* Queue Length */
+
+ 0, /* XXX This is not an *audio* device! */
+};
+
+/*
+ * Here are the main functions to interact to the user process.
+ */
+
+static int
+csamidi_probe(device_t dev)
+{
+ char *s;
+ sc_p scp;
+ struct sndcard_func *func;
+
+ /* The parent device has already been probed. */
+
+ func = device_get_ivars(dev);
+ if (func == NULL || func->func != SCF_MIDI)
+ return (ENXIO);
+
+ s = "CS461x Midi Interface";
+
+ scp = device_get_softc(dev);
+ bzero(scp, sizeof(*scp));
+ scp->io_rid = CS461x_IO_OFFSET;
+ scp->mem_rid = CS461x_MEM_OFFSET;
+ scp->irq_rid = 0;
+
+ device_set_desc(dev, s);
+ return (0);
+}
+
+static int
+csamidi_attach(device_t dev)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ struct sndcard_func *func;
+ int unit;
+
+ scp = device_get_softc(dev);
+ unit = device_get_unit(dev);
+ func = device_get_ivars(dev);
+ scp->binfo = func->varinfo;
+
+ /* Allocate the resources. */
+ if (csamidi_allocres(scp, dev)) {
+ csamidi_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ /* Fill the softc. */
+ scp->dev = dev;
+ scp->devinfo = devinfo = &midi_info[unit];
+
+ /* Fill the midi info. */
+ bcopy(&csamidi_op_desc, devinfo, sizeof(csamidi_op_desc));
+ midiinit(devinfo, dev);
+ devinfo->flags = 0;
+ bcopy(&midisynth_op_desc, &devinfo->synth, sizeof(midisynth_op_desc));
+ snprintf(devinfo->midistat, sizeof(devinfo->midistat), "at irq %d",
+ (int)rman_get_start(scp->irq));
+
+ /* Init the queue. */
+ devinfo->midi_dbuf_in.unit_size = devinfo->midi_dbuf_out.unit_size = 1;
+ midibuf_init(&devinfo->midi_dbuf_in);
+ midibuf_init(&devinfo->midi_dbuf_out);
+
+ /* Increase the number of midi devices. */
+ nmidi++;
+
+ /* Enable interrupt. */
+ if (bus_setup_intr(dev, scp->irq, INTR_TYPE_TTY, csamidi_intr, scp, &scp->ih)) {
+ csamidi_releaseres(scp, dev);
+ return (ENXIO);
+ }
+
+ /* Reset the interface. */
+ csamidi_reset(scp);
+
+ return (0);
+}
+
+static int
+csamidi_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc *p)
+{
+ sc_p scp;
+ mididev_info *devinfo;
+ int unit;
+ struct synth_info *synthinfo;
+ struct midi_info *midiinfo;
+
+ unit = MIDIUNIT(i_dev);
+
+ if (unit >= nmidi + nsynth) {
+ DEB(printf("csamidi_ioctl: unit %d does not exist.\n", unit));
+ return (ENXIO);
+ }
+
+ devinfo = get_mididev_info(i_dev, &unit);
+ if (devinfo == NULL) {
+ DEB(printf("csamidi_ioctl: unit %d is not configured.\n", unit));
+ return (ENXIO);
+ }
+ scp = devinfo->softc;
+
+ switch (cmd) {
+ case SNDCTL_SYNTH_INFO:
+ synthinfo = (struct synth_info *)arg;
+ if (synthinfo->device > nmidi + nsynth || synthinfo->device != unit)
+ return (ENXIO);
+ bcopy(&csamidi_synthinfo, synthinfo, sizeof(csamidi_synthinfo));
+ synthinfo->device = unit;
+ return (0);
+ break;
+ case SNDCTL_MIDI_INFO:
+ midiinfo = (struct midi_info *)arg;
+ if (midiinfo->device > nmidi + nsynth || midiinfo->device != unit)
+ return (ENXIO);
+ bcopy(&csamidi_midiinfo, midiinfo, sizeof(csamidi_midiinfo));
+ midiinfo->device = unit;
+ return (0);
+ break;
+ default:
+ return (ENOSYS);
+ }
+ /* NOTREACHED */
+ return (EINVAL);
+}
+
+static void
+csamidi_intr(void *arg)
+{
+ sc_p scp;
+ u_char c;
+ mididev_info *devinfo;
+
+ scp = (sc_p)arg;
+ devinfo = scp->devinfo;
+
+ /* Read the received data. */
+ while ((csamidi_status(scp) & MIDSR_RBE) == 0) {
+ /* Receive the data. */
+ c = (u_char)csamidi_readdata(scp);
+ /* Queue into the passthru buffer and start transmitting if we can. */
+ if ((devinfo->flags & MIDI_F_PASSTHRU) != 0 && ((devinfo->flags & MIDI_F_BUSY) == 0 || (devinfo->fflags & FWRITE) == 0)) {
+ midibuf_input_intr(&devinfo->midi_dbuf_passthru, &c, sizeof(c));
+ devinfo->callback(devinfo, MIDI_CB_START | MIDI_CB_WR);
+ }
+ /* Queue if we are reading. Discard an active sensing. */
+ if ((devinfo->flags & MIDI_F_READING) != 0 && c != 0xfe)
+ midibuf_input_intr(&devinfo->midi_dbuf_in, &c, sizeof(c));
+ }
+
+ /* Transmit out data. */
+ if ((devinfo->flags & MIDI_F_WRITING) != 0 && (csamidi_status(scp) & MIDSR_TBF) == 0)
+ csamidi_xmit(scp);
+
+ /* Invoke the upper layer. */
+ midi_intr(devinfo);
+}
+
+static int
+csamidi_callback(mididev_info *d, int reason)
+{
+ int unit;
+ sc_p scp;
+
+ if (d == NULL) {
+ DEB(printf("csamidi_callback: device not configured.\n"));
+ return (ENXIO);
+ }
+
+ unit = d->unit;
+ scp = d->softc;
+
+ switch (reason & MIDI_CB_REASON_MASK) {
+ case MIDI_CB_START:
+ if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) == 0)
+ /* Begin recording. */
+ d->flags |= MIDI_F_READING;
+ if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) == 0)
+ /* Start playing. */
+ csamidi_startplay(scp);
+ break;
+ case MIDI_CB_STOP:
+ case MIDI_CB_ABORT:
+ if ((reason & MIDI_CB_RD) != 0 && (d->flags & MIDI_F_READING) != 0)
+ /* Stop recording. */
+ d->flags &= ~MIDI_F_READING;
+ if ((reason & MIDI_CB_WR) != 0 && (d->flags & MIDI_F_WRITING) != 0)
+ /* Stop Playing. */
+ d->flags &= ~MIDI_F_WRITING;
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * The functions below here are the libraries for the above ones.
+ */
+
+/*
+ * Starts to play the data in the output queue.
+ * Call this at >=splclock.
+ */
+static void
+csamidi_startplay(sc_p scp)
+{
+ mididev_info *devinfo;
+
+ devinfo = scp->devinfo;
+
+ /* Can we play now? */
+ if (devinfo->midi_dbuf_out.rl == 0)
+ return;
+
+ devinfo->flags |= MIDI_F_WRITING;
+ csamidi_xmit(scp);
+}
+
+static void
+csamidi_xmit(sc_p scp)
+{
+ register mididev_info *devinfo;
+ register midi_dbuf *dbuf;
+ u_char c;
+
+ devinfo = scp->devinfo;
+
+ /* See which source to use. */
+ if ((devinfo->flags & MIDI_F_PASSTHRU) == 0 || ((devinfo->flags & MIDI_F_BUSY) != 0 && (devinfo->fflags & FWRITE) != 0))
+ dbuf = &devinfo->midi_dbuf_out;
+ else
+ dbuf = &devinfo->midi_dbuf_passthru;
+
+ /* Transmit the data in the queue. */
+ while ((devinfo->flags & MIDI_F_WRITING) != 0 && (csamidi_status(scp) & MIDSR_TBF) == 0) {
+ /* Do we have the data to transmit? */
+ if (dbuf->rl == 0) {
+ /* Stop playing. */
+ devinfo->flags &= ~MIDI_F_WRITING;
+ break;
+ } else {
+ /* Send the data. */
+ midibuf_output_intr(dbuf, &c, sizeof(c));
+ csamidi_writedata(scp, c);
+ /* We are playing now. */
+ devinfo->flags |= MIDI_F_WRITING;
+ }
+ }
+}
+
+/* Reset midi. */
+static int
+csamidi_reset(sc_p scp)
+{
+ int i, resp;
+
+ /* Reset the midi. */
+ resp = 0;
+ for (i = 0 ; i < CSAMIDI_TRYDATA ; i++) {
+ resp = csamidi_command(scp, MIDCR_MRST);
+ if (resp == 0)
+ break;
+ }
+ if (resp != 0)
+ return (1);
+ for (i = 0 ; i < CSAMIDI_TRYDATA ; i++) {
+ resp = csamidi_command(scp, MIDCR_TXE | MIDCR_RXE | MIDCR_RIE | MIDCR_TIE);
+ if (resp == 0)
+ break;
+ }
+ if (resp != 0)
+ return (1);
+
+ DELAY(CSAMIDI_DELAY);
+
+ return (0);
+}
+
+/* Reads the status. */
+static int
+csamidi_status(sc_p scp)
+{
+ return csamidi_readio(scp, BA0_MIDSR);
+}
+
+/* Writes a command. */
+static int
+csamidi_command(sc_p scp, u_int32_t value)
+{
+ csamidi_writeio(scp, BA0_MIDCR, value);
+
+ return (0);
+}
+
+/* Reads a byte of data. */
+static int
+csamidi_readdata(sc_p scp)
+{
+ u_int status;
+
+ /* Is the interface ready to read? */
+ status = csamidi_status(scp);
+ if ((status & MIDSR_RBE) != 0)
+ /* The interface is busy. */
+ return (-EAGAIN);
+
+ return (int)csamidi_readio(scp, BA0_MIDRP) & 0xff;
+}
+
+/* Writes a byte of data. */
+static int
+csamidi_writedata(sc_p scp, u_int32_t value)
+{
+ u_int status;
+
+ /* Is the interface ready to write? */
+ status = csamidi_status(scp);
+ if ((status & MIDSR_TBF) != 0)
+ /* The interface is busy. */
+ return (EAGAIN);
+
+ csamidi_writeio(scp, BA0_MIDWP, value & 0xff);
+
+ return (0);
+}
+
+static u_int32_t
+csamidi_readio(sc_p scp, u_long offset)
+{
+ if (offset < BA0_AC97_RESET)
+ return bus_space_read_4(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), offset) & 0xffffffff;
+ else
+ return (0);
+}
+
+static void
+csamidi_writeio(sc_p scp, u_long offset, u_int32_t data)
+{
+ if (offset < BA0_AC97_RESET)
+ bus_space_write_4(rman_get_bustag(scp->io), rman_get_bushandle(scp->io), offset, data);
+}
+
+static u_int32_t
+csamidi_readmem(sc_p scp, u_long offset)
+{
+ return bus_space_read_4(rman_get_bustag(scp->mem), rman_get_bushandle(scp->mem), offset) & 0xffffffff;
+}
+
+static void
+csamidi_writemem(sc_p scp, u_long offset, u_int32_t data)
+{
+ bus_space_write_4(rman_get_bustag(scp->mem), rman_get_bushandle(scp->mem), offset, data);
+}
+
+/* Allocates resources. */
+static int
+csamidi_allocres(sc_p scp, device_t dev)
+{
+ if (scp->io == NULL) {
+ scp->io = bus_alloc_resource(dev, SYS_RES_MEMORY, &scp->io_rid, 0, ~0, CS461x_IO_SIZE, RF_ACTIVE);
+ if (scp->io == NULL)
+ return (1);
+ }
+ if (scp->mem == NULL) {
+ scp->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &scp->mem_rid, 0, ~0, CS461x_MEM_SIZE, RF_ACTIVE);
+ if (scp->mem == NULL)
+ return (1);
+ }
+ if (scp->irq == NULL) {
+ scp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &scp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (scp->irq == NULL)
+ return (1);
+ }
+
+ return (0);
+}
+
+/* Releases resources. */
+static void
+csamidi_releaseres(sc_p scp, device_t dev)
+{
+ if (scp->irq != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, scp->irq_rid, scp->irq);
+ scp->irq = NULL;
+ }
+ if (scp->io != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, scp->io_rid, scp->io);
+ scp->io = NULL;
+ }
+ if (scp->mem != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, scp->mem_rid, scp->mem);
+ scp->mem = NULL;
+ }
+}
+
+static device_method_t csamidi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe , csamidi_probe ),
+ DEVMETHOD(device_attach, csamidi_attach),
+
+ { 0, 0 },
+};
+
+static driver_t csamidi_driver = {
+ "midi",
+ csamidi_methods,
+ sizeof(struct csamidi_softc),
+};
+
+DRIVER_MODULE(csamidi, csa, csamidi_driver, midi_devclass, 0, 0);
diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c
index 5733568..68e45d9 100644
--- a/sys/dev/sound/pcm/mixer.c
+++ b/sys/dev/sound/pcm/mixer.c
@@ -32,6 +32,7 @@ static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
[SOUND_MIXER_VOLUME] = 75,
[SOUND_MIXER_BASS] = 50,
[SOUND_MIXER_TREBLE] = 50,
+ [SOUND_MIXER_SYNTH] = 75,
[SOUND_MIXER_PCM] = 75,
[SOUND_MIXER_SPEAKER] = 75,
[SOUND_MIXER_LINE] = 75,
diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES
index 1c46e4c..2a8214e 100644
--- a/sys/i386/conf/NOTES
+++ b/sys/i386/conf/NOTES
@@ -1649,8 +1649,32 @@ hint.pcm.0.flags="0x0"
# For PnP/PCI sound cards, no hints are required.
+#
+# midi: MIDI interfaces and synthesizers
+#
+
+device midi
+
+# For non-pnp sound cards with no bridge drivers:
+hint.midi.0.at="isa"
+hint.midi.0.irq="5"
+hint.midi.0.flags="0x0"
+
+# For serial ports (this example configures port 2):
+# TODO: implement generic tty-midi interface so that we can use
+# other uarts.
+hint.midi.0.at="isa"
+hint.midi.0.port="0x2F8"
+hint.midi.0.irq="3"
+
+#
+# seq: MIDI sequencer
+#
+
+device seq
+
# The bridge drivers for sound cards. These can be seperately configured
-# for providing services to the likes of new-midi (not in the tree yet).
+# for providing services to the likes of new-midi.
# When used with 'device pcm' they also provide pcm sound services.
#
# sbc: Creative SoundBlaster ISA PnP/non-PnP
diff --git a/sys/i386/i386/bios.c b/sys/i386/i386/bios.c
index 4a5a942..06eafe0 100644
--- a/sys/i386/i386/bios.c
+++ b/sys/i386/i386/bios.c
@@ -588,7 +588,8 @@ pnpbios_identify(driver_t *driver, device_t parent)
isa_set_logicalid(dev, pd->devid);
ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0);
pnp_parse_resources(dev, &pd->devdata[0],
- pd->size - sizeof(struct pnp_sysdev));
+ pd->size - sizeof(struct pnp_sysdev),
+ isa_get_vendorid(dev), isa_get_logicalid(dev), 0);
if (!device_get_desc(dev))
device_set_desc_copy(dev, pnp_eisaformat(pd->devid));
diff --git a/sys/isa/pnp.c b/sys/isa/pnp.c
index 12b5dab..f9266bb 100644
--- a/sys/isa/pnp.c
+++ b/sys/isa/pnp.c
@@ -54,6 +54,7 @@ struct pnp_quirk {
u_int32_t logical_id; /* ID of the device with quirk */
int type;
#define PNP_QUIRK_WRITE_REG 1 /* Need to write a pnp register */
+#define PNP_QUIRK_EXTRA_IO 2 /* Has extra io ports */
int arg1;
int arg2;
};
@@ -66,6 +67,24 @@ struct pnp_quirk pnp_quirks[] = {
*/
{ 0x0100561e /* GRV0001 */, 0,
PNP_QUIRK_WRITE_REG, 0xf2, 0xff },
+ /*
+ * An emu8000 does not give us other than the first
+ * port.
+ */
+ { 0x26008c0e /* SB16 */, 0x21008c0e,
+ PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
+ { 0x42008c0e /* SB32(CTL0042) */, 0x21008c0e,
+ PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
+ { 0x44008c0e /* SB32(CTL0044) */, 0x21008c0e,
+ PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
+ { 0x49008c0e /* SB32(CTL0049) */, 0x21008c0e,
+ PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
+ { 0xf1008c0e /* SB32(CTL00f1) */, 0x21008c0e,
+ PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
+ { 0xc1008c0e /* SB64(CTL00c1) */, 0x22008c0e,
+ PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
+ { 0xe4008c0e /* SB64(CTL00e4) */, 0x22008c0e,
+ PNP_QUIRK_EXTRA_IO, 0x400, 0x800 },
{ 0 }
};
@@ -363,8 +382,8 @@ pnp_set_config(void *arg, struct isa_config *config, int enable)
/*
* Process quirks for a logical device.. The card must be in Config state.
*/
-static void
-pnp_check_quirks(u_int32_t vendor_id, u_int32_t logical_id, int ldn)
+void
+pnp_check_quirks(u_int32_t vendor_id, u_int32_t logical_id, int ldn, struct isa_config *config)
{
struct pnp_quirk *qp;
@@ -377,6 +396,22 @@ pnp_check_quirks(u_int32_t vendor_id, u_int32_t logical_id, int ldn)
pnp_write(PNP_SET_LDN, ldn);
pnp_write(qp->arg1, qp->arg2);
break;
+ case PNP_QUIRK_EXTRA_IO:
+ if (config == NULL)
+ break;
+ if (qp->arg1 != 0) {
+ config->ic_nport++;
+ config->ic_port[config->ic_nport - 1] = config->ic_port[0];
+ config->ic_port[config->ic_nport - 1].ir_start += qp->arg1;
+ config->ic_port[config->ic_nport - 1].ir_end += qp->arg1;
+ }
+ if (qp->arg2 != 0) {
+ config->ic_nport++;
+ config->ic_port[config->ic_nport - 1] = config->ic_port[0];
+ config->ic_port[config->ic_nport - 1].ir_start += qp->arg2;
+ config->ic_port[config->ic_nport - 1].ir_end += qp->arg2;
+ }
+ break;
}
}
}
@@ -461,7 +496,8 @@ pnp_create_devices(device_t parent, pnp_id *p, int csn,
*/
if (startres) {
pnp_parse_resources(dev, startres,
- resinfo - startres - 1);
+ resinfo - startres - 1,
+ p->vendor_id, logical_id, ldn);
dev = 0;
startres = 0;
}
@@ -471,7 +507,7 @@ pnp_create_devices(device_t parent, pnp_id *p, int csn,
* resources.
*/
bcopy(resinfo, &logical_id, 4);
- pnp_check_quirks(p->vendor_id, logical_id, ldn);
+ pnp_check_quirks(p->vendor_id, logical_id, ldn, NULL);
compat_id = 0;
dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1);
if (desc)
@@ -502,7 +538,8 @@ pnp_create_devices(device_t parent, pnp_id *p, int csn,
break;
}
pnp_parse_resources(dev, startres,
- resinfo - startres - 1);
+ resinfo - startres - 1,
+ p->vendor_id, logical_id, ldn);
dev = 0;
startres = 0;
scanning = 0;
diff --git a/sys/isa/pnpparse.c b/sys/isa/pnpparse.c
index c6c0869..dfb5c33 100644
--- a/sys/isa/pnpparse.c
+++ b/sys/isa/pnpparse.c
@@ -47,7 +47,7 @@
* Resource Data or it reaches the end of Resource Data.
*/
void
-pnp_parse_resources(device_t dev, u_char *resources, int len)
+pnp_parse_resources(device_t dev, u_char *resources, int len, u_int32_t vendor_id, u_int32_t logical_id, int ldn)
{
device_t parent = device_get_parent(dev);
u_char tag, *resp, *resinfo;
@@ -189,6 +189,9 @@ pnp_parse_resources(device_t dev, u_char *resources, int len)
config->ic_port[config->ic_nport].ir_align =
resinfo[5];
config->ic_nport++;
+ pnp_check_quirks(vendor_id,
+ logical_id,
+ ldn, config);
break;
case PNP_TAG_IO_FIXED:
diff --git a/sys/isa/pnpvar.h b/sys/isa/pnpvar.h
index 0e7dee9..050adb9 100644
--- a/sys/isa/pnpvar.h
+++ b/sys/isa/pnpvar.h
@@ -53,7 +53,9 @@ u_char pnp_read(int d); /* currently unused, but who knows... */
| (PNP_HEXTONUM(s[5]) << 28))
char *pnp_eisaformat(u_int32_t id);
-void pnp_parse_resources(device_t dev, u_char *resources, int len);
+void pnp_parse_resources(device_t dev, u_char *resources, int len, u_int32_t vendor_id, u_int32_t logical_id, int ldn);
+
+void pnp_check_quirks(u_int32_t vendor_id, u_int32_t logical_id, int ldn, struct isa_config *config);
#endif /* _KERNEL */
diff --git a/sys/sys/soundcard.h b/sys/sys/soundcard.h
index e608b34..762250d 100644
--- a/sys/sys/soundcard.h
+++ b/sys/sys/soundcard.h
@@ -91,6 +91,8 @@
#define SNDCARD_PSEUDO_MSS 24
#define SNDCARD_AWE32 25
#define SNDCARD_NSS 26
+#define SNDCARD_UART16550 27
+#define SNDCARD_OPL 28
#include <sys/types.h>
#ifndef _IOWR
@@ -704,6 +706,8 @@ typedef struct {
#define SNDCTL_MIDI_PRETIME _IOWR('m', 0, int)
#define SNDCTL_MIDI_MPUMODE _IOWR('m', 1, int)
#define SNDCTL_MIDI_MPUCMD _IOWR('m', 2, mpu_command_rec)
+#define MIOSPASSTHRU _IOWR('m', 3, int)
+#define MIOGPASSTHRU _IOWR('m', 4, int)
/*
* IOCTL commands for /dev/dsp and /dev/audio
OpenPOWER on IntegriCloud