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 "sound_config.h"
20 
21 #include "gus.h"
22 #include "gus_hw.h"
23 
24 static int      midi_busy = 0, input_opened = 0;
25 static int      my_dev;
26 static int      output_used = 0;
27 static volatile unsigned char gus_midi_control;
28 
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 
GUS_MIDI_STATUS(void)38 static int GUS_MIDI_STATUS(void)
39 {
40 	return inb(u_MidiStatus);
41 }
42 
gus_midi_open(int dev,int mode,void (* input)(int dev,unsigned char data),void (* output)(int dev))43 static int gus_midi_open(int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev))
44 {
45 	if (midi_busy)
46 	{
47 /*		printk("GUS: Midi busy\n");*/
48 		return -EBUSY;
49 	}
50 	outb((MIDI_RESET), u_MidiControl);
51 	gus_delay();
52 
53 	gus_midi_control = 0;
54 	input_opened = 0;
55 
56 	if (mode == OPEN_READ || mode == OPEN_READWRITE)
57 		if (!gus_pnp_flag)
58 		{
59 			gus_midi_control |= MIDI_ENABLE_RCV;
60 			input_opened = 1;
61 		}
62 	outb((gus_midi_control), u_MidiControl);	/* Enable */
63 
64 	midi_busy = 1;
65 	qlen = qhead = qtail = output_used = 0;
66 	midi_input_intr = input;
67 
68 	return 0;
69 }
70 
dump_to_midi(unsigned char midi_byte)71 static int dump_to_midi(unsigned char midi_byte)
72 {
73 	unsigned long   flags;
74 	int             ok = 0;
75 
76 	output_used = 1;
77 
78 	save_flags(flags);
79 	cli();
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 	restore_flags(flags);
96 	return ok;
97 }
98 
gus_midi_close(int dev)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 
gus_midi_out(int dev,unsigned char midi_byte)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 
117 	save_flags(flags);
118 	cli();
119 
120 	while (qlen && dump_to_midi(tmp_queue[qhead]))
121 	{
122 		qlen--;
123 		qhead++;
124 	}
125 	restore_flags(flags);
126 
127 	/*
128 	 *	Output the byte if the local queue is empty.
129 	 */
130 
131 	if (!qlen)
132 		if (dump_to_midi(midi_byte))
133 			return 1;	/*
134 					 * OK
135 					 */
136 
137 	/*
138 	 *	Put to the local queue
139 	 */
140 
141 	if (qlen >= 256)
142 		return 0;	/*
143 				 * Local queue full
144 				 */
145 	save_flags(flags);
146 	cli();
147 
148 	tmp_queue[qtail] = midi_byte;
149 	qlen++;
150 	qtail++;
151 
152 	restore_flags(flags);
153 	return 1;
154 }
155 
gus_midi_start_read(int dev)156 static int gus_midi_start_read(int dev)
157 {
158 	return 0;
159 }
160 
gus_midi_end_read(int dev)161 static int gus_midi_end_read(int dev)
162 {
163 	return 0;
164 }
165 
gus_midi_kick(int dev)166 static void gus_midi_kick(int dev)
167 {
168 }
169 
gus_midi_buffer_status(int dev)170 static int gus_midi_buffer_status(int dev)
171 {
172 	unsigned long   flags;
173 
174 	if (!output_used)
175 		return 0;
176 
177 	save_flags(flags);
178 	cli();
179 
180 	if (qlen && dump_to_midi(tmp_queue[qhead]))
181 	{
182 		qlen--;
183 		qhead++;
184 	}
185 	restore_flags(flags);
186 	return (qlen > 0) || !(GUS_MIDI_STATUS() & MIDI_XMIT_EMPTY);
187 }
188 
189 #define MIDI_SYNTH_NAME	"Gravis Ultrasound Midi"
190 #define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
191 #include "midi_synth.h"
192 
193 static struct midi_operations gus_midi_operations =
194 {
195 	owner:		THIS_MODULE,
196 	info:		{"Gravis UltraSound Midi", 0, 0, SNDCARD_GUS},
197 	converter:	&std_midi_synth,
198 	in_info:	{0},
199 	open:		gus_midi_open,
200 	close:		gus_midi_close,
201 	outputc:	gus_midi_out,
202 	start_read:	gus_midi_start_read,
203 	end_read:	gus_midi_end_read,
204 	kick:		gus_midi_kick,
205 	buffer_status:	gus_midi_buffer_status,
206 };
207 
gus_midi_init(struct address_info * hw_config)208 void __init gus_midi_init(struct address_info *hw_config)
209 {
210 	int dev = sound_alloc_mididev();
211 
212 	if (dev == -1)
213 	{
214 		printk(KERN_INFO "gus_midi: Too many midi devices detected\n");
215 		return;
216 	}
217 	outb((MIDI_RESET), u_MidiControl);
218 
219 	std_midi_synth.midi_dev = my_dev = dev;
220 	hw_config->slots[2] = dev;
221 	midi_devs[dev] = &gus_midi_operations;
222 	sequencer_init();
223 	return;
224 }
225 
gus_midi_interrupt(int dummy)226 void gus_midi_interrupt(int dummy)
227 {
228 	volatile unsigned char stat, data;
229 	unsigned long flags;
230 	int timeout = 10;
231 
232 	save_flags(flags);
233 	cli();
234 
235 	while (timeout-- > 0 && (stat = GUS_MIDI_STATUS()) & (MIDI_RCV_FULL | MIDI_XMIT_EMPTY))
236 	{
237 		if (stat & MIDI_RCV_FULL)
238 		{
239 			data = inb(u_MidiData);
240 			if (input_opened)
241 				midi_input_intr(my_dev, data);
242 		}
243 		if (stat & MIDI_XMIT_EMPTY)
244 		{
245 			while (qlen && dump_to_midi(tmp_queue[qhead]))
246 			{
247 				qlen--;
248 				qhead++;
249 			}
250 			if (!qlen)
251 			{
252 			      /*
253 			       * Disable Midi output interrupts, since no data in the buffer
254 			       */
255 			      gus_midi_control &= ~MIDI_ENABLE_XMIT;
256 			      outb((gus_midi_control), u_MidiControl);
257 			      outb((gus_midi_control), u_MidiControl);
258 			}
259 		}
260 	}
261 	restore_flags(flags);
262 }
263