Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / dev / sound / isa / i386 / sbmidi / sb16_midi.c
1 /*
2  * sound/sb16_midi.c
3  * 
4  * The low level driver for the MPU-401 UART emulation of the SB16.
5  * 
6  * Copyright by Hannu Savolainen 1993
7  * 
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met: 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer. 2.
12  * Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  * 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  * 
28  * $FreeBSD: src/sys/i386/isa/sound/sb16_midi.c,v 1.20 1999/12/27 04:37:18 tanimura Exp $
29  *
30  */
31
32 #include <i386/isa/sound/sound_config.h>
33 #include <i386/isa/sound/sbcard.h>
34
35 #if defined(CONFIG_SB) && defined(CONFIG_SB16) && defined(CONFIG_MIDI)
36
37 #include "sb.h"
38
39 #ifdef PC98
40 #define DATAPORT   (sb16midi_base)
41 #define COMDPORT   (sb16midi_base+0x100)
42 #define STATPORT   (sb16midi_base+0x100)
43 #else
44 #define DATAPORT   (sb16midi_base)
45 #define COMDPORT   (sb16midi_base+1)
46 #define STATPORT   (sb16midi_base+1)
47 #endif
48
49 extern sound_os_info *sb_osp;
50
51 #define sb16midi_status()               inb( STATPORT)
52 #define input_avail()           (!(sb16midi_status()&INPUT_AVAIL))
53 #define output_ready()          (!(sb16midi_status()&OUTPUT_READY))
54 #define sb16midi_cmd(cmd)               outb( COMDPORT,  cmd)
55 #define sb16midi_read()         inb( DATAPORT)
56 #define sb16midi_write(byte)    outb( DATAPORT,  byte)
57
58 #define OUTPUT_READY    0x40
59 #define INPUT_AVAIL     0x80
60 #define MPU_ACK         0xFE
61 #define MPU_RESET       0xFF
62 #define UART_MODE_ON    0x3F
63
64 static int      sb16midi_opened = 0;
65 #ifdef PC98
66 static int      sb16midi_base = 0x80d2;
67 #else
68 static int      sb16midi_base = 0x330;
69 #endif
70 static int      sb16midi_detected = 0;
71 static int      my_dev;
72 extern int      sbc_base;
73
74 static int      reset_sb16midi(void);
75 static void     (*midi_input_intr) (int dev, u_char data);
76 static volatile u_char input_byte;
77
78 static void
79 sb16midi_input_loop(void)
80 {
81         while (input_avail()) {
82                 u_char   c = sb16midi_read();
83
84                 if (c == MPU_ACK)
85                         input_byte = c;
86                 else if (sb16midi_opened & OPEN_READ && midi_input_intr)
87                         midi_input_intr(my_dev, c);
88         }
89 }
90
91 void
92 sb16midiintr(int unit)
93 {
94         if (input_avail())
95                 sb16midi_input_loop();
96 }
97
98 static int
99 sb16midi_open(int dev, int mode,
100               void (*input) (int dev, u_char data),
101               void (*output) (int dev)
102 )
103 {
104         if (sb16midi_opened) {
105                 return -(EBUSY);
106         }
107         sb16midi_input_loop();
108
109         midi_input_intr = input;
110         sb16midi_opened = mode;
111
112         return 0;
113 }
114
115 static void
116 sb16midi_close(int dev)
117 {
118         sb16midi_opened = 0;
119 }
120
121 static int
122 sb16midi_out(int dev, u_char midi_byte)
123 {
124         int             timeout;
125         u_long   flags;
126
127         /*
128          * Test for input since pending input seems to block the output.
129          */
130
131         flags = splhigh();
132
133         if (input_avail())
134                 sb16midi_input_loop();
135
136         splx(flags);
137
138         /*
139          * Sometimes it takes about 13000 loops before the output becomes
140          * ready (After reset). Normally it takes just about 10 loops.
141          */
142
143         for (timeout = 30000; timeout > 0 && !output_ready(); timeout--);       /* Wait */
144
145         if (!output_ready()) {
146                 printf("MPU-401: Timeout\n");
147                 return 0;
148         }
149         sb16midi_write(midi_byte);
150         return 1;
151 }
152
153 static int
154 sb16midi_start_read(int dev)
155 {
156         return 0;
157 }
158
159 static int
160 sb16midi_end_read(int dev)
161 {
162         return 0;
163 }
164
165 static int
166 sb16midi_ioctl(int dev, u_int cmd, ioctl_arg arg)
167 {
168         return -(EINVAL);
169 }
170
171 static void
172 sb16midi_kick(int dev)
173 {
174 }
175
176 static int
177 sb16midi_buffer_status(int dev)
178 {
179         return 0;               /* No data in buffers */
180 }
181
182 #define MIDI_SYNTH_NAME "SoundBlaster 16 Midi"
183 #define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
184 #include <i386/isa/sound/midi_synth.h>
185
186 static struct midi_operations sb16midi_operations =
187 {
188         {"SoundBlaster 16 Midi", 0, 0, SNDCARD_SB16MIDI},
189         &std_midi_synth,
190         {0},
191         sb16midi_open,
192         sb16midi_close,
193         sb16midi_ioctl,
194         sb16midi_out,
195         sb16midi_start_read,
196         sb16midi_end_read,
197         sb16midi_kick,
198         NULL,
199         sb16midi_buffer_status,
200         NULL
201 };
202
203
204 void
205 attach_sb16midi(struct address_info * hw_config)
206 {
207         int             ok, timeout;
208         u_long   flags;
209
210         sb16midi_base = hw_config->io_base;
211
212         if (!sb16midi_detected)
213                 return;
214
215         flags = splhigh();
216         for (timeout = 30000; timeout < 0 && !output_ready(); timeout--);       /* Wait */
217         input_byte = 0;
218         sb16midi_cmd(UART_MODE_ON);
219
220         ok = 0;
221         for (timeout = 50000; timeout > 0 && !ok; timeout--)
222                 if (input_byte == MPU_ACK)
223                         ok = 1;
224                 else if (input_avail())
225                         if (sb16midi_read() == MPU_ACK)
226                                 ok = 1;
227
228         splx(flags);
229
230         if (num_midis >= MAX_MIDI_DEV) {
231                 printf("Sound: Too many midi devices detected\n");
232                 return;
233         }
234         
235         conf_printf("SoundBlaster MPU-401", hw_config);
236         std_midi_synth.midi_dev = my_dev = num_midis;
237         midi_devs[num_midis++] = &sb16midi_operations;
238         return;
239 }
240
241 static int
242 reset_sb16midi(void)
243 {
244         int             ok, timeout, n;
245
246         /*
247          * Send the RESET command. Try again if no success at the first time.
248          */
249
250         if (inb(STATPORT) == 0xff)
251                 return 0;
252
253         ok = 0;
254
255         /* flags = splhigh(); */
256
257         for (n = 0; n < 2 && !ok; n++) {
258                 for (timeout = 30000; timeout < 0 && !output_ready(); timeout--);       /* Wait */
259                 input_byte = 0;
260                 sb16midi_cmd(MPU_RESET);        /* Send MPU-401 RESET Command */
261
262                 /*
263                  * Wait at least 25 msec. This method is not accurate so
264                  * let's make the loop bit longer. Cannot sleep since this is
265                  * called during boot.
266                  */
267
268                 for (timeout = 50000; timeout > 0 && !ok; timeout--)
269                         if (input_byte == MPU_ACK)      /* Interrupt */
270                                 ok = 1;
271                         else if (input_avail())
272                                 if (sb16midi_read() == MPU_ACK)
273                                         ok = 1;
274
275         }
276
277         sb16midi_opened = 0;
278         if (ok)
279                 sb16midi_input_loop();  /* Flush input before enabling
280                                          * interrupts */
281
282         /* splx(flags); */
283
284         return ok;
285 }
286
287
288 int
289 probe_sb16midi(struct address_info * hw_config)
290 {
291         int             ok = 0;
292         struct address_info *sb_config;
293
294         if (sbc_major < 4)
295                 return 0;       /* Not a SB16 */
296
297         if (!(sb_config = sound_getconf(SNDCARD_SB))) {
298           printf("SB16 Error: Plain SB not configured\n");
299           return 0;
300         }
301
302         sb16midi_base = hw_config->io_base;
303
304         ok = reset_sb16midi();
305
306         sb16midi_detected = ok;
307         return ok;
308 }
309
310 #endif
311