File: /usr/src/linux/drivers/sound/pas2_midi.c

1     /*
2      * sound/pas2_midi.c
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 "sound_config.h"
18     
19     #include "pas2.h"
20     
21     static int      midi_busy = 0, input_opened = 0;
22     static int      my_dev;
23     
24     int pas2_mididev=-1;
25     
26     static unsigned char tmp_queue[256];
27     static volatile int qlen;
28     static volatile unsigned char qhead, qtail;
29     
30     static void     (*midi_input_intr) (int dev, unsigned char data);
31     
32     static int pas_midi_open(int dev, int mode,
33     	      void            (*input) (int dev, unsigned char data),
34     	      void            (*output) (int dev)
35     )
36     {
37     	int             err;
38     	unsigned long   flags;
39     	unsigned char   ctrl;
40     
41     
42     	if (midi_busy)
43     		return -EBUSY;
44     
45     	/*
46     	 * Reset input and output FIFO pointers
47     	 */
48     	pas_write(0x20 | 0x40,
49     		  0x178b);
50     
51     	save_flags(flags);
52     	cli();
53     
54     	if ((err = pas_set_intr(0x10)) < 0)
55     	{
56     		restore_flags(flags);
57     		return err;
58     	}
59     	/*
60     	 * Enable input available and output FIFO empty interrupts
61     	 */
62     
63     	ctrl = 0;
64     	input_opened = 0;
65     	midi_input_intr = input;
66     
67     	if (mode == OPEN_READ || mode == OPEN_READWRITE)
68     	{
69     		ctrl |= 0x04;	/* Enable input */
70     		input_opened = 1;
71     	}
72     	if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
73     	{
74     		ctrl |= 0x08 | 0x10;	/* Enable output */
75     	}
76     	pas_write(ctrl, 0x178b);
77     
78     	/*
79     	 * Acknowledge any pending interrupts
80     	 */
81     
82     	pas_write(0xff, 0x1B88);
83     
84     	restore_flags(flags);
85     
86     	midi_busy = 1;
87     	qlen = qhead = qtail = 0;
88     	return 0;
89     }
90     
91     static void pas_midi_close(int dev)
92     {
93     
94     	/*
95     	 * Reset FIFO pointers, disable intrs
96     	 */
97     	pas_write(0x20 | 0x40, 0x178b);
98     
99     	pas_remove_intr(0x10);
100     	midi_busy = 0;
101     }
102     
103     static int dump_to_midi(unsigned char midi_byte)
104     {
105     	int fifo_space, x;
106     
107     	fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f;
108     
109     	/*
110     	 * The MIDI FIFO space register and it's documentation is nonunderstandable.
111     	 * There seem to be no way to differentiate between buffer full and buffer
112     	 * empty situations. For this reason we don't never write the buffer
113     	 * completely full. In this way we can assume that 0 (or is it 15)
114     	 * means that the buffer is empty.
115     	 */
116     
117     	if (fifo_space < 2 && fifo_space != 0)	/* Full (almost) */
118     		return 0;	/* Ask upper layers to retry after some time */
119     
120     	pas_write(midi_byte, 0x178A);
121     
122     	return 1;
123     }
124     
125     static int pas_midi_out(int dev, unsigned char midi_byte)
126     {
127     
128     	unsigned long flags;
129     
130     	/*
131     	 * Drain the local queue first
132     	 */
133     
134     	save_flags(flags);
135     	cli();
136     
137     	while (qlen && dump_to_midi(tmp_queue[qhead]))
138     	{
139     		qlen--;
140     		qhead++;
141     	}
142     
143     	restore_flags(flags);
144     
145     	/*
146     	 *	Output the byte if the local queue is empty.
147     	 */
148     
149     	if (!qlen)
150     		if (dump_to_midi(midi_byte))
151     			return 1;
152     
153     	/*
154     	 *	Put to the local queue
155     	 */
156     
157     	if (qlen >= 256)
158     		return 0;	/* Local queue full */
159     
160     	save_flags(flags);
161     	cli();
162     
163     	tmp_queue[qtail] = midi_byte;
164     	qlen++;
165     	qtail++;
166     
167     	restore_flags(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     	unsigned long   flags;
230     
231     	stat = pas_read(0x1B88);
232     
233     	if (stat & 0x04)	/* Input data available */
234     	{
235     		incount = pas_read(0x1B89) & 0x0f;	/* Input FIFO size */
236     		if (!incount)
237     			incount = 16;
238     
239     		for (i = 0; i < incount; i++)
240     			if (input_opened)
241     			{
242     				midi_input_intr(my_dev, pas_read(0x178A));
243     			} else
244     				pas_read(0x178A);	/* Flush */
245     	}
246     	if (stat & (0x08 | 0x10))
247     	{
248     		save_flags(flags);
249     		cli();
250     
251     		while (qlen && dump_to_midi(tmp_queue[qhead]))
252     		{
253     			qlen--;
254     			qhead++;
255     		}
256     
257     		restore_flags(flags);
258     	}
259     	if (stat & 0x40)
260     	{
261     		printk(KERN_WARNING "MIDI output overrun %x,%x\n", pas_read(0x1B89), stat);
262     	}
263     	pas_write(stat, 0x1B88);	/* Acknowledge interrupts */
264     }
265