linux/sound/isa/gus/gus_uart.c
<<
>>
Prefs
   1/*
   2 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   3 *  Routines for the GF1 MIDI interface - like UART 6850
   4 *
   5 *
   6 *   This program is free software; you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   This program is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *   GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program; if not, write to the Free Software
  18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19 *
  20 */
  21
  22#include <linux/delay.h>
  23#include <linux/interrupt.h>
  24#include <linux/time.h>
  25#include <sound/core.h>
  26#include <sound/gus.h>
  27
  28static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)
  29{
  30        int count;
  31        unsigned char stat, data, byte;
  32        unsigned long flags;
  33
  34        count = 10;
  35        while (count) {
  36                spin_lock_irqsave(&gus->uart_cmd_lock, flags);
  37                stat = snd_gf1_uart_stat(gus);
  38                if (!(stat & 0x01)) {   /* data in Rx FIFO? */
  39                        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  40                        count--;
  41                        continue;
  42                }
  43                count = 100;    /* arm counter to new value */
  44                data = snd_gf1_uart_get(gus);
  45                if (!(gus->gf1.uart_cmd & 0x80)) {
  46                        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  47                        continue;
  48                }                       
  49                if (stat & 0x10) {      /* framing error */
  50                        gus->gf1.uart_framing++;
  51                        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  52                        continue;
  53                }
  54                byte = snd_gf1_uart_get(gus);
  55                spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  56                snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
  57                if (stat & 0x20) {
  58                        gus->gf1.uart_overrun++;
  59                }
  60        }
  61}
  62
  63static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)
  64{
  65        char byte;
  66        unsigned long flags;
  67
  68        /* try unlock output */
  69        if (snd_gf1_uart_stat(gus) & 0x01)
  70                snd_gf1_interrupt_midi_in(gus);
  71
  72        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
  73        if (snd_gf1_uart_stat(gus) & 0x02) {    /* Tx FIFO free? */
  74                if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) {  /* no other bytes or error */
  75                        snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */
  76                } else {
  77                        snd_gf1_uart_put(gus, byte);
  78                }
  79        }
  80        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
  81}
  82
  83static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
  84{
  85        snd_gf1_uart_cmd(gus, 0x03);    /* reset */
  86        if (!close && gus->uart_enable) {
  87                udelay(160);
  88                snd_gf1_uart_cmd(gus, 0x00);    /* normal operations */
  89        }
  90}
  91
  92static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream)
  93{
  94        unsigned long flags;
  95        struct snd_gus_card *gus;
  96
  97        gus = substream->rmidi->private_data;
  98        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
  99        if (!(gus->gf1.uart_cmd & 0x80)) {      /* input active? */
 100                snd_gf1_uart_reset(gus, 0);
 101        }
 102        gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
 103        gus->midi_substream_output = substream;
 104        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 105#if 0
 106        snd_printk(KERN_DEBUG "write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
 107#endif
 108        return 0;
 109}
 110
 111static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
 112{
 113        unsigned long flags;
 114        struct snd_gus_card *gus;
 115        int i;
 116
 117        gus = substream->rmidi->private_data;
 118        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
 119        if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
 120                snd_gf1_uart_reset(gus, 0);
 121        }
 122        gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
 123        gus->midi_substream_input = substream;
 124        if (gus->uart_enable) {
 125                for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
 126                        snd_gf1_uart_get(gus);  /* clean Rx */
 127                if (i >= 1000)
 128                        snd_printk(KERN_ERR "gus midi uart init read - cleanup error\n");
 129        }
 130        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 131#if 0
 132        snd_printk("read init - enable = %i, cmd = 0x%x, stat = 0x%x\n", gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
 133        snd_printk("[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n", gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100), inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
 134#endif
 135        return 0;
 136}
 137
 138static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream)
 139{
 140        unsigned long flags;
 141        struct snd_gus_card *gus;
 142
 143        gus = substream->rmidi->private_data;
 144        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
 145        if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)
 146                snd_gf1_uart_reset(gus, 1);
 147        snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
 148        gus->midi_substream_output = NULL;
 149        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 150        return 0;
 151}
 152
 153static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream)
 154{
 155        unsigned long flags;
 156        struct snd_gus_card *gus;
 157
 158        gus = substream->rmidi->private_data;
 159        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
 160        if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
 161                snd_gf1_uart_reset(gus, 1);
 162        snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
 163        gus->midi_substream_input = NULL;
 164        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 165        return 0;
 166}
 167
 168static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
 169{
 170        struct snd_gus_card *gus;
 171        unsigned long flags;
 172
 173        gus = substream->rmidi->private_data;
 174
 175        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
 176        if (up) {
 177                if ((gus->gf1.uart_cmd & 0x80) == 0)
 178                        snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
 179        } else {
 180                if (gus->gf1.uart_cmd & 0x80)
 181                        snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
 182        }
 183        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 184}
 185
 186static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
 187{
 188        unsigned long flags;
 189        struct snd_gus_card *gus;
 190        char byte;
 191        int timeout;
 192
 193        gus = substream->rmidi->private_data;
 194
 195        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
 196        if (up) {
 197                if ((gus->gf1.uart_cmd & 0x20) == 0) {
 198                        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 199                        /* wait for empty Rx - Tx is probably unlocked */
 200                        timeout = 10000;
 201                        while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01);
 202                        /* Tx FIFO free? */
 203                        spin_lock_irqsave(&gus->uart_cmd_lock, flags);
 204                        if (gus->gf1.uart_cmd & 0x20) {
 205                                spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 206                                return;
 207                        }
 208                        if (snd_gf1_uart_stat(gus) & 0x02) {
 209                                if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
 210                                        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 211                                        return;
 212                                }
 213                                snd_gf1_uart_put(gus, byte);
 214                        }
 215                        snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20);        /* enable Tx interrupt */
 216                }
 217        } else {
 218                if (gus->gf1.uart_cmd & 0x20)
 219                        snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20);
 220        }
 221        spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
 222}
 223
 224static struct snd_rawmidi_ops snd_gf1_uart_output =
 225{
 226        .open =         snd_gf1_uart_output_open,
 227        .close =        snd_gf1_uart_output_close,
 228        .trigger =      snd_gf1_uart_output_trigger,
 229};
 230
 231static struct snd_rawmidi_ops snd_gf1_uart_input =
 232{
 233        .open =         snd_gf1_uart_input_open,
 234        .close =        snd_gf1_uart_input_close,
 235        .trigger =      snd_gf1_uart_input_trigger,
 236};
 237
 238int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi ** rrawmidi)
 239{
 240        struct snd_rawmidi *rmidi;
 241        int err;
 242
 243        if (rrawmidi)
 244                *rrawmidi = NULL;
 245        if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0)
 246                return err;
 247        strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
 248        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
 249        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
 250        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
 251        rmidi->private_data = gus;
 252        gus->midi_uart = rmidi;
 253        if (rrawmidi)
 254                *rrawmidi = rmidi;
 255        return err;
 256}
 257
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.