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