Merge with /pub/scm/linux/kernel/git/torvalds/linux-2.6.git
[linux-2.6] / sound / oss / gus_midi.c
1 /*
2  * sound/gus2_midi.c
3  *
4  * The low level driver for the GUS 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  * Changes:
14  * 11-10-2000   Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
15  *              Added __init to gus_midi_init()
16  */
17
18 #include <linux/init.h>
19 #include <linux/spinlock.h>
20 #include "sound_config.h"
21
22 #include "gus.h"
23 #include "gus_hw.h"
24
25 static int      midi_busy, input_opened;
26 static int      my_dev;
27 static int      output_used;
28 static volatile unsigned char gus_midi_control;
29 static void     (*midi_input_intr) (int dev, unsigned char data);
30
31 static unsigned char tmp_queue[256];
32 extern int      gus_pnp_flag;
33 static volatile int qlen;
34 static volatile unsigned char qhead, qtail;
35 extern int      gus_base, gus_irq, gus_dma;
36 extern int     *gus_osp;
37 extern spinlock_t gus_lock;
38
39 static int GUS_MIDI_STATUS(void)
40 {
41         return inb(u_MidiStatus);
42 }
43
44 static int gus_midi_open(int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev))
45 {
46         if (midi_busy)
47         {
48 /*              printk("GUS: Midi busy\n");*/
49                 return -EBUSY;
50         }
51         outb((MIDI_RESET), u_MidiControl);
52         gus_delay();
53
54         gus_midi_control = 0;
55         input_opened = 0;
56
57         if (mode == OPEN_READ || mode == OPEN_READWRITE)
58                 if (!gus_pnp_flag)
59                 {
60                         gus_midi_control |= MIDI_ENABLE_RCV;
61                         input_opened = 1;
62                 }
63         outb((gus_midi_control), u_MidiControl);        /* Enable */
64
65         midi_busy = 1;
66         qlen = qhead = qtail = output_used = 0;
67         midi_input_intr = input;
68
69         return 0;
70 }
71
72 static int dump_to_midi(unsigned char midi_byte)
73 {
74         unsigned long   flags;
75         int             ok = 0;
76
77         output_used = 1;
78
79         spin_lock_irqsave(&gus_lock, flags);
80
81         if (GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY)
82         {
83                 ok = 1;
84                 outb((midi_byte), u_MidiData);
85         }
86         else
87         {
88                 /*
89                  * Enable Midi xmit interrupts (again)
90                  */
91                 gus_midi_control |= MIDI_ENABLE_XMIT;
92                 outb((gus_midi_control), u_MidiControl);
93         }
94
95         spin_unlock_irqrestore(&gus_lock,flags);
96         return ok;
97 }
98
99 static void gus_midi_close(int dev)
100 {
101         /*
102          * Reset FIFO pointers, disable intrs
103          */
104
105         outb((MIDI_RESET), u_MidiControl);
106         midi_busy = 0;
107 }
108
109 static int gus_midi_out(int dev, unsigned char midi_byte)
110 {
111         unsigned long   flags;
112
113         /*
114          * Drain the local queue first
115          */
116         spin_lock_irqsave(&gus_lock, flags);
117
118         while (qlen && dump_to_midi(tmp_queue[qhead]))
119         {
120                 qlen--;
121                 qhead++;
122         }
123         spin_unlock_irqrestore(&gus_lock,flags);
124
125         /*
126          *      Output the byte if the local queue is empty.
127          */
128
129         if (!qlen)
130                 if (dump_to_midi(midi_byte))
131                         return 1;       /*
132                                          * OK
133                                          */
134
135         /*
136          *      Put to the local queue
137          */
138
139         if (qlen >= 256)
140                 return 0;       /*
141                                  * Local queue full
142                                  */
143         spin_lock_irqsave(&gus_lock, flags);
144
145         tmp_queue[qtail] = midi_byte;
146         qlen++;
147         qtail++;
148
149         spin_unlock_irqrestore(&gus_lock,flags);
150         return 1;
151 }
152
153 static int gus_midi_start_read(int dev)
154 {
155         return 0;
156 }
157
158 static int gus_midi_end_read(int dev)
159 {
160         return 0;
161 }
162
163 static void gus_midi_kick(int dev)
164 {
165 }
166
167 static int gus_midi_buffer_status(int dev)
168 {
169         unsigned long   flags;
170
171         if (!output_used)
172                 return 0;
173
174         spin_lock_irqsave(&gus_lock, flags);
175
176         if (qlen && dump_to_midi(tmp_queue[qhead]))
177         {
178                 qlen--;
179                 qhead++;
180         }
181         spin_unlock_irqrestore(&gus_lock,flags);
182         return (qlen > 0) || !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY);
183 }
184
185 #define MIDI_SYNTH_NAME "Gravis Ultrasound Midi"
186 #define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
187 #include "midi_synth.h"
188
189 static struct midi_operations gus_midi_operations =
190 {
191         .owner          = THIS_MODULE,
192         .info           = {"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS},
193         .converter      = &std_midi_synth,
194         .in_info        = {0},
195         .open           = gus_midi_open,
196         .close          = gus_midi_close,
197         .outputc        = gus_midi_out,
198         .start_read     = gus_midi_start_read,
199         .end_read       = gus_midi_end_read,
200         .kick           = gus_midi_kick,
201         .buffer_status  = gus_midi_buffer_status,
202 };
203
204 void __init gus_midi_init(struct address_info *hw_config)
205 {
206         int dev = sound_alloc_mididev();
207
208         if (dev == -1)
209         {
210                 printk(KERN_INFO "gus_midi: Too many midi devices detected\n");
211                 return;
212         }
213         outb((MIDI_RESET), u_MidiControl);
214
215         std_midi_synth.midi_dev = my_dev = dev;
216         hw_config->slots[2] = dev;
217         midi_devs[dev] = &gus_midi_operations;
218         sequencer_init();
219         return;
220 }
221
222 void gus_midi_interrupt(int dummy)
223 {
224         volatile unsigned char stat, data;
225         int timeout = 10;
226
227         spin_lock(&gus_lock);
228
229         while (timeout-- > 0 && (stat = GUS_MIDI_STATUS()) & (MIDI_RCV_FULL | MIDI_XMIT_EMPTY))
230         {
231                 if (stat & MIDI_RCV_FULL)
232                 {
233                         data = inb(u_MidiData);
234                         if (input_opened)
235                                 midi_input_intr(my_dev, data);
236                 }
237                 if (stat & MIDI_XMIT_EMPTY)
238                 {
239                         while (qlen && dump_to_midi(tmp_queue[qhead]))
240                         {
241                                 qlen--;
242                                 qhead++;
243                         }
244                         if (!qlen)
245                         {
246                               /*
247                                * Disable Midi output interrupts, since no data in the buffer
248                                */
249                               gus_midi_control &= ~MIDI_ENABLE_XMIT;
250                               outb((gus_midi_control), u_MidiControl);
251                               outb((gus_midi_control), u_MidiControl);
252                         }
253                 }
254         }
255         spin_unlock(&gus_lock);
256 }