1/*
2 * sound/oss/v_midi.c
3 *
4 * The low level driver for the Sound Blaster DS chips.
5 *
6 *
7 * Copyright (C) by Hannu Savolainen 1993-1996
8 *
9 * USS/Lite 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 *
14 * Changes
15 *	Alan Cox		Modularisation, changed memory allocations
16 *	Christoph Hellwig	Adapted to module_init/module_exit
17 *
18 * Status
19 *	Untested
20 */
21
22#include <linux/init.h>
23#include <linux/module.h>
24#include <linux/slab.h>
25#include <linux/spinlock.h>
26#include "sound_config.h"
27
28#include "v_midi.h"
29
30static vmidi_devc *v_devc[2] = { NULL, NULL};
31static int midi1,midi2;
32static void *midi_mem = NULL;
33
34/*
35 * The DSP channel can be used either for input or output. Variable
36 * 'sb_irq_mode' will be set when the program calls read or write first time
37 * after open. Current version doesn't support mode changes without closing
38 * and reopening the device. Support for this feature may be implemented in a
39 * future version of this driver.
40 */
41
42
43static int v_midi_open (int dev, int mode,
44	      void            (*input) (int dev, unsigned char data),
45	      void            (*output) (int dev)
46)
47{
48	vmidi_devc *devc = midi_devs[dev]->devc;
49	unsigned long flags;
50
51	if (devc == NULL)
52		return -(ENXIO);
53
54	spin_lock_irqsave(&devc->lock,flags);
55	if (devc->opened)
56	{
57		spin_unlock_irqrestore(&devc->lock,flags);
58		return -(EBUSY);
59	}
60	devc->opened = 1;
61	spin_unlock_irqrestore(&devc->lock,flags);
62
63	devc->intr_active = 1;
64
65	if (mode & OPEN_READ)
66	{
67		devc->input_opened = 1;
68		devc->midi_input_intr = input;
69	}
70
71	return 0;
72}
73
74static void v_midi_close (int dev)
75{
76	vmidi_devc *devc = midi_devs[dev]->devc;
77	unsigned long flags;
78
79	if (devc == NULL)
80		return;
81
82	spin_lock_irqsave(&devc->lock,flags);
83	devc->intr_active = 0;
84	devc->input_opened = 0;
85	devc->opened = 0;
86	spin_unlock_irqrestore(&devc->lock,flags);
87}
88
89static int v_midi_out (int dev, unsigned char midi_byte)
90{
91	vmidi_devc *devc = midi_devs[dev]->devc;
92	vmidi_devc *pdevc;
93
94	if (devc == NULL)
95		return -ENXIO;
96
97	pdevc = midi_devs[devc->pair_mididev]->devc;
98	if (pdevc->input_opened > 0){
99		if (MIDIbuf_avail(pdevc->my_mididev) > 500)
100			return 0;
101		pdevc->midi_input_intr (pdevc->my_mididev, midi_byte);
102	}
103	return 1;
104}
105
106static inline int v_midi_start_read (int dev)
107{
108	return 0;
109}
110
111static int v_midi_end_read (int dev)
112{
113	vmidi_devc *devc = midi_devs[dev]->devc;
114	if (devc == NULL)
115		return -ENXIO;
116
117	devc->intr_active = 0;
118	return 0;
119}
120
121/* why -EPERM and not -EINVAL?? */
122
123static inline int v_midi_ioctl (int dev, unsigned cmd, void __user *arg)
124{
125	return -EPERM;
126}
127
128
129#define MIDI_SYNTH_NAME	"Loopback MIDI"
130#define MIDI_SYNTH_CAPS	SYNTH_CAP_INPUT
131
132#include "midi_synth.h"
133
134static struct midi_operations v_midi_operations =
135{
136	.owner		= THIS_MODULE,
137	.info		= {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI},
138	.converter	= &std_midi_synth,
139	.in_info	= {0},
140	.open		= v_midi_open,
141	.close		= v_midi_close,
142	.ioctl		= v_midi_ioctl,
143	.outputc	= v_midi_out,
144	.start_read	= v_midi_start_read,
145	.end_read	= v_midi_end_read,
146};
147
148static struct midi_operations v_midi_operations2 =
149{
150	.owner		= THIS_MODULE,
151	.info		= {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI},
152	.converter	= &std_midi_synth,
153	.in_info	= {0},
154	.open		= v_midi_open,
155	.close		= v_midi_close,
156	.ioctl		= v_midi_ioctl,
157	.outputc	= v_midi_out,
158	.start_read	= v_midi_start_read,
159	.end_read	= v_midi_end_read,
160};
161
162/*
163 *	We kmalloc just one of these - it makes life simpler and the code
164 *	cleaner and the memory handling far more efficient
165 */
166
167struct vmidi_memory
168{
169	/* Must be first */
170	struct midi_operations m_ops[2];
171	struct synth_operations s_ops[2];
172	struct vmidi_devc v_ops[2];
173};
174
175static void __init attach_v_midi (struct address_info *hw_config)
176{
177	struct vmidi_memory *m;
178	/* printk("Attaching v_midi device.....\n"); */
179
180	midi1 = sound_alloc_mididev();
181	if (midi1 == -1)
182	{
183		printk(KERN_ERR "v_midi: Too many midi devices detected\n");
184		return;
185	}
186
187	m = kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL);
188	if (m == NULL)
189	{
190		printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n");
191		sound_unload_mididev(midi1);
192		return;
193	}
194
195	midi_mem = m;
196
197	midi_devs[midi1] = &m->m_ops[0];
198
199
200	midi2 = sound_alloc_mididev();
201	if (midi2 == -1)
202	{
203		printk (KERN_ERR "v_midi: Too many midi devices detected\n");
204		kfree(m);
205		sound_unload_mididev(midi1);
206		return;
207	}
208
209	midi_devs[midi2] = &m->m_ops[1];
210
211	/* printk("VMIDI1: %d   VMIDI2: %d\n",midi1,midi2); */
212
213	/* for MIDI-1 */
214	v_devc[0] = &m->v_ops[0];
215	memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations,
216		sizeof (struct midi_operations));
217
218	v_devc[0]->my_mididev = midi1;
219	v_devc[0]->pair_mididev = midi2;
220	v_devc[0]->opened = v_devc[0]->input_opened = 0;
221	v_devc[0]->intr_active = 0;
222	v_devc[0]->midi_input_intr = NULL;
223	spin_lock_init(&v_devc[0]->lock);
224
225	midi_devs[midi1]->devc = v_devc[0];
226
227	midi_devs[midi1]->converter = &m->s_ops[0];
228	std_midi_synth.midi_dev = midi1;
229	memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth,
230		sizeof (struct synth_operations));
231	midi_devs[midi1]->converter->id = "V_MIDI 1";
232
233	/* for MIDI-2 */
234	v_devc[1] = &m->v_ops[1];
235
236	memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2,
237		sizeof (struct midi_operations));
238
239	v_devc[1]->my_mididev = midi2;
240	v_devc[1]->pair_mididev = midi1;
241	v_devc[1]->opened = v_devc[1]->input_opened = 0;
242	v_devc[1]->intr_active = 0;
243	v_devc[1]->midi_input_intr = NULL;
244	spin_lock_init(&v_devc[1]->lock);
245
246	midi_devs[midi2]->devc = v_devc[1];
247	midi_devs[midi2]->converter = &m->s_ops[1];
248
249	std_midi_synth.midi_dev = midi2;
250	memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth,
251		sizeof (struct synth_operations));
252	midi_devs[midi2]->converter->id = "V_MIDI 2";
253
254	sequencer_init();
255	/* printk("Attached v_midi device\n"); */
256}
257
258static inline int __init probe_v_midi(struct address_info *hw_config)
259{
260	return(1);	/* always OK */
261}
262
263
264static void __exit unload_v_midi(struct address_info *hw_config)
265{
266	sound_unload_mididev(midi1);
267	sound_unload_mididev(midi2);
268	kfree(midi_mem);
269}
270
271static struct address_info cfg; /* dummy */
272
273static int __init init_vmidi(void)
274{
275	printk("MIDI Loopback device driver\n");
276	if (!probe_v_midi(&cfg))
277		return -ENODEV;
278	attach_v_midi(&cfg);
279
280	return 0;
281}
282
283static void __exit cleanup_vmidi(void)
284{
285	unload_v_midi(&cfg);
286}
287
288module_init(init_vmidi);
289module_exit(cleanup_vmidi);
290MODULE_LICENSE("GPL");
291