diff options
Diffstat (limited to 'sound/oss/pas2_midi.c')
-rw-r--r-- | sound/oss/pas2_midi.c | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/sound/oss/pas2_midi.c b/sound/oss/pas2_midi.c new file mode 100644 index 000000000000..79d6a5827b6d --- /dev/null +++ b/sound/oss/pas2_midi.c | |||
@@ -0,0 +1,262 @@ | |||
1 | /* | ||
2 | * sound/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 | |||
22 | extern spinlock_t pas_lock; | ||
23 | |||
24 | static int midi_busy, input_opened; | ||
25 | static int my_dev; | ||
26 | |||
27 | int pas2_mididev=-1; | ||
28 | |||
29 | static unsigned char tmp_queue[256]; | ||
30 | static volatile int qlen; | ||
31 | static volatile unsigned char qhead, qtail; | ||
32 | |||
33 | static void (*midi_input_intr) (int dev, unsigned char data); | ||
34 | |||
35 | static 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 | |||
93 | static 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 | |||
105 | static 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 | |||
127 | static 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 | |||
172 | static int pas_midi_start_read(int dev) | ||
173 | { | ||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static int pas_midi_end_read(int dev) | ||
178 | { | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static void pas_midi_kick(int dev) | ||
183 | { | ||
184 | } | ||
185 | |||
186 | static 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 | |||
195 | static 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 | |||
210 | void __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 | |||
225 | void 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 | } | ||