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/spinlock.h> 25#include "sound_config.h" 26 27#include "v_midi.h" 28 29static vmidi_devc *v_devc[2] = { NULL, NULL}; 30static int midi1,midi2; 31static 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 42static int v_midi_open (int dev, int mode, 43 void (*input) (int dev, unsigned char data), 44 void (*output) (int dev) 45) 46{ 47 vmidi_devc *devc = midi_devs[dev]->devc; 48 unsigned long flags; 49 50 if (devc == NULL) 51 return -(ENXIO); 52 53 spin_lock_irqsave(&devc->lock,flags); 54 if (devc->opened) 55 { 56 spin_unlock_irqrestore(&devc->lock,flags); 57 return -(EBUSY); 58 } 59 devc->opened = 1; 60 spin_unlock_irqrestore(&devc->lock,flags); 61 62 devc->intr_active = 1; 63 64 if (mode & OPEN_READ) 65 { 66 devc->input_opened = 1; 67 devc->midi_input_intr = input; 68 } 69 70 return 0; 71} 72 73static void v_midi_close (int dev) 74{ 75 vmidi_devc *devc = midi_devs[dev]->devc; 76 unsigned long flags; 77 78 if (devc == NULL) 79 return; 80 81 spin_lock_irqsave(&devc->lock,flags); 82 devc->intr_active = 0; 83 devc->input_opened = 0; 84 devc->opened = 0; 85 spin_unlock_irqrestore(&devc->lock,flags); 86} 87 88static int v_midi_out (int dev, unsigned char midi_byte) 89{ 90 vmidi_devc *devc = midi_devs[dev]->devc; 91 vmidi_devc *pdevc; 92 93 if (devc == NULL) 94 return -ENXIO; 95 96 pdevc = midi_devs[devc->pair_mididev]->devc; 97 if (pdevc->input_opened > 0){ 98 if (MIDIbuf_avail(pdevc->my_mididev) > 500) 99 return 0; 100 pdevc->midi_input_intr (pdevc->my_mididev, midi_byte); 101 } 102 return 1; 103} 104 105static inline int v_midi_start_read (int dev) 106{ 107 return 0; 108} 109 110static int v_midi_end_read (int dev) 111{ 112 vmidi_devc *devc = midi_devs[dev]->devc; 113 if (devc == NULL) 114 return -ENXIO; 115 116 devc->intr_active = 0; 117 return 0; 118} 119 120/* why -EPERM and not -EINVAL?? */ 121 122static inline int v_midi_ioctl (int dev, unsigned cmd, void __user *arg) 123{ 124 return -EPERM; 125} 126 127 128#define MIDI_SYNTH_NAME "Loopback MIDI" 129#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT 130 131#include "midi_synth.h" 132 133static struct midi_operations v_midi_operations = 134{ 135 .owner = THIS_MODULE, 136 .info = {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI}, 137 .converter = &std_midi_synth, 138 .in_info = {0}, 139 .open = v_midi_open, 140 .close = v_midi_close, 141 .ioctl = v_midi_ioctl, 142 .outputc = v_midi_out, 143 .start_read = v_midi_start_read, 144 .end_read = v_midi_end_read, 145}; 146 147static struct midi_operations v_midi_operations2 = 148{ 149 .owner = THIS_MODULE, 150 .info = {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI}, 151 .converter = &std_midi_synth, 152 .in_info = {0}, 153 .open = v_midi_open, 154 .close = v_midi_close, 155 .ioctl = v_midi_ioctl, 156 .outputc = v_midi_out, 157 .start_read = v_midi_start_read, 158 .end_read = v_midi_end_read, 159}; 160 161/* 162 * We kmalloc just one of these - it makes life simpler and the code 163 * cleaner and the memory handling far more efficient 164 */ 165 166struct vmidi_memory 167{ 168 /* Must be first */ 169 struct midi_operations m_ops[2]; 170 struct synth_operations s_ops[2]; 171 struct vmidi_devc v_ops[2]; 172}; 173 174static void __init attach_v_midi (struct address_info *hw_config) 175{ 176 struct vmidi_memory *m; 177 /* printk("Attaching v_midi device.....\n"); */ 178 179 midi1 = sound_alloc_mididev(); 180 if (midi1 == -1) 181 { 182 printk(KERN_ERR "v_midi: Too many midi devices detected\n"); 183 return; 184 } 185 186 m = kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL); 187 if (m == NULL) 188 { 189 printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n"); 190 sound_unload_mididev(midi1); 191 return; 192 } 193 194 midi_mem = m; 195 196 midi_devs[midi1] = &m->m_ops[0]; 197 198 199 midi2 = sound_alloc_mididev(); 200 if (midi2 == -1) 201 { 202 printk (KERN_ERR "v_midi: Too many midi devices detected\n"); 203 kfree(m); 204 sound_unload_mididev(midi1); 205 return; 206 } 207 208 midi_devs[midi2] = &m->m_ops[1]; 209 210 /* printk("VMIDI1: %d VMIDI2: %d\n",midi1,midi2); */ 211 212 /* for MIDI-1 */ 213 v_devc[0] = &m->v_ops[0]; 214 memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations, 215 sizeof (struct midi_operations)); 216 217 v_devc[0]->my_mididev = midi1; 218 v_devc[0]->pair_mididev = midi2; 219 v_devc[0]->opened = v_devc[0]->input_opened = 0; 220 v_devc[0]->intr_active = 0; 221 v_devc[0]->midi_input_intr = NULL; 222 spin_lock_init(&v_devc[0]->lock); 223 224 midi_devs[midi1]->devc = v_devc[0]; 225 226 midi_devs[midi1]->converter = &m->s_ops[0]; 227 std_midi_synth.midi_dev = midi1; 228 memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth, 229 sizeof (struct synth_operations)); 230 midi_devs[midi1]->converter->id = "V_MIDI 1"; 231 232 /* for MIDI-2 */ 233 v_devc[1] = &m->v_ops[1]; 234 235 memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2, 236 sizeof (struct midi_operations)); 237 238 v_devc[1]->my_mididev = midi2; 239 v_devc[1]->pair_mididev = midi1; 240 v_devc[1]->opened = v_devc[1]->input_opened = 0; 241 v_devc[1]->intr_active = 0; 242 v_devc[1]->midi_input_intr = NULL; 243 spin_lock_init(&v_devc[1]->lock); 244 245 midi_devs[midi2]->devc = v_devc[1]; 246 midi_devs[midi2]->converter = &m->s_ops[1]; 247 248 std_midi_synth.midi_dev = midi2; 249 memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth, 250 sizeof (struct synth_operations)); 251 midi_devs[midi2]->converter->id = "V_MIDI 2"; 252 253 sequencer_init(); 254 /* printk("Attached v_midi device\n"); */ 255} 256 257static inline int __init probe_v_midi(struct address_info *hw_config) 258{ 259 return(1); /* always OK */ 260} 261 262 263static void __exit unload_v_midi(struct address_info *hw_config) 264{ 265 sound_unload_mididev(midi1); 266 sound_unload_mididev(midi2); 267 kfree(midi_mem); 268} 269 270static struct address_info cfg; /* dummy */ 271 272static int __init init_vmidi(void) 273{ 274 printk("MIDI Loopback device driver\n"); 275 if (!probe_v_midi(&cfg)) 276 return -ENODEV; 277 attach_v_midi(&cfg); 278 279 return 0; 280} 281 282static void __exit cleanup_vmidi(void) 283{ 284 unload_v_midi(&cfg); 285} 286 287module_init(init_vmidi); 288module_exit(cleanup_vmidi); 289MODULE_LICENSE("GPL"); 290