Commit | Line | Data |
---|---|---|
1b261ae5 JH |
1 | /* |
2 | * linux/kernel/chr_drv/sound/mpu401.c | |
3 | * | |
4 | * The low level driver for Roland MPU-401 compatible Midi cards. | |
5 | * | |
6 | * This version supports just the DUMB UART mode. | |
7 | * | |
8 | * (C) 1993 Hannu Savolainen (hsavolai@cs.helsinki.fi) See COPYING for further | |
9 | * details. Should be distributed with this file. | |
10 | */ | |
11 | ||
12 | #include "sound_config.h" | |
13 | ||
14 | #ifdef CONFIGURE_SOUNDCARD | |
15 | ||
16 | #if !defined(EXCLUDE_MPU401) && !defined(EXCLUDE_MIDI) | |
17 | ||
18 | #define DATAPORT (mpu401_base)/* MPU-401 Data I/O Port on IBM */ | |
19 | #define COMDPORT (mpu401_base+1) /* MPU-401 Command Port on IBM */ | |
20 | #define STATPORT (mpu401_base+1) /* MPU-401 Status Port on IBM */ | |
21 | ||
22 | #define mpu401_status() INB(STATPORT) | |
23 | #define input_avail() (!(mpu401_status()&INPUT_AVAIL)) | |
24 | #define output_ready() (!(mpu401_status()&OUTPUT_READY)) | |
25 | #define mpu401_cmd(cmd) OUTB(cmd, COMDPORT) | |
26 | #define mpu401_read() INB(DATAPORT) | |
27 | #define mpu401_write(byte) OUTB(byte, DATAPORT) | |
28 | ||
29 | #define OUTPUT_READY 0x40 /* Mask for Data Read Redy Bit */ | |
30 | #define INPUT_AVAIL 0x80 /* Mask for Data Send Ready Bit */ | |
31 | #define MPU_ACK 0xFE /* MPU-401 Acknowledge Response */ | |
32 | #define MPU_RESET 0xFF /* MPU-401 Total Reset Command */ | |
33 | #define UART_MODE_ON 0x3F /* MPU-401 "Dumb UART Mode" */ | |
34 | ||
35 | static int mpu401_opened = 0; | |
36 | static int mpu401_base = 0x330; | |
37 | static int mpu401_irq; | |
38 | static int mpu401_detected = 0; | |
39 | static int my_dev; | |
40 | ||
41 | static int reset_mpu401 (void); | |
42 | ||
43 | static void | |
44 | mpu401_input_loop (void) | |
45 | { | |
46 | int count; | |
47 | ||
48 | count = 10; | |
49 | ||
50 | while (count) /* Not timed out */ | |
51 | if (input_avail ()) | |
52 | { | |
53 | unsigned char c = mpu401_read (); | |
54 | ||
55 | count = 100; | |
56 | ||
57 | if (mpu401_opened & OPEN_READ) | |
58 | sequencer_midi_input (my_dev, c); | |
59 | } | |
60 | else | |
61 | while (!input_avail () && count) | |
62 | count--; | |
63 | } | |
64 | ||
65 | void | |
66 | mpuintr (int unit) | |
67 | { | |
68 | unsigned char c; | |
69 | ||
70 | if (input_avail ()) | |
71 | mpu401_input_loop (); | |
72 | } | |
73 | ||
74 | /* | |
75 | * It looks like there is no input interrupts in the UART mode. Let's try | |
76 | * polling. | |
77 | */ | |
78 | ||
79 | static void | |
80 | poll_mpu401 (unsigned long dummy) | |
81 | { | |
82 | unsigned long flags; | |
83 | ||
84 | static struct timer_list mpu401_timer = | |
85 | {NULL, 0, 0, poll_mpu401}; | |
86 | ||
87 | if (!(mpu401_opened & OPEN_READ)) | |
88 | return; /* No longer required */ | |
89 | ||
90 | DISABLE_INTR (flags); | |
91 | ||
92 | if (input_avail ()) | |
93 | mpu401_input_loop (); | |
94 | ||
95 | mpu401_timer.expires = 1; | |
96 | add_timer (&mpu401_timer); /* Come back later */ | |
97 | ||
98 | RESTORE_INTR (flags); | |
99 | } | |
100 | ||
101 | static int | |
102 | set_mpu401_irq (int interrupt_level) | |
103 | { | |
104 | int retcode; | |
105 | ||
106 | #ifdef linux | |
107 | struct sigaction sa; | |
108 | ||
109 | sa.sa_handler = mpuintr; | |
110 | ||
111 | #ifdef SND_SA_INTERRUPT | |
112 | sa.sa_flags = SA_INTERRUPT; | |
113 | #else | |
114 | sa.sa_flags = 0; | |
115 | #endif | |
116 | ||
117 | sa.sa_mask = 0; | |
118 | sa.sa_restorer = NULL; | |
119 | ||
120 | retcode = irqaction (interrupt_level, &sa); | |
121 | ||
122 | if (retcode < 0) | |
123 | { | |
124 | printk ("MPU-401: IRQ%d already in use\n", interrupt_level); | |
125 | } | |
126 | ||
127 | #else | |
128 | /* # error Unimplemented for this OS */ | |
129 | #endif | |
130 | return retcode; | |
131 | } | |
132 | ||
133 | static int | |
134 | mpu401_open (int dev, int mode) | |
135 | { | |
136 | if (mpu401_opened) | |
137 | { | |
138 | printk ("MPU-401: Midi busy\n"); | |
139 | return RET_ERROR (EBUSY); | |
140 | } | |
141 | ||
142 | mpu401_input_loop (); | |
143 | ||
144 | mpu401_opened = mode; | |
145 | poll_mpu401 (0); /* Enable input polling */ | |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
150 | static void | |
151 | mpu401_close (int dev) | |
152 | { | |
153 | mpu401_opened = 0; | |
154 | } | |
155 | ||
156 | static int | |
157 | mpu401_out (int dev, unsigned char midi_byte) | |
158 | { | |
159 | int timeout; | |
160 | unsigned long flags; | |
161 | ||
162 | /* | |
163 | * Test for input since pending input seems to block the output. | |
164 | */ | |
165 | ||
166 | DISABLE_INTR (flags); | |
167 | ||
168 | if (input_avail ()) | |
169 | mpu401_input_loop (); | |
170 | ||
171 | RESTORE_INTR (flags); | |
172 | ||
173 | /* | |
174 | * Sometimes it takes about 13000 loops before the output becomes ready | |
175 | * (After reset). Normally it takes just about 10 loops. | |
176 | */ | |
177 | ||
178 | for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /* Wait */ | |
179 | ||
180 | if (!output_ready ()) | |
181 | { | |
182 | printk ("MPU-401: Timeout\n"); | |
183 | return 0; | |
184 | } | |
185 | ||
186 | mpu401_write (midi_byte); | |
187 | return 1; | |
188 | } | |
189 | ||
190 | static int | |
191 | mpu401_command (int dev, unsigned char midi_byte) | |
192 | { | |
193 | return 1; | |
194 | } | |
195 | ||
196 | static int | |
197 | mpu401_start_read (int dev) | |
198 | { | |
199 | return 0; | |
200 | } | |
201 | ||
202 | static int | |
203 | mpu401_end_read (int dev) | |
204 | { | |
205 | return 0; | |
206 | } | |
207 | ||
208 | static int | |
209 | mpu401_ioctl (int dev, unsigned cmd, unsigned arg) | |
210 | { | |
211 | return RET_ERROR (EINVAL); | |
212 | } | |
213 | ||
214 | static void | |
215 | mpu401_kick (int dev) | |
216 | { | |
217 | } | |
218 | ||
219 | static int | |
220 | mpu401_buffer_status (int dev) | |
221 | { | |
222 | return 0; /* No data in buffers */ | |
223 | } | |
224 | ||
225 | static struct midi_operations mpu401_operations = | |
226 | { | |
227 | {"MPU-401", 0}, | |
228 | mpu401_open, | |
229 | mpu401_close, | |
230 | mpu401_ioctl, | |
231 | mpu401_out, | |
232 | mpu401_start_read, | |
233 | mpu401_end_read, | |
234 | mpu401_kick, | |
235 | mpu401_command, | |
236 | mpu401_buffer_status | |
237 | }; | |
238 | ||
239 | ||
240 | long | |
241 | attach_mpu401 (long mem_start, struct address_info *hw_config) | |
242 | { | |
243 | int ok, timeout; | |
244 | unsigned long flags; | |
245 | ||
246 | mpu401_base = hw_config->io_base; | |
247 | mpu401_irq = hw_config->irq; | |
248 | ||
249 | if (!mpu401_detected) | |
250 | return RET_ERROR (EIO); | |
251 | ||
252 | DISABLE_INTR (flags); | |
253 | for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */ | |
254 | mpu401_cmd (UART_MODE_ON); | |
255 | ||
256 | ok = 0; | |
257 | for (timeout = 50000; timeout > 0 && !ok; timeout--) | |
258 | if (input_avail ()) | |
259 | if (mpu401_read () == MPU_ACK) | |
260 | ok = 1; | |
261 | ||
262 | RESTORE_INTR (flags); | |
263 | ||
264 | printk (" <Roland MPU-401>"); | |
265 | ||
266 | my_dev = num_midis; | |
267 | mpu401_dev = num_midis; | |
268 | midi_devs[num_midis++] = &mpu401_operations; | |
269 | return mem_start; | |
270 | } | |
271 | ||
272 | static int | |
273 | reset_mpu401 (void) | |
274 | { | |
275 | unsigned long flags; | |
276 | int ok, timeout, n; | |
277 | ||
278 | /* | |
279 | * Send the RESET command. Try twice if no success at the first time. | |
280 | */ | |
281 | ||
282 | ok = 0; | |
283 | ||
284 | DISABLE_INTR (flags); | |
285 | ||
286 | for (n = 0; n < 2 && !ok; n++) | |
287 | { | |
288 | for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */ | |
289 | mpu401_cmd (MPU_RESET); /* Send MPU-401 RESET Command */ | |
290 | ||
291 | /* | |
292 | * Wait at least 25 msec. This method is not accurate so let's make the | |
293 | * loop bit longer. Cannot sleep since this is called during boot. | |
294 | */ | |
295 | ||
296 | for (timeout = 50000; timeout > 0 && !ok; timeout--) | |
297 | if (input_avail ()) | |
298 | if (mpu401_read () == MPU_ACK) | |
299 | ok = 1; | |
300 | ||
301 | } | |
302 | ||
303 | mpu401_opened = 0; | |
304 | if (ok) | |
305 | mpu401_input_loop (); /* Flush input before enabling interrupts */ | |
306 | ||
307 | RESTORE_INTR (flags); | |
308 | ||
309 | return ok; | |
310 | } | |
311 | ||
312 | ||
313 | int | |
314 | probe_mpu401 (struct address_info *hw_config) | |
315 | { | |
316 | int ok = 0; | |
317 | ||
318 | mpu401_base = hw_config->io_base; | |
319 | mpu401_irq = hw_config->irq; | |
320 | ||
321 | if (set_mpu401_irq (mpu401_irq) < 0) | |
322 | return 0; | |
323 | ||
324 | ok = reset_mpu401 (); | |
325 | ||
326 | mpu401_detected = ok; | |
327 | return ok; | |
328 | } | |
329 | ||
330 | #endif | |
331 | ||
332 | #endif |