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