]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
f30c2269 | 2 | * sound/oss/pas2_midi.c |
1da177e4 LT |
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 | } |