Finally merge newmidi.
(I had been busy for my own research activity until the last weekend) Supported devices: SB Midi Port (sbc + midi) SB OPL3 (sbc + midi) 16550 UART (midi, needs a trick in your hint) CS461x Midi Port (csa + midi) OSS-compatible sequencer (seq) Supported playing software: playmidi (We definitely need more) Notes: /dev/midistat now reports installed midi drivers. /dev/sndstat reports only pcm drivers. We need the new name(pcmstat?). EMU8000(SB AWE) does not sound yet but does get probed so that the OPL3 synth on an AWE card works. TODO: MSS/PCI bridge drivers Midi-tty interface to support general serial devices Modules
This commit is contained in:
parent
a611641f01
commit
fb0ef52838
etc
sys
amd64/amd64
conf
dev/sound
isa
midi
pci
pcm
i386
isa
sys
@ -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
|
||||
;;
|
||||
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
2037
sys/dev/sound/isa/emu8000.c
Normal file
2037
sys/dev/sound/isa/emu8000.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
|
523
sys/dev/sound/isa/gusmidi.c
Normal file
523
sys/dev/sound/isa/gusmidi.c
Normal file
@ -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);
|
835
sys/dev/sound/isa/mpu.c
Normal file
835
sys/dev/sound/isa/mpu.c
Normal file
@ -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);
|
@ -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;
|
||||
|
1904
sys/dev/sound/isa/opl.c
Normal file
1904
sys/dev/sound/isa/opl.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
524
sys/dev/sound/isa/uartsio.c
Normal file
524
sys/dev/sound/isa/uartsio.c
Normal file
@ -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);
|
702
sys/dev/sound/midi/midi.c
Normal file
702
sys/dev/sound/midi/midi.c
Normal file
@ -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);
|
||||
}
|
294
sys/dev/sound/midi/midi.h
Normal file
294
sys/dev/sound/midi/midi.h
Normal file
@ -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_ */
|
424
sys/dev/sound/midi/midibuf.c
Normal file
424
sys/dev/sound/midi/midibuf.c
Normal file
@ -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);
|
||||
}
|
64
sys/dev/sound/midi/midibuf.h
Normal file
64
sys/dev/sound/midi/midibuf.h
Normal file
@ -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);
|
632
sys/dev/sound/midi/midisynth.c
Normal file
632
sys/dev/sound/midi/midisynth.c
Normal file
@ -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);
|
||||
}
|
97
sys/dev/sound/midi/midisynth.h
Normal file
97
sys/dev/sound/midi/midisynth.h
Normal file
@ -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;
|
34
sys/dev/sound/midi/miditypes.h
Normal file
34
sys/dev/sound/midi/miditypes.h
Normal file
@ -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);
|
2040
sys/dev/sound/midi/sequencer.c
Normal file
2040
sys/dev/sound/midi/sequencer.c
Normal file
File diff suppressed because it is too large
Load Diff
234
sys/dev/sound/midi/sequencer.h
Normal file
234
sys/dev/sound/midi/sequencer.h
Normal file
@ -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_ */
|
@ -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);
|
||||
|
554
sys/dev/sound/pci/csamidi.c
Normal file
554
sys/dev/sound/pci/csamidi.c
Normal file
@ -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);
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user