This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / sound / pci / echoaudio / midi.c
diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c
new file mode 100644 (file)
index 0000000..e31f0f1
--- /dev/null
@@ -0,0 +1,327 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+/******************************************************************************
+       MIDI lowlevel code
+******************************************************************************/
+
+/* Start and stop Midi input */
+static int enable_midi_input(struct echoaudio *chip, char enable)
+{
+       DE_MID(("enable_midi_input(%d)\n", enable));
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       if (enable) {
+               chip->mtc_state = MIDI_IN_STATE_NORMAL;
+               chip->comm_page->flags |=
+                       __constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT);
+       } else
+               chip->comm_page->flags &=
+                       ~__constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT);
+
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+/* Send a buffer full of MIDI data to the DSP
+Returns how many actually written or < 0 on error */
+static int write_midi(struct echoaudio *chip, u8 *data, int bytes)
+{
+       snd_assert(bytes > 0 && bytes < MIDI_OUT_BUFFER_SIZE, return -EINVAL);
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       /* HF4 indicates that it is safe to write MIDI output data */
+       if (! (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_REG_HF4))
+               return 0;
+
+       chip->comm_page->midi_output[0] = bytes;
+       memcpy(&chip->comm_page->midi_output[1], data, bytes);
+       chip->comm_page->midi_out_free_count = 0;
+       clear_handshake(chip);
+       send_vector(chip, DSP_VC_MIDI_WRITE);
+       DE_MID(("write_midi: %d\n", bytes));
+       return bytes;
+}
+
+
+
+/* Run the state machine for MIDI input data
+MIDI time code sync isn't supported by this code right now, but you still need
+this state machine to parse the incoming MIDI data stream.  Every time the DSP
+sees a 0xF1 byte come in, it adds the DSP sample position to the MIDI data
+stream. The DSP sample position is represented as a 32 bit unsigned value,
+with the high 16 bits first, followed by the low 16 bits. Since these aren't
+real MIDI bytes, the following logic is needed to skip them. */
+static inline int mtc_process_data(struct echoaudio *chip, short midi_byte)
+{
+       switch (chip->mtc_state) {
+       case MIDI_IN_STATE_NORMAL:
+               if (midi_byte == 0xF1)
+                       chip->mtc_state = MIDI_IN_STATE_TS_HIGH;
+               break;
+       case MIDI_IN_STATE_TS_HIGH:
+               chip->mtc_state = MIDI_IN_STATE_TS_LOW;
+               return MIDI_IN_SKIP_DATA;
+               break;
+       case MIDI_IN_STATE_TS_LOW:
+               chip->mtc_state = MIDI_IN_STATE_F1_DATA;
+               return MIDI_IN_SKIP_DATA;
+               break;
+       case MIDI_IN_STATE_F1_DATA:
+               chip->mtc_state = MIDI_IN_STATE_NORMAL;
+               break;
+       }
+       return 0;
+}
+
+
+
+/* This function is called from the IRQ handler and it reads the midi data
+from the DSP's buffer.  It returns the number of bytes received. */
+static int midi_service_irq(struct echoaudio *chip)
+{
+       short int count, midi_byte, i, received;
+
+       /* The count is at index 0, followed by actual data */
+       count = le16_to_cpu(chip->comm_page->midi_input[0]);
+
+       snd_assert(count < MIDI_IN_BUFFER_SIZE, return 0);
+
+       /* Get the MIDI data from the comm page */
+       i = 1;
+       received = 0;
+       for (i = 1; i <= count; i++) {
+               /* Get the MIDI byte */
+               midi_byte = le16_to_cpu(chip->comm_page->midi_input[i]);
+
+               /* Parse the incoming MIDI stream. The incoming MIDI data
+               consists of MIDI bytes and timestamps for the MIDI time code
+               0xF1 bytes. mtc_process_data() is a little state machine that
+               parses the stream. If you get MIDI_IN_SKIP_DATA back, then
+               this is a timestamp byte, not a MIDI byte, so don't store it
+               in the MIDI input buffer. */
+               if (mtc_process_data(chip, midi_byte) == MIDI_IN_SKIP_DATA)
+                       continue;
+
+               chip->midi_buffer[received++] = (u8)midi_byte;
+       }
+
+       return received;
+}
+
+
+
+
+/******************************************************************************
+       MIDI interface
+******************************************************************************/
+
+static int snd_echo_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+       struct echoaudio *chip = substream->rmidi->private_data;
+
+       chip->midi_in = substream;
+       DE_MID(("rawmidi_iopen\n"));
+       return 0;
+}
+
+
+
+static void snd_echo_midi_input_trigger(struct snd_rawmidi_substream *substream,
+                                       int up)
+{
+       struct echoaudio *chip = substream->rmidi->private_data;
+
+       if (up != chip->midi_input_enabled) {
+               spin_lock_irq(&chip->lock);
+               enable_midi_input(chip, up);
+               spin_unlock_irq(&chip->lock);
+               chip->midi_input_enabled = up;
+       }
+}
+
+
+
+static int snd_echo_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+       struct echoaudio *chip = substream->rmidi->private_data;
+
+       chip->midi_in = NULL;
+       DE_MID(("rawmidi_iclose\n"));
+       return 0;
+}
+
+
+
+static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+       struct echoaudio *chip = substream->rmidi->private_data;
+
+       chip->tinuse = 0;
+       chip->midi_full = 0;
+       chip->midi_out = substream;
+       DE_MID(("rawmidi_oopen\n"));
+       return 0;
+}
+
+
+
+static void snd_echo_midi_output_write(unsigned long data)
+{
+       struct echoaudio *chip = (struct echoaudio *)data;
+       unsigned long flags;
+       int bytes, sent, time;
+       unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1];
+
+       DE_MID(("snd_echo_midi_output_write\n"));
+       /* No interrupts are involved: we have to check at regular intervals
+       if the card's output buffer has room for new data. */
+       sent = bytes = 0;
+       spin_lock_irqsave(&chip->lock, flags);
+       chip->midi_full = 0;
+       if (chip->midi_out && !snd_rawmidi_transmit_empty(chip->midi_out)) {
+               bytes = snd_rawmidi_transmit_peek(chip->midi_out, buf,
+                                                 MIDI_OUT_BUFFER_SIZE - 1);
+               DE_MID(("Try to send %d bytes...\n", bytes));
+               sent = write_midi(chip, buf, bytes);
+               if (sent < 0) {
+                       snd_printk(KERN_ERR "write_midi() error %d\n", sent);
+                       /* retry later */
+                       sent = 9000;
+                       chip->midi_full = 1;
+               } else if (sent > 0) {
+                       DE_MID(("%d bytes sent\n", sent));
+                       snd_rawmidi_transmit_ack(chip->midi_out, sent);
+               } else {
+                       /* Buffer is full. DSP's internal buffer is 64 (128 ?)
+                       bytes long. Let's wait until half of them are sent */
+                       DE_MID(("Full\n"));
+                       sent = 32;
+                       chip->midi_full = 1;
+               }
+       }
+
+       /* We restart the timer only if there is some data left to send */
+       if (!snd_rawmidi_transmit_empty(chip->midi_out) && chip->tinuse) {
+               /* The timer will expire slightly after the data has been
+                  sent */
+               time = (sent << 3) / 25 + 1;    /* 8/25=0.32ms to send a byte */
+               mod_timer(&chip->timer, jiffies + (time * HZ + 999) / 1000);
+               DE_MID(("Timer armed(%d)\n", ((time * HZ + 999) / 1000)));
+       }
+       spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+
+static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream,
+                                        int up)
+{
+       struct echoaudio *chip = substream->rmidi->private_data;
+
+       DE_MID(("snd_echo_midi_output_trigger(%d)\n", up));
+       spin_lock_irq(&chip->lock);
+       if (up) {
+               if (!chip->tinuse) {
+                       init_timer(&chip->timer);
+                       chip->timer.function = snd_echo_midi_output_write;
+                       chip->timer.data = (unsigned long)chip;
+                       chip->tinuse = 1;
+               }
+       } else {
+               if (chip->tinuse) {
+                       del_timer(&chip->timer);
+                       chip->tinuse = 0;
+                       DE_MID(("Timer removed\n"));
+               }
+       }
+       spin_unlock_irq(&chip->lock);
+
+       if (up && !chip->midi_full)
+               snd_echo_midi_output_write((unsigned long)chip);
+}
+
+
+
+static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+       struct echoaudio *chip = substream->rmidi->private_data;
+
+       chip->midi_out = NULL;
+       DE_MID(("rawmidi_oclose\n"));
+       return 0;
+}
+
+
+
+static struct snd_rawmidi_ops snd_echo_midi_input = {
+       .open = snd_echo_midi_input_open,
+       .close = snd_echo_midi_input_close,
+       .trigger = snd_echo_midi_input_trigger,
+};
+
+static struct snd_rawmidi_ops snd_echo_midi_output = {
+       .open = snd_echo_midi_output_open,
+       .close = snd_echo_midi_output_close,
+       .trigger = snd_echo_midi_output_trigger,
+};
+
+
+
+/* <--snd_echo_probe() */
+static int __devinit snd_echo_midi_create(struct snd_card *card,
+                                         struct echoaudio *chip)
+{
+       int err;
+
+       if ((err = snd_rawmidi_new(card, card->shortname, 0, 1, 1,
+                                  &chip->rmidi)) < 0)
+               return err;
+
+       strcpy(chip->rmidi->name, card->shortname);
+       chip->rmidi->private_data = chip;
+
+       snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+                           &snd_echo_midi_input);
+       snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                           &snd_echo_midi_output);
+
+       chip->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+               SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+       DE_INIT(("MIDI ok\n"));
+       return 0;
+}