summaryrefslogtreecommitdiff
path: root/sound/oss/v_midi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/oss/v_midi.c')
-rw-r--r--sound/oss/v_midi.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/sound/oss/v_midi.c b/sound/oss/v_midi.c
new file mode 100644
index 000000000000..077b76797665
--- /dev/null
+++ b/sound/oss/v_midi.c
@@ -0,0 +1,291 @@
+/*
+ * sound/v_midi.c
+ *
+ * The low level driver for the Sound Blaster DS chips.
+ *
+ *
+ * Copyright (C) by Hannu Savolainen 1993-1996
+ *
+ * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
+ * ??
+ *
+ * Changes
+ * Alan Cox Modularisation, changed memory allocations
+ * Christoph Hellwig Adapted to module_init/module_exit
+ *
+ * Status
+ * Untested
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include "sound_config.h"
+
+#include "v_midi.h"
+
+static vmidi_devc *v_devc[2] = { NULL, NULL};
+static int midi1,midi2;
+static void *midi_mem = NULL;
+
+/*
+ * The DSP channel can be used either for input or output. Variable
+ * 'sb_irq_mode' will be set when the program calls read or write first time
+ * after open. Current version doesn't support mode changes without closing
+ * and reopening the device. Support for this feature may be implemented in a
+ * future version of this driver.
+ */
+
+
+void (*midi_input_intr) (int dev, unsigned char data);
+
+static int v_midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ vmidi_devc *devc = midi_devs[dev]->devc;
+ unsigned long flags;
+
+ if (devc == NULL)
+ return -(ENXIO);
+
+ spin_lock_irqsave(&devc->lock,flags);
+ if (devc->opened)
+ {
+ spin_unlock_irqrestore(&devc->lock,flags);
+ return -(EBUSY);
+ }
+ devc->opened = 1;
+ spin_unlock_irqrestore(&devc->lock,flags);
+
+ devc->intr_active = 1;
+
+ if (mode & OPEN_READ)
+ {
+ devc->input_opened = 1;
+ devc->midi_input_intr = input;
+ }
+
+ return 0;
+}
+
+static void v_midi_close (int dev)
+{
+ vmidi_devc *devc = midi_devs[dev]->devc;
+ unsigned long flags;
+
+ if (devc == NULL)
+ return;
+
+ spin_lock_irqsave(&devc->lock,flags);
+ devc->intr_active = 0;
+ devc->input_opened = 0;
+ devc->opened = 0;
+ spin_unlock_irqrestore(&devc->lock,flags);
+}
+
+static int v_midi_out (int dev, unsigned char midi_byte)
+{
+ vmidi_devc *devc = midi_devs[dev]->devc;
+ vmidi_devc *pdevc;
+
+ if (devc == NULL)
+ return -ENXIO;
+
+ pdevc = midi_devs[devc->pair_mididev]->devc;
+ if (pdevc->input_opened > 0){
+ if (MIDIbuf_avail(pdevc->my_mididev) > 500)
+ return 0;
+ pdevc->midi_input_intr (pdevc->my_mididev, midi_byte);
+ }
+ return 1;
+}
+
+static inline int v_midi_start_read (int dev)
+{
+ return 0;
+}
+
+static int v_midi_end_read (int dev)
+{
+ vmidi_devc *devc = midi_devs[dev]->devc;
+ if (devc == NULL)
+ return -ENXIO;
+
+ devc->intr_active = 0;
+ return 0;
+}
+
+/* why -EPERM and not -EINVAL?? */
+
+static inline int v_midi_ioctl (int dev, unsigned cmd, void __user *arg)
+{
+ return -EPERM;
+}
+
+
+#define MIDI_SYNTH_NAME "Loopback MIDI"
+#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
+
+#include "midi_synth.h"
+
+static struct midi_operations v_midi_operations =
+{
+ .owner = THIS_MODULE,
+ .info = {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI},
+ .converter = &std_midi_synth,
+ .in_info = {0},
+ .open = v_midi_open,
+ .close = v_midi_close,
+ .ioctl = v_midi_ioctl,
+ .outputc = v_midi_out,
+ .start_read = v_midi_start_read,
+ .end_read = v_midi_end_read,
+};
+
+static struct midi_operations v_midi_operations2 =
+{
+ .owner = THIS_MODULE,
+ .info = {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI},
+ .converter = &std_midi_synth,
+ .in_info = {0},
+ .open = v_midi_open,
+ .close = v_midi_close,
+ .ioctl = v_midi_ioctl,
+ .outputc = v_midi_out,
+ .start_read = v_midi_start_read,
+ .end_read = v_midi_end_read,
+};
+
+/*
+ * We kmalloc just one of these - it makes life simpler and the code
+ * cleaner and the memory handling far more efficient
+ */
+
+struct vmidi_memory
+{
+ /* Must be first */
+ struct midi_operations m_ops[2];
+ struct synth_operations s_ops[2];
+ struct vmidi_devc v_ops[2];
+};
+
+static void __init attach_v_midi (struct address_info *hw_config)
+{
+ struct vmidi_memory *m;
+ /* printk("Attaching v_midi device.....\n"); */
+
+ midi1 = sound_alloc_mididev();
+ if (midi1 == -1)
+ {
+ printk(KERN_ERR "v_midi: Too many midi devices detected\n");
+ return;
+ }
+
+ m=(struct vmidi_memory *)kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL);
+ if (m == NULL)
+ {
+ printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n");
+ sound_unload_mididev(midi1);
+ return;
+ }
+
+ midi_mem = m;
+
+ midi_devs[midi1] = &m->m_ops[0];
+
+
+ midi2 = sound_alloc_mididev();
+ if (midi2 == -1)
+ {
+ printk (KERN_ERR "v_midi: Too many midi devices detected\n");
+ kfree(m);
+ sound_unload_mididev(midi1);
+ return;
+ }
+
+ midi_devs[midi2] = &m->m_ops[1];
+
+ /* printk("VMIDI1: %d VMIDI2: %d\n",midi1,midi2); */
+
+ /* for MIDI-1 */
+ v_devc[0] = &m->v_ops[0];
+ memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations,
+ sizeof (struct midi_operations));
+
+ v_devc[0]->my_mididev = midi1;
+ v_devc[0]->pair_mididev = midi2;
+ v_devc[0]->opened = v_devc[0]->input_opened = 0;
+ v_devc[0]->intr_active = 0;
+ v_devc[0]->midi_input_intr = NULL;
+ spin_lock_init(&v_devc[0]->lock);
+
+ midi_devs[midi1]->devc = v_devc[0];
+
+ midi_devs[midi1]->converter = &m->s_ops[0];
+ std_midi_synth.midi_dev = midi1;
+ memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth,
+ sizeof (struct synth_operations));
+ midi_devs[midi1]->converter->id = "V_MIDI 1";
+
+ /* for MIDI-2 */
+ v_devc[1] = &m->v_ops[1];
+
+ memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2,
+ sizeof (struct midi_operations));
+
+ v_devc[1]->my_mididev = midi2;
+ v_devc[1]->pair_mididev = midi1;
+ v_devc[1]->opened = v_devc[1]->input_opened = 0;
+ v_devc[1]->intr_active = 0;
+ v_devc[1]->midi_input_intr = NULL;
+ spin_lock_init(&v_devc[1]->lock);
+
+ midi_devs[midi2]->devc = v_devc[1];
+ midi_devs[midi2]->converter = &m->s_ops[1];
+
+ std_midi_synth.midi_dev = midi2;
+ memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth,
+ sizeof (struct synth_operations));
+ midi_devs[midi2]->converter->id = "V_MIDI 2";
+
+ sequencer_init();
+ /* printk("Attached v_midi device\n"); */
+}
+
+static inline int __init probe_v_midi(struct address_info *hw_config)
+{
+ return(1); /* always OK */
+}
+
+
+static void __exit unload_v_midi(struct address_info *hw_config)
+{
+ sound_unload_mididev(midi1);
+ sound_unload_mididev(midi2);
+ kfree(midi_mem);
+}
+
+static struct address_info cfg; /* dummy */
+
+static int __init init_vmidi(void)
+{
+ printk("MIDI Loopback device driver\n");
+ if (!probe_v_midi(&cfg))
+ return -ENODEV;
+ attach_v_midi(&cfg);
+
+ return 0;
+}
+
+static void __exit cleanup_vmidi(void)
+{
+ unload_v_midi(&cfg);
+}
+
+module_init(init_vmidi);
+module_exit(cleanup_vmidi);
+MODULE_LICENSE("GPL");