linux/sound/oss/pas2_midi.c
<<
>>
Prefs
   1/*
   2 * sound/oss/pas2_midi.c
   3 *
   4 * The low level driver for the PAS Midi Interface.
   5 */
   6/*
   7 * Copyright (C) by Hannu Savolainen 1993-1997
   8 *
   9 * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  10 * Version 2 (June 1991). See the "COPYING" file distributed with this software
  11 * for more info.
  12 *
  13 * Bartlomiej Zolnierkiewicz    : Added __init to pas_init_mixer()
  14 */
  15
  16#include <linux/init.h>
  17#include <linux/spinlock.h>
  18#include "sound_config.h"
  19
  20#include "pas2.h"
  21
  22extern spinlock_t pas_lock;
  23
  24static int      midi_busy, input_opened;
  25static int      my_dev;
  26
  27int pas2_mididev=-1;
  28
  29static unsigned char tmp_queue[256];
  30static volatile int qlen;
  31static volatile unsigned char qhead, qtail;
  32
  33static void     (*midi_input_intr) (int dev, unsigned char data);
  34
  35static int pas_midi_open(int dev, int mode,
  36              void            (*input) (int dev, unsigned char data),
  37              void            (*output) (int dev)
  38)
  39{
  40        int             err;
  41        unsigned long   flags;
  42        unsigned char   ctrl;
  43
  44
  45        if (midi_busy)
  46                return -EBUSY;
  47
  48        /*
  49         * Reset input and output FIFO pointers
  50         */
  51        pas_write(0x20 | 0x40,
  52                  0x178b);
  53
  54        spin_lock_irqsave(&pas_lock, flags);
  55
  56        if ((err = pas_set_intr(0x10)) < 0)
  57        {
  58                spin_unlock_irqrestore(&pas_lock, flags);
  59                return err;
  60        }
  61        /*
  62         * Enable input available and output FIFO empty interrupts
  63         */
  64
  65        ctrl = 0;
  66        input_opened = 0;
  67        midi_input_intr = input;
  68
  69        if (mode == OPEN_READ || mode == OPEN_READWRITE)
  70        {
  71                ctrl |= 0x04;   /* Enable input */
  72                input_opened = 1;
  73        }
  74        if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
  75        {
  76                ctrl |= 0x08 | 0x10;    /* Enable output */
  77        }
  78        pas_write(ctrl, 0x178b);
  79
  80        /*
  81         * Acknowledge any pending interrupts
  82         */
  83
  84        pas_write(0xff, 0x1B88);
  85
  86        spin_unlock_irqrestore(&pas_lock, flags);
  87
  88        midi_busy = 1;
  89        qlen = qhead = qtail = 0;
  90        return 0;
  91}
  92
  93static void pas_midi_close(int dev)
  94{
  95
  96        /*
  97         * Reset FIFO pointers, disable intrs
  98         */
  99        pas_write(0x20 | 0x40, 0x178b);
 100
 101        pas_remove_intr(0x10);
 102        midi_busy = 0;
 103}
 104
 105static int dump_to_midi(unsigned char midi_byte)
 106{
 107        int fifo_space, x;
 108
 109        fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f;
 110
 111        /*
 112         * The MIDI FIFO space register and it's documentation is nonunderstandable.
 113         * There seem to be no way to differentiate between buffer full and buffer
 114         * empty situations. For this reason we don't never write the buffer
 115         * completely full. In this way we can assume that 0 (or is it 15)
 116         * means that the buffer is empty.
 117         */
 118
 119        if (fifo_space < 2 && fifo_space != 0)  /* Full (almost) */
 120                return 0;       /* Ask upper layers to retry after some time */
 121
 122        pas_write(midi_byte, 0x178A);
 123
 124        return 1;
 125}
 126
 127static int pas_midi_out(int dev, unsigned char midi_byte)
 128{
 129
 130        unsigned long flags;
 131
 132        /*
 133         * Drain the local queue first
 134         */
 135
 136        spin_lock_irqsave(&pas_lock, flags);
 137
 138        while (qlen && dump_to_midi(tmp_queue[qhead]))
 139        {
 140                qlen--;
 141                qhead++;
 142        }
 143
 144        spin_unlock_irqrestore(&pas_lock, flags);
 145
 146        /*
 147         *      Output the byte if the local queue is empty.
 148         */
 149
 150        if (!qlen)
 151                if (dump_to_midi(midi_byte))
 152                        return 1;
 153
 154        /*
 155         *      Put to the local queue
 156         */
 157
 158        if (qlen >= 256)
 159                return 0;       /* Local queue full */
 160
 161        spin_lock_irqsave(&pas_lock, flags);
 162
 163        tmp_queue[qtail] = midi_byte;
 164        qlen++;
 165        qtail++;
 166
 167        spin_unlock_irqrestore(&pas_lock, flags);
 168
 169        return 1;
 170}
 171
 172static int pas_midi_start_read(int dev)
 173{
 174        return 0;
 175}
 176
 177static int pas_midi_end_read(int dev)
 178{
 179        return 0;
 180}
 181
 182static void pas_midi_kick(int dev)
 183{
 184}
 185
 186static int pas_buffer_status(int dev)
 187{
 188        return qlen;
 189}
 190
 191#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi"
 192#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
 193#include "midi_synth.h"
 194
 195static struct midi_operations pas_midi_operations =
 196{
 197        .owner          = THIS_MODULE,
 198        .info           = {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS},
 199        .converter      = &std_midi_synth,
 200        .in_info        = {0},
 201        .open           = pas_midi_open,
 202        .close          = pas_midi_close,
 203        .outputc        = pas_midi_out,
 204        .start_read     = pas_midi_start_read,
 205        .end_read       = pas_midi_end_read,
 206        .kick           = pas_midi_kick,
 207        .buffer_status  = pas_buffer_status,
 208};
 209
 210void __init pas_midi_init(void)
 211{
 212        int dev = sound_alloc_mididev();
 213
 214        if (dev == -1)
 215        {
 216                printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n");
 217                return;
 218        }
 219        std_midi_synth.midi_dev = my_dev = dev;
 220        midi_devs[dev] = &pas_midi_operations;
 221        pas2_mididev = dev;
 222        sequencer_init();
 223}
 224
 225void pas_midi_interrupt(void)
 226{
 227        unsigned char   stat;
 228        int             i, incount;
 229
 230        stat = pas_read(0x1B88);
 231
 232        if (stat & 0x04)        /* Input data available */
 233        {
 234                incount = pas_read(0x1B89) & 0x0f;      /* Input FIFO size */
 235                if (!incount)
 236                        incount = 16;
 237
 238                for (i = 0; i < incount; i++)
 239                        if (input_opened)
 240                        {
 241                                midi_input_intr(my_dev, pas_read(0x178A));
 242                        } else
 243                                pas_read(0x178A);       /* Flush */
 244        }
 245        if (stat & (0x08 | 0x10))
 246        {
 247                spin_lock(&pas_lock);/* called in irq context */
 248
 249                while (qlen && dump_to_midi(tmp_queue[qhead]))
 250                {
 251                        qlen--;
 252                        qhead++;
 253                }
 254
 255                spin_unlock(&pas_lock);
 256        }
 257        if (stat & 0x40)
 258        {
 259                printk(KERN_WARNING "MIDI output overrun %x,%x\n", pas_read(0x1B89), stat);
 260        }
 261        pas_write(stat, 0x1B88);        /* Acknowledge interrupts */
 262}
 263