1/* 2 * sound/oss/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/slab.h> 25#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 43static 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) 52 return -(ENXIO); 53 54 spin_lock_irqsave(&devc->lock,flags); 55 if (devc->opened) 56 { 57 spin_unlock_irqrestore(&devc->lock,flags); 58 return -(EBUSY); 59 } 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 187 m = kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL); 188 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"); 291