Merge master.kernel.org:/home/rmk/linux-2.6-arm
[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 static 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
73 static 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
88 static 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
105 static inline int v_midi_start_read (int dev)
106 {
107         return 0;
108 }
109
110 static 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
122 static 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
133 static 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
147 static 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  
166 struct 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
174 static 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=(struct vmidi_memory *)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
257 static inline int __init probe_v_midi(struct address_info *hw_config)
258 {
259         return(1);      /* always OK */
260 }
261
262
263 static 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
270 static struct address_info cfg; /* dummy */
271
272 static 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
282 static void __exit cleanup_vmidi(void)
283 {
284         unload_v_midi(&cfg);
285 }
286
287 module_init(init_vmidi);
288 module_exit(cleanup_vmidi);
289 MODULE_LICENSE("GPL");