diff options
Diffstat (limited to 'sound/oss/v_midi.c')
-rw-r--r-- | sound/oss/v_midi.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/sound/oss/v_midi.c b/sound/oss/v_midi.c new file mode 100644 index 00000000..f0b4151d --- /dev/null +++ b/sound/oss/v_midi.c @@ -0,0 +1,290 @@ +/* + * sound/oss/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/slab.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. + */ + + +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 = 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"); |