Path: blob/master/sound/isa/wavefront/wavefront_midi.c
10817 views
/*1* Copyright (C) by Paul Barton-Davis 1998-19992*3* This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)4* Version 2 (June 1991). See the "COPYING" file distributed with this5* software for more info.6*/78/* The low level driver for the WaveFront ICS2115 MIDI interface(s)9*10* Note that there is also an MPU-401 emulation (actually, a UART-40111* emulation) on the CS4232 on the Tropez and Tropez Plus. This code12* has nothing to do with that interface at all.13*14* The interface is essentially just a UART-401, but is has the15* interesting property of supporting what Turtle Beach called16* "Virtual MIDI" mode. In this mode, there are effectively *two*17* MIDI buses accessible via the interface, one that is routed18* solely to/from the external WaveFront synthesizer and the other19* corresponding to the pin/socket connector used to link external20* MIDI devices to the board.21*22* This driver fully supports this mode, allowing two distinct MIDI23* busses to be used completely independently, giving 32 channels of24* MIDI routing, 16 to the WaveFront synth and 16 to the external MIDI25* bus. The devices are named /dev/snd/midiCnD0 and /dev/snd/midiCnD1,26* where `n' is the card number. Note that the device numbers may be27* something other than 0 and 1 if the CS4232 UART/MPU-401 interface28* is enabled.29*30* Switching between the two is accomplished externally by the driver31* using the two otherwise unused MIDI bytes. See the code for more details.32*33* NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see lowlevel/isa/wavefront.c)34*35* The main reason to turn off Virtual MIDI mode is when you want to36* tightly couple the WaveFront synth with an external MIDI37* device. You won't be able to distinguish the source of any MIDI38* data except via SysEx ID, but thats probably OK, since for the most39* part, the WaveFront won't be sending any MIDI data at all.40*41* The main reason to turn on Virtual MIDI Mode is to provide two42* completely independent 16-channel MIDI buses, one to the43* WaveFront and one to any external MIDI devices. Given the 3244* voice nature of the WaveFront, its pretty easy to find a use45* for all 16 channels driving just that synth.46*47*/4849#include <asm/io.h>50#include <linux/init.h>51#include <linux/time.h>52#include <linux/wait.h>53#include <sound/core.h>54#include <sound/snd_wavefront.h>5556static inline int57wf_mpu_status (snd_wavefront_midi_t *midi)5859{60return inb (midi->mpu_status_port);61}6263static inline int64input_avail (snd_wavefront_midi_t *midi)6566{67return !(wf_mpu_status(midi) & INPUT_AVAIL);68}6970static inline int71output_ready (snd_wavefront_midi_t *midi)7273{74return !(wf_mpu_status(midi) & OUTPUT_READY);75}7677static inline int78read_data (snd_wavefront_midi_t *midi)7980{81return inb (midi->mpu_data_port);82}8384static inline void85write_data (snd_wavefront_midi_t *midi, unsigned char byte)8687{88outb (byte, midi->mpu_data_port);89}9091static snd_wavefront_midi_t *92get_wavefront_midi (struct snd_rawmidi_substream *substream)9394{95struct snd_card *card;96snd_wavefront_card_t *acard;9798if (substream == NULL || substream->rmidi == NULL)99return NULL;100101card = substream->rmidi->card;102103if (card == NULL)104return NULL;105106if (card->private_data == NULL)107return NULL;108109acard = card->private_data;110111return &acard->wavefront.midi;112}113114static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)115{116snd_wavefront_midi_t *midi = &card->wavefront.midi;117snd_wavefront_mpu_id mpu;118unsigned long flags;119unsigned char midi_byte;120int max = 256, mask = 1;121int timeout;122123/* Its not OK to try to change the status of "virtuality" of124the MIDI interface while we're outputting stuff. See125snd_wavefront_midi_{enable,disable}_virtual () for the126other half of this.127128The first loop attempts to flush any data from the129current output device, and then the second130emits the switch byte (if necessary), and starts131outputting data for the output device currently in use.132*/133134if (midi->substream_output[midi->output_mpu] == NULL) {135goto __second;136}137138while (max > 0) {139140/* XXX fix me - no hard timing loops allowed! */141142for (timeout = 30000; timeout > 0; timeout--) {143if (output_ready (midi))144break;145}146147spin_lock_irqsave (&midi->virtual, flags);148if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) {149spin_unlock_irqrestore (&midi->virtual, flags);150goto __second;151}152if (output_ready (midi)) {153if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) {154if (!midi->isvirtual ||155(midi_byte != WF_INTERNAL_SWITCH &&156midi_byte != WF_EXTERNAL_SWITCH))157write_data(midi, midi_byte);158max--;159} else {160if (midi->istimer) {161if (--midi->istimer <= 0)162del_timer(&midi->timer);163}164midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;165spin_unlock_irqrestore (&midi->virtual, flags);166goto __second;167}168} else {169spin_unlock_irqrestore (&midi->virtual, flags);170return;171}172spin_unlock_irqrestore (&midi->virtual, flags);173}174175__second:176177if (midi->substream_output[!midi->output_mpu] == NULL) {178return;179}180181while (max > 0) {182183/* XXX fix me - no hard timing loops allowed! */184185for (timeout = 30000; timeout > 0; timeout--) {186if (output_ready (midi))187break;188}189190spin_lock_irqsave (&midi->virtual, flags);191if (!midi->isvirtual)192mask = 0;193mpu = midi->output_mpu ^ mask;194mask = 0; /* don't invert the value from now */195if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) {196spin_unlock_irqrestore (&midi->virtual, flags);197return;198}199if (snd_rawmidi_transmit_empty(midi->substream_output[mpu]))200goto __timer;201if (output_ready (midi)) {202if (mpu != midi->output_mpu) {203write_data(midi, mpu == internal_mpu ?204WF_INTERNAL_SWITCH :205WF_EXTERNAL_SWITCH);206midi->output_mpu = mpu;207} else if (snd_rawmidi_transmit(midi->substream_output[mpu], &midi_byte, 1) == 1) {208if (!midi->isvirtual ||209(midi_byte != WF_INTERNAL_SWITCH &&210midi_byte != WF_EXTERNAL_SWITCH))211write_data(midi, midi_byte);212max--;213} else {214__timer:215if (midi->istimer) {216if (--midi->istimer <= 0)217del_timer(&midi->timer);218}219midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;220spin_unlock_irqrestore (&midi->virtual, flags);221return;222}223} else {224spin_unlock_irqrestore (&midi->virtual, flags);225return;226}227spin_unlock_irqrestore (&midi->virtual, flags);228}229}230231static int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream)232{233unsigned long flags;234snd_wavefront_midi_t *midi;235snd_wavefront_mpu_id mpu;236237if (snd_BUG_ON(!substream || !substream->rmidi))238return -ENXIO;239if (snd_BUG_ON(!substream->rmidi->private_data))240return -ENXIO;241242mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);243244if ((midi = get_wavefront_midi (substream)) == NULL)245return -EIO;246247spin_lock_irqsave (&midi->open, flags);248midi->mode[mpu] |= MPU401_MODE_INPUT;249midi->substream_input[mpu] = substream;250spin_unlock_irqrestore (&midi->open, flags);251252return 0;253}254255static int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substream)256{257unsigned long flags;258snd_wavefront_midi_t *midi;259snd_wavefront_mpu_id mpu;260261if (snd_BUG_ON(!substream || !substream->rmidi))262return -ENXIO;263if (snd_BUG_ON(!substream->rmidi->private_data))264return -ENXIO;265266mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);267268if ((midi = get_wavefront_midi (substream)) == NULL)269return -EIO;270271spin_lock_irqsave (&midi->open, flags);272midi->mode[mpu] |= MPU401_MODE_OUTPUT;273midi->substream_output[mpu] = substream;274spin_unlock_irqrestore (&midi->open, flags);275276return 0;277}278279static int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substream)280{281unsigned long flags;282snd_wavefront_midi_t *midi;283snd_wavefront_mpu_id mpu;284285if (snd_BUG_ON(!substream || !substream->rmidi))286return -ENXIO;287if (snd_BUG_ON(!substream->rmidi->private_data))288return -ENXIO;289290mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);291292if ((midi = get_wavefront_midi (substream)) == NULL)293return -EIO;294295spin_lock_irqsave (&midi->open, flags);296midi->mode[mpu] &= ~MPU401_MODE_INPUT;297spin_unlock_irqrestore (&midi->open, flags);298299return 0;300}301302static int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substream)303{304unsigned long flags;305snd_wavefront_midi_t *midi;306snd_wavefront_mpu_id mpu;307308if (snd_BUG_ON(!substream || !substream->rmidi))309return -ENXIO;310if (snd_BUG_ON(!substream->rmidi->private_data))311return -ENXIO;312313mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);314315if ((midi = get_wavefront_midi (substream)) == NULL)316return -EIO;317318spin_lock_irqsave (&midi->open, flags);319midi->mode[mpu] &= ~MPU401_MODE_OUTPUT;320spin_unlock_irqrestore (&midi->open, flags);321return 0;322}323324static void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)325{326unsigned long flags;327snd_wavefront_midi_t *midi;328snd_wavefront_mpu_id mpu;329330if (substream == NULL || substream->rmidi == NULL)331return;332333if (substream->rmidi->private_data == NULL)334return;335336mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);337338if ((midi = get_wavefront_midi (substream)) == NULL) {339return;340}341342spin_lock_irqsave (&midi->virtual, flags);343if (up) {344midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER;345} else {346midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER;347}348spin_unlock_irqrestore (&midi->virtual, flags);349}350351static void snd_wavefront_midi_output_timer(unsigned long data)352{353snd_wavefront_card_t *card = (snd_wavefront_card_t *)data;354snd_wavefront_midi_t *midi = &card->wavefront.midi;355unsigned long flags;356357spin_lock_irqsave (&midi->virtual, flags);358midi->timer.expires = 1 + jiffies;359add_timer(&midi->timer);360spin_unlock_irqrestore (&midi->virtual, flags);361snd_wavefront_midi_output_write(card);362}363364static void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)365{366unsigned long flags;367snd_wavefront_midi_t *midi;368snd_wavefront_mpu_id mpu;369370if (substream == NULL || substream->rmidi == NULL)371return;372373if (substream->rmidi->private_data == NULL)374return;375376mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);377378if ((midi = get_wavefront_midi (substream)) == NULL) {379return;380}381382spin_lock_irqsave (&midi->virtual, flags);383if (up) {384if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) {385if (!midi->istimer) {386init_timer(&midi->timer);387midi->timer.function = snd_wavefront_midi_output_timer;388midi->timer.data = (unsigned long) substream->rmidi->card->private_data;389midi->timer.expires = 1 + jiffies;390add_timer(&midi->timer);391}392midi->istimer++;393midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER;394}395} else {396midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;397}398spin_unlock_irqrestore (&midi->virtual, flags);399400if (up)401snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data);402}403404void405snd_wavefront_midi_interrupt (snd_wavefront_card_t *card)406407{408unsigned long flags;409snd_wavefront_midi_t *midi;410static struct snd_rawmidi_substream *substream = NULL;411static int mpu = external_mpu;412int max = 128;413unsigned char byte;414415midi = &card->wavefront.midi;416417if (!input_avail (midi)) { /* not for us */418snd_wavefront_midi_output_write(card);419return;420}421422spin_lock_irqsave (&midi->virtual, flags);423while (--max) {424425if (input_avail (midi)) {426byte = read_data (midi);427428if (midi->isvirtual) {429if (byte == WF_EXTERNAL_SWITCH) {430substream = midi->substream_input[external_mpu];431mpu = external_mpu;432} else if (byte == WF_INTERNAL_SWITCH) {433substream = midi->substream_output[internal_mpu];434mpu = internal_mpu;435} /* else just leave it as it is */436} else {437substream = midi->substream_input[internal_mpu];438mpu = internal_mpu;439}440441if (substream == NULL) {442continue;443}444445if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) {446snd_rawmidi_receive(substream, &byte, 1);447}448} else {449break;450}451}452spin_unlock_irqrestore (&midi->virtual, flags);453454snd_wavefront_midi_output_write(card);455}456457void458snd_wavefront_midi_enable_virtual (snd_wavefront_card_t *card)459460{461unsigned long flags;462463spin_lock_irqsave (&card->wavefront.midi.virtual, flags);464card->wavefront.midi.isvirtual = 1;465card->wavefront.midi.output_mpu = internal_mpu;466card->wavefront.midi.input_mpu = internal_mpu;467spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);468}469470void471snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)472473{474unsigned long flags;475476spin_lock_irqsave (&card->wavefront.midi.virtual, flags);477// snd_wavefront_midi_input_close (card->ics2115_external_rmidi);478// snd_wavefront_midi_output_close (card->ics2115_external_rmidi);479card->wavefront.midi.isvirtual = 0;480spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);481}482483int __devinit484snd_wavefront_midi_start (snd_wavefront_card_t *card)485486{487int ok, i;488unsigned char rbuf[4], wbuf[4];489snd_wavefront_t *dev;490snd_wavefront_midi_t *midi;491492dev = &card->wavefront;493midi = &dev->midi;494495/* The ICS2115 MPU-401 interface doesn't do anything496until its set into UART mode.497*/498499/* XXX fix me - no hard timing loops allowed! */500501for (i = 0; i < 30000 && !output_ready (midi); i++);502503if (!output_ready (midi)) {504snd_printk ("MIDI interface not ready for command\n");505return -1;506}507508/* Any interrupts received from now on509are owned by the MIDI side of things.510*/511512dev->interrupts_are_midi = 1;513514outb (UART_MODE_ON, midi->mpu_command_port);515516for (ok = 0, i = 50000; i > 0 && !ok; i--) {517if (input_avail (midi)) {518if (read_data (midi) == MPU_ACK) {519ok = 1;520break;521}522}523}524525if (!ok) {526snd_printk ("cannot set UART mode for MIDI interface");527dev->interrupts_are_midi = 0;528return -1;529}530531/* Route external MIDI to WaveFront synth (by default) */532533if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) {534snd_printk ("can't enable MIDI-IN-2-synth routing.\n");535/* XXX error ? */536}537538/* Turn on Virtual MIDI, but first *always* turn it off,539since otherwise consecutive reloads of the driver will540never cause the hardware to generate the initial "internal" or541"external" source bytes in the MIDI data stream. This542is pretty important, since the internal hardware generally will543be used to generate none or very little MIDI output, and544thus the only source of MIDI data is actually external. Without545the switch bytes, the driver will think it all comes from546the internal interface. Duh.547*/548549if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) {550snd_printk ("virtual MIDI mode not disabled\n");551return 0; /* We're OK, but missing the external MIDI dev */552}553554snd_wavefront_midi_enable_virtual (card);555556if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) {557snd_printk ("cannot enable virtual MIDI mode.\n");558snd_wavefront_midi_disable_virtual (card);559}560return 0;561}562563struct snd_rawmidi_ops snd_wavefront_midi_output =564{565.open = snd_wavefront_midi_output_open,566.close = snd_wavefront_midi_output_close,567.trigger = snd_wavefront_midi_output_trigger,568};569570struct snd_rawmidi_ops snd_wavefront_midi_input =571{572.open = snd_wavefront_midi_input_open,573.close = snd_wavefront_midi_input_close,574.trigger = snd_wavefront_midi_input_trigger,575};576577578579