added mtd driver
[linux-2.4.git] / drivers / sound / 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
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         save_flags (flags);
56         cli();
57         if (devc->opened)
58         {
59                 restore_flags (flags);
60                 return -(EBUSY);
61         }
62         devc->opened = 1;
63         restore_flags (flags);
64
65         devc->intr_active = 1;
66
67         if (mode & OPEN_READ)
68         {
69                 devc->input_opened = 1;
70                 devc->midi_input_intr = input;
71         }
72
73         return 0;
74 }
75
76 static void v_midi_close (int dev)
77 {
78         vmidi_devc *devc = midi_devs[dev]->devc;
79         unsigned long flags;
80
81         if (devc == NULL)
82                 return;
83
84         save_flags (flags);
85         cli ();
86         devc->intr_active = 0;
87         devc->input_opened = 0;
88         devc->opened = 0;
89         restore_flags (flags);
90 }
91
92 static int v_midi_out (int dev, unsigned char midi_byte)
93 {
94         vmidi_devc *devc = midi_devs[dev]->devc;
95         vmidi_devc *pdevc = midi_devs[devc->pair_mididev]->devc;
96
97         if (devc == NULL)
98                 return -(ENXIO);
99
100         if (pdevc->input_opened > 0){
101                 if (MIDIbuf_avail(pdevc->my_mididev) > 500)
102                         return 0;
103                 pdevc->midi_input_intr (pdevc->my_mididev, midi_byte);
104         }
105         return 1;
106 }
107
108 static inline int v_midi_start_read (int dev)
109 {
110         return 0;
111 }
112
113 static int v_midi_end_read (int dev)
114 {
115         vmidi_devc *devc = midi_devs[dev]->devc;
116         if (devc == NULL)
117                 return -ENXIO;
118
119         devc->intr_active = 0;
120         return 0;
121 }
122
123 /* why -EPERM and not -EINVAL?? */
124
125 static inline int v_midi_ioctl (int dev, unsigned cmd, caddr_t arg)
126 {
127         return -EPERM;
128 }
129
130
131 #define MIDI_SYNTH_NAME "Loopback MIDI"
132 #define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
133
134 #include "midi_synth.h"
135
136 static struct midi_operations v_midi_operations =
137 {
138         owner:          THIS_MODULE,
139         info:           {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI},
140         converter:      &std_midi_synth,
141         in_info:        {0},
142         open:           v_midi_open,
143         close:          v_midi_close,
144         ioctl:          v_midi_ioctl,
145         outputc:        v_midi_out,
146         start_read:     v_midi_start_read,
147         end_read:       v_midi_end_read,
148 };
149
150 static struct midi_operations v_midi_operations2 =
151 {
152         owner:          THIS_MODULE,
153         info:           {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI},
154         converter:      &std_midi_synth,
155         in_info:        {0},
156         open:           v_midi_open,
157         close:          v_midi_close,
158         ioctl:          v_midi_ioctl,
159         outputc:        v_midi_out,
160         start_read:     v_midi_start_read,
161         end_read:       v_midi_end_read,
162 };
163
164 /*
165  *      We kmalloc just one of these - it makes life simpler and the code
166  *      cleaner and the memory handling far more efficient
167  */
168  
169 struct vmidi_memory
170 {
171         /* Must be first */
172         struct midi_operations m_ops[2];
173         struct synth_operations s_ops[2];
174         struct vmidi_devc v_ops[2];
175 };
176
177 static void __init attach_v_midi (struct address_info *hw_config)
178 {
179         struct vmidi_memory *m;
180         /* printk("Attaching v_midi device.....\n"); */
181
182         midi1 = sound_alloc_mididev();
183         if (midi1 == -1)
184         {
185                 printk(KERN_ERR "v_midi: Too many midi devices detected\n");
186                 return;
187         }
188         
189         m=(struct vmidi_memory *)kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL);
190         if (m == NULL)
191         {
192                 printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n");
193                 sound_unload_mididev(midi1);
194                 return;
195         }
196         
197         midi_mem = m;
198         
199         midi_devs[midi1] = &m->m_ops[0];
200         
201
202         midi2 = sound_alloc_mididev();
203         if (midi2 == -1)
204         {
205                 printk (KERN_ERR "v_midi: Too many midi devices detected\n");
206                 kfree(m);
207                 sound_unload_mididev(midi1);
208                 return;
209         }
210
211         midi_devs[midi2] = &m->m_ops[1];
212
213         /* printk("VMIDI1: %d   VMIDI2: %d\n",midi1,midi2); */
214
215         /* for MIDI-1 */
216         v_devc[0] = &m->v_ops[0];
217         memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations,
218                 sizeof (struct midi_operations));
219
220         v_devc[0]->my_mididev = midi1;
221         v_devc[0]->pair_mididev = midi2;
222         v_devc[0]->opened = v_devc[0]->input_opened = 0;
223         v_devc[0]->intr_active = 0;
224         v_devc[0]->midi_input_intr = NULL;
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
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
258 static inline int __init probe_v_midi(struct address_info *hw_config)
259 {
260         return(1);      /* always OK */
261 }
262
263
264 static 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
271 static struct address_info cfg; /* dummy */
272
273 static 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
283 static void __exit cleanup_vmidi(void)
284 {
285         unload_v_midi(&cfg);
286 }
287
288 module_init(init_vmidi);
289 module_exit(cleanup_vmidi);
290 MODULE_LICENSE("GPL");