// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( C ) by Paul Barton - Davis 1998 - 1999
*/
/* The low level driver for the WaveFront ICS2115 MIDI interface(s)
*
* Note that there is also an MPU - 401 emulation ( actually , a UART - 401
* emulation ) on the CS4232 on the Tropez and Tropez Plus . This code
* has nothing to do with that interface at all .
*
* The interface is essentially just a UART - 401 , but is has the
* interesting property of supporting what Turtle Beach called
* " Virtual MIDI " mode . In this mode , there are effectively * two *
* MIDI buses accessible via the interface , one that is routed
* solely to / from the external WaveFront synthesizer and the other
* corresponding to the pin / socket connector used to link external
* MIDI devices to the board .
*
* This driver fully supports this mode , allowing two distinct MIDI
* busses to be used completely independently , giving 32 channels of
* MIDI routing , 16 to the WaveFront synth and 16 to the external MIDI
* bus . The devices are named / dev / snd / midiCnD0 and / dev / snd / midiCnD1 ,
* where ` n ' is the card number . Note that the device numbers may be
* something other than 0 and 1 if the CS4232 UART / MPU - 401 interface
* is enabled .
*
* Switching between the two is accomplished externally by the driver
* using the two otherwise unused MIDI bytes . See the code for more details .
*
* NOTE : VIRTUAL MIDI MODE IS ON BY DEFAULT ( see lowlevel / isa / wavefront . c )
*
* The main reason to turn off Virtual MIDI mode is when you want to
* tightly couple the WaveFront synth with an external MIDI
* device . You won ' t be able to distinguish the source of any MIDI
* data except via SysEx ID , but thats probably OK , since for the most
* part , the WaveFront won ' t be sending any MIDI data at all .
*
* The main reason to turn on Virtual MIDI Mode is to provide two
* completely independent 16 - channel MIDI buses , one to the
* WaveFront and one to any external MIDI devices . Given the 32
* voice nature of the WaveFront , its pretty easy to find a use
* for all 16 channels driving just that synth .
*
*/
#include <linux/io.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <sound/core.h>
#include <sound/snd_wavefront.h>
static inline int
wf_mpu_status (snd_wavefront_midi_t *midi)
{
return inb (midi->mpu_status_port);
}
static inline int
input_avail (snd_wavefront_midi_t *midi)
{
return !(wf_mpu_status(midi) & INPUT_AVAIL);
}
static inline int
output_ready (snd_wavefront_midi_t *midi)
{
return !(wf_mpu_status(midi) & OUTPUT_READY);
}
static inline int
read_data (snd_wavefront_midi_t *midi)
{
return inb (midi->mpu_data_port);
}
static inline void
write_data (snd_wavefront_midi_t *midi, unsigned char byte)
{
outb (byte, midi->mpu_data_port);
}
static snd_wavefront_midi_t *
get_wavefront_midi (struct snd_rawmidi_substream *substream)
{
struct snd_card *card;
snd_wavefront_card_t *acard;
if (substream == NULL || substream->rmidi == NULL)
return NULL;
card = substream->rmidi->card;
if (card == NULL)
return NULL;
if (card->private_data == NULL)
return NULL;
acard = card->private_data;
return &acard->wavefront.midi;
}
static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
{
snd_wavefront_midi_t *midi = &card->wavefront.midi;
snd_wavefront_mpu_id mpu;
unsigned long flags;
unsigned char midi_byte;
int max = 256 , mask = 1 ;
int timeout;
/* Its not OK to try to change the status of "virtuality" of
the MIDI interface while we ' re outputting stuff . See
snd_wavefront_midi_ { enable , disable } _ virtual ( ) for the
other half of this .
The first loop attempts to flush any data from the
current output device , and then the second
emits the switch byte ( if necessary ) , and starts
outputting data for the output device currently in use .
*/
if (midi->substream_output[midi->output_mpu] == NULL) {
goto __second;
}
while (max > 0 ) {
/* XXX fix me - no hard timing loops allowed! */
for (timeout = 30000 ; timeout > 0 ; timeout--) {
if (output_ready (midi))
break ;
}
spin_lock_irqsave (&midi->virtual , flags);
if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0 ) {
spin_unlock_irqrestore (&midi->virtual , flags);
goto __second;
}
if (output_ready (midi)) {
if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1 ) == 1 ) {
if (!midi->isvirtual ||
(midi_byte != WF_INTERNAL_SWITCH &&
midi_byte != WF_EXTERNAL_SWITCH))
write_data(midi, midi_byte);
max--;
} else {
if (midi->istimer) {
if (--midi->istimer <= 0 )
timer_delete(&midi->timer);
}
midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
spin_unlock_irqrestore (&midi->virtual , flags);
goto __second;
}
} else {
spin_unlock_irqrestore (&midi->virtual , flags);
return ;
}
spin_unlock_irqrestore (&midi->virtual , flags);
}
__second:
if (midi->substream_output[!midi->output_mpu] == NULL) {
return ;
}
while (max > 0 ) {
/* XXX fix me - no hard timing loops allowed! */
for (timeout = 30000 ; timeout > 0 ; timeout--) {
if (output_ready (midi))
break ;
}
spin_lock_irqsave (&midi->virtual , flags);
if (!midi->isvirtual)
mask = 0 ;
mpu = midi->output_mpu ^ mask;
mask = 0 ; /* don't invert the value from now */
if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0 ) {
spin_unlock_irqrestore (&midi->virtual , flags);
return ;
}
if (snd_rawmidi_transmit_empty(midi->substream_output[mpu]))
goto __timer;
if (output_ready (midi)) {
if (mpu != midi->output_mpu) {
write_data(midi, mpu == internal_mpu ?
WF_INTERNAL_SWITCH :
WF_EXTERNAL_SWITCH);
midi->output_mpu = mpu;
} else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1 ) == 1 ) {
if (!midi->isvirtual ||
(midi_byte != WF_INTERNAL_SWITCH &&
midi_byte != WF_EXTERNAL_SWITCH))
write_data(midi, midi_byte);
max--;
} else {
__timer:
if (midi->istimer) {
if (--midi->istimer <= 0 )
timer_delete(&midi->timer);
}
midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
spin_unlock_irqrestore (&midi->virtual , flags);
return ;
}
} else {
spin_unlock_irqrestore (&midi->virtual , flags);
return ;
}
spin_unlock_irqrestore (&midi->virtual , flags);
}
}
static int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream)
{
unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
if (snd_BUG_ON(!substream || !substream->rmidi))
return -ENXIO;
if (snd_BUG_ON(!substream->rmidi->private_data))
return -ENXIO;
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
midi = get_wavefront_midi(substream);
if (!midi)
return -EIO;
spin_lock_irqsave (&midi->open, flags);
midi->mode[mpu] |= MPU401_MODE_INPUT;
midi->substream_input[mpu] = substream;
spin_unlock_irqrestore (&midi->open, flags);
return 0 ;
}
static int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substream)
{
unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
if (snd_BUG_ON(!substream || !substream->rmidi))
return -ENXIO;
if (snd_BUG_ON(!substream->rmidi->private_data))
return -ENXIO;
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
midi = get_wavefront_midi(substream);
if (!midi)
return -EIO;
spin_lock_irqsave (&midi->open, flags);
midi->mode[mpu] |= MPU401_MODE_OUTPUT;
midi->substream_output[mpu] = substream;
spin_unlock_irqrestore (&midi->open, flags);
return 0 ;
}
static int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substream)
{
unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
if (snd_BUG_ON(!substream || !substream->rmidi))
return -ENXIO;
if (snd_BUG_ON(!substream->rmidi->private_data))
return -ENXIO;
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
midi = get_wavefront_midi(substream);
if (!midi)
return -EIO;
spin_lock_irqsave (&midi->open, flags);
midi->mode[mpu] &= ~MPU401_MODE_INPUT;
spin_unlock_irqrestore (&midi->open, flags);
return 0 ;
}
static int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substream)
{
unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
if (snd_BUG_ON(!substream || !substream->rmidi))
return -ENXIO;
if (snd_BUG_ON(!substream->rmidi->private_data))
return -ENXIO;
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
midi = get_wavefront_midi(substream);
if (!midi)
return -EIO;
spin_lock_irqsave (&midi->open, flags);
midi->mode[mpu] &= ~MPU401_MODE_OUTPUT;
spin_unlock_irqrestore (&midi->open, flags);
return 0 ;
}
static void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
if (substream == NULL || substream->rmidi == NULL)
return ;
if (substream->rmidi->private_data == NULL)
return ;
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
midi = get_wavefront_midi(substream);
if (!midi)
return ;
spin_lock_irqsave (&midi->virtual , flags);
if (up) {
midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER;
} else {
midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER;
}
spin_unlock_irqrestore (&midi->virtual , flags);
}
static void snd_wavefront_midi_output_timer(struct timer_list *t)
{
snd_wavefront_midi_t *midi = timer_container_of(midi, t, timer);
snd_wavefront_card_t *card = midi->timer_card;
unsigned long flags;
spin_lock_irqsave (&midi->virtual , flags);
mod_timer(&midi->timer, 1 + jiffies);
spin_unlock_irqrestore (&midi->virtual , flags);
snd_wavefront_midi_output_write(card);
}
static void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
if (substream == NULL || substream->rmidi == NULL)
return ;
if (substream->rmidi->private_data == NULL)
return ;
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
midi = get_wavefront_midi(substream);
if (!midi)
return ;
spin_lock_irqsave (&midi->virtual , flags);
if (up) {
if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0 ) {
if (!midi->istimer) {
timer_setup(&midi->timer,
snd_wavefront_midi_output_timer,
0 );
mod_timer(&midi->timer, 1 + jiffies);
}
midi->istimer++;
midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER;
}
} else {
midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
}
spin_unlock_irqrestore (&midi->virtual , flags);
if (up)
snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data);
}
void
snd_wavefront_midi_interrupt (snd_wavefront_card_t *card)
{
unsigned long flags;
snd_wavefront_midi_t *midi;
static struct snd_rawmidi_substream *substream = NULL;
static int mpu = external_mpu;
int max = 128 ;
unsigned char byte;
midi = &card->wavefront.midi;
if (!input_avail (midi)) { /* not for us */
snd_wavefront_midi_output_write(card);
return ;
}
spin_lock_irqsave (&midi->virtual , flags);
while (--max) {
if (input_avail (midi)) {
byte = read_data (midi);
if (midi->isvirtual) {
if (byte == WF_EXTERNAL_SWITCH) {
substream = midi->substream_input[external_mpu];
mpu = external_mpu;
} else if (byte == WF_INTERNAL_SWITCH) {
substream = midi->substream_output[internal_mpu];
mpu = internal_mpu;
} /* else just leave it as it is */
} else {
substream = midi->substream_input[internal_mpu];
mpu = internal_mpu;
}
if (substream == NULL) {
continue ;
}
if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) {
snd_rawmidi_receive(substream, &byte, 1 );
}
} else {
break ;
}
}
spin_unlock_irqrestore (&midi->virtual , flags);
snd_wavefront_midi_output_write(card);
}
void
snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card)
{
unsigned long flags;
spin_lock_irqsave (&card->wavefront.midi.virtual , flags);
card->wavefront.midi.isvirtual = 1 ;
card->wavefront.midi.output_mpu = internal_mpu;
card->wavefront.midi.input_mpu = internal_mpu;
spin_unlock_irqrestore (&card->wavefront.midi.virtual , flags);
}
void
snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)
{
unsigned long flags;
spin_lock_irqsave (&card->wavefront.midi.virtual , flags);
// snd_wavefront_midi_input_close (card->ics2115_external_rmidi);
// snd_wavefront_midi_output_close (card->ics2115_external_rmidi);
card->wavefront.midi.isvirtual = 0 ;
spin_unlock_irqrestore (&card->wavefront.midi.virtual , flags);
}
int
snd_wavefront_midi_start (snd_wavefront_card_t *card)
{
int ok, i;
unsigned char rbuf[4 ], wbuf[4 ];
snd_wavefront_t *dev;
snd_wavefront_midi_t *midi;
dev = &card->wavefront;
midi = &dev->midi;
/* The ICS2115 MPU-401 interface doesn't do anything
until its set into UART mode .
*/
/* XXX fix me - no hard timing loops allowed! */
for (i = 0 ; i < 30000 && !output_ready (midi); i++);
if (!output_ready (midi)) {
dev_err(card->wavefront.card->dev,
"MIDI interface not ready for command\n" );
return -1 ;
}
/* Any interrupts received from now on
are owned by the MIDI side of things .
*/
dev->interrupts_are_midi = 1 ;
outb (UART_MODE_ON, midi->mpu_command_port);
for (ok = 0 , i = 50000 ; i > 0 && !ok; i--) {
if (input_avail (midi)) {
if (read_data (midi) == MPU_ACK) {
ok = 1 ;
break ;
}
}
}
if (!ok) {
dev_err(card->wavefront.card->dev,
"cannot set UART mode for MIDI interface" );
dev->interrupts_are_midi = 0 ;
return -1 ;
}
/* Route external MIDI to WaveFront synth (by default) */
if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) {
dev_warn(card->wavefront.card->dev,
"can't enable MIDI-IN-2-synth routing.\n" );
/* XXX error ? */
}
/* Turn on Virtual MIDI, but first *always* turn it off,
since otherwise consecutive reloads of the driver will
never cause the hardware to generate the initial " internal " or
" external " source bytes in the MIDI data stream . This
is pretty important , since the internal hardware generally will
be used to generate none or very little MIDI output , and
thus the only source of MIDI data is actually external . Without
the switch bytes , the driver will think it all comes from
the internal interface . Duh .
*/
if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) {
dev_warn(card->wavefront.card->dev,
"virtual MIDI mode not disabled\n" );
return 0 ; /* We're OK, but missing the external MIDI dev */
}
snd_wavefront_midi_enable_virtual (card);
if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) {
dev_warn(card->wavefront.card->dev,
"cannot enable virtual MIDI mode.\n" );
snd_wavefront_midi_disable_virtual (card);
}
return 0 ;
}
const struct snd_rawmidi_ops snd_wavefront_midi_output =
{
.open = snd_wavefront_midi_output_open,
.close = snd_wavefront_midi_output_close,
.trigger = snd_wavefront_midi_output_trigger,
};
const struct snd_rawmidi_ops snd_wavefront_midi_input =
{
.open = snd_wavefront_midi_input_open,
.close = snd_wavefront_midi_input_close,
.trigger = snd_wavefront_midi_input_trigger,
};
Messung V0.5 in Prozent C=94 H=96 G=94
¤ Dauer der Verarbeitung: 0.6 Sekunden
¤
*© Formatika GbR, Deutschland