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