blob: fc0ba276cc8fd133213bf0c7975c315b17f66bf4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Uwe Zeisbergerf30c2262006-10-03 23:01:26 +02002 * sound/oss/v_midi.c
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090024#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#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
Linus Torvalds1da177e2005-04-16 15:20:36 -070043static 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)
Joe Perches9a303dc2015-03-23 12:44:49 -070052 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
54 spin_lock_irqsave(&devc->lock,flags);
55 if (devc->opened)
56 {
57 spin_unlock_irqrestore(&devc->lock,flags);
Joe Perches9a303dc2015-03-23 12:44:49 -070058 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 }
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
Robert P. J. Day5cbded52006-12-13 00:35:56 -0800187 m = kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 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");