| /* |
| * sound/oss/wf_midi.c |
| * |
| * The low level driver for the WaveFront ICS2115 MIDI interface(s) |
| * Note that there is also an MPU-401 emulation (actually, a UART-401 |
| * emulation) on the CS4232 on the Tropez Plus. This code has nothing |
| * to do with that interface at all. |
| * |
| * The interface is essentially just a UART-401, but is has the |
| * interesting property of supporting what Turtle Beach called |
| * "Virtual MIDI" mode. In this mode, there are effectively *two* |
| * MIDI buses accessible via the interface, one that is routed |
| * solely to/from the external WaveFront synthesizer and the other |
| * corresponding to the pin/socket connector used to link external |
| * MIDI devices to the board. |
| * |
| * This driver fully supports this mode, allowing two distinct |
| * midi devices (/dev/midiNN and /dev/midiNN+1) to be used |
| * completely independently, giving 32 channels of MIDI routing, |
| * 16 to the WaveFront synth and 16 to the external MIDI bus. |
| * |
| * Switching between the two is accomplished externally by the driver |
| * using the two otherwise unused MIDI bytes. See the code for more details. |
| * |
| * NOTE: VIRTUAL MIDI MODE IS ON BY DEFAULT (see wavefront.c) |
| * |
| * The main reason to turn off Virtual MIDI mode is when you want to |
| * tightly couple the WaveFront synth with an external MIDI |
| * device. You won't be able to distinguish the source of any MIDI |
| * data except via SysEx ID, but thats probably OK, since for the most |
| * part, the WaveFront won't be sending any MIDI data at all. |
| * |
| * The main reason to turn on Virtual MIDI Mode is to provide two |
| * completely independent 16-channel MIDI buses, one to the |
| * WaveFront and one to any external MIDI devices. Given the 32 |
| * voice nature of the WaveFront, its pretty easy to find a use |
| * for all 16 channels driving just that synth. |
| * |
| */ |
| |
| /* |
| * Copyright (C) by Paul Barton-Davis 1998 |
| * Some portions of this file are derived from work that is: |
| * |
| * 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. |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/interrupt.h> |
| #include <linux/spinlock.h> |
| #include "sound_config.h" |
| |
| #include <linux/wavefront.h> |
| |
| #ifdef MODULE |
| |
| struct wf_mpu_config { |
| int base; |
| #define DATAPORT(d) (d)->base |
| #define COMDPORT(d) (d)->base+1 |
| #define STATPORT(d) (d)->base+1 |
| |
| int irq; |
| int opened; |
| int devno; |
| int synthno; |
| int mode; |
| #define MODE_MIDI 1 |
| #define MODE_SYNTH 2 |
| |
| void (*inputintr) (int dev, unsigned char data); |
| char isvirtual; /* do virtual I/O stuff */ |
| }; |
| |
| static struct wf_mpu_config devs[2]; |
| static struct wf_mpu_config *phys_dev = &devs[0]; |
| static struct wf_mpu_config *virt_dev = &devs[1]; |
| |
| static void start_uart_mode (void); |
| static DEFINE_SPINLOCK(lock); |
| |
| #define OUTPUT_READY 0x40 |
| #define INPUT_AVAIL 0x80 |
| #define MPU_ACK 0xFE |
| #define UART_MODE_ON 0x3F |
| |
| static inline int wf_mpu_status (void) |
| { |
| return inb (STATPORT (phys_dev)); |
| } |
| |
| static inline int input_avail (void) |
| { |
| return !(wf_mpu_status() & INPUT_AVAIL); |
| } |
| |
| static inline int output_ready (void) |
| { |
| return !(wf_mpu_status() & OUTPUT_READY); |
| } |
| |
| static inline int read_data (void) |
| { |
| return inb (DATAPORT (phys_dev)); |
| } |
| |
| static inline void write_data (unsigned char byte) |
| { |
| outb (byte, DATAPORT (phys_dev)); |
| } |
| |
| /* |
| * States for the input scanner (should be in dev_table.h) |
| */ |
| |
| #define MST_SYSMSG 100 /* System message (sysx etc). */ |
| #define MST_MTC 102 /* Midi Time Code (MTC) qframe msg */ |
| #define MST_SONGSEL 103 /* Song select */ |
| #define MST_SONGPOS 104 /* Song position pointer */ |
| #define MST_TIMED 105 /* Leading timing byte rcvd */ |
| |
| /* buffer space check for input scanner */ |
| |
| #define BUFTEST(mi) if (mi->m_ptr >= MI_MAX || mi->m_ptr < 0) \ |
| {printk(KERN_ERR "WF-MPU: Invalid buffer pointer %d/%d, s=%d\n", \ |
| mi->m_ptr, mi->m_left, mi->m_state);mi->m_ptr--;} |
| |
| static unsigned char len_tab[] = /* # of data bytes following a status |
| */ |
| { |
| 2, /* 8x */ |
| 2, /* 9x */ |
| 2, /* Ax */ |
| 2, /* Bx */ |
| 1, /* Cx */ |
| 1, /* Dx */ |
| 2, /* Ex */ |
| 0 /* Fx */ |
| }; |
| |
| static int |
| wf_mpu_input_scanner (int devno, int synthdev, unsigned char midic) |
| |
| { |
| struct midi_input_info *mi = &midi_devs[devno]->in_info; |
| |
| switch (mi->m_state) { |
| case MST_INIT: |
| switch (midic) { |
| case 0xf8: |
| /* Timer overflow */ |
| break; |
| |
| case 0xfc: |
| break; |
| |
| case 0xfd: |
| /* XXX do something useful with this. If there is |
| an external MIDI timer (e.g. a hardware sequencer, |
| a useful timer can be derived ... |
| |
| For now, no timer support. |
| */ |
| break; |
| |
| case 0xfe: |
| return MPU_ACK; |
| break; |
| |
| case 0xf0: |
| case 0xf1: |
| case 0xf2: |
| case 0xf3: |
| case 0xf4: |
| case 0xf5: |
| case 0xf6: |
| case 0xf7: |
| break; |
| |
| case 0xf9: |
| break; |
| |
| case 0xff: |
| mi->m_state = MST_SYSMSG; |
| break; |
| |
| default: |
| if (midic <= 0xef) { |
| mi->m_state = MST_TIMED; |
| } |
| else |
| printk (KERN_ERR "<MPU: Unknown event %02x> ", |
| midic); |
| } |
| break; |
| |
| case MST_TIMED: |
| { |
| int msg = ((int) (midic & 0xf0) >> 4); |
| |
| mi->m_state = MST_DATA; |
| |
| if (msg < 8) { /* Data byte */ |
| |
| msg = ((int) (mi->m_prev_status & 0xf0) >> 4); |
| msg -= 8; |
| mi->m_left = len_tab[msg] - 1; |
| |
| mi->m_ptr = 2; |
| mi->m_buf[0] = mi->m_prev_status; |
| mi->m_buf[1] = midic; |
| |
| if (mi->m_left <= 0) { |
| mi->m_state = MST_INIT; |
| do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); |
| mi->m_ptr = 0; |
| } |
| } else if (msg == 0xf) { /* MPU MARK */ |
| |
| mi->m_state = MST_INIT; |
| |
| switch (midic) { |
| case 0xf8: |
| break; |
| |
| case 0xf9: |
| break; |
| |
| case 0xfc: |
| break; |
| |
| default: |
| break; |
| } |
| } else { |
| mi->m_prev_status = midic; |
| msg -= 8; |
| mi->m_left = len_tab[msg]; |
| |
| mi->m_ptr = 1; |
| mi->m_buf[0] = midic; |
| |
| if (mi->m_left <= 0) { |
| mi->m_state = MST_INIT; |
| do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); |
| mi->m_ptr = 0; |
| } |
| } |
| } |
| break; |
| |
| case MST_SYSMSG: |
| switch (midic) { |
| case 0xf0: |
| mi->m_state = MST_SYSEX; |
| break; |
| |
| case 0xf1: |
| mi->m_state = MST_MTC; |
| break; |
| |
| case 0xf2: |
| mi->m_state = MST_SONGPOS; |
| mi->m_ptr = 0; |
| break; |
| |
| case 0xf3: |
| mi->m_state = MST_SONGSEL; |
| break; |
| |
| case 0xf6: |
| mi->m_state = MST_INIT; |
| |
| /* |
| * Real time messages |
| */ |
| case 0xf8: |
| /* midi clock */ |
| mi->m_state = MST_INIT; |
| /* XXX need ext MIDI timer support */ |
| break; |
| |
| case 0xfA: |
| mi->m_state = MST_INIT; |
| /* XXX need ext MIDI timer support */ |
| break; |
| |
| case 0xFB: |
| mi->m_state = MST_INIT; |
| /* XXX need ext MIDI timer support */ |
| break; |
| |
| case 0xFC: |
| mi->m_state = MST_INIT; |
| /* XXX need ext MIDI timer support */ |
| break; |
| |
| case 0xFE: |
| /* active sensing */ |
| mi->m_state = MST_INIT; |
| break; |
| |
| case 0xff: |
| mi->m_state = MST_INIT; |
| break; |
| |
| default: |
| printk (KERN_ERR "unknown MIDI sysmsg %0x\n", midic); |
| mi->m_state = MST_INIT; |
| } |
| break; |
| |
| case MST_MTC: |
| mi->m_state = MST_INIT; |
| break; |
| |
| case MST_SYSEX: |
| if (midic == 0xf7) { |
| mi->m_state = MST_INIT; |
| } else { |
| /* XXX fix me */ |
| } |
| break; |
| |
| case MST_SONGPOS: |
| BUFTEST (mi); |
| mi->m_buf[mi->m_ptr++] = midic; |
| if (mi->m_ptr == 2) { |
| mi->m_state = MST_INIT; |
| mi->m_ptr = 0; |
| /* XXX need ext MIDI timer support */ |
| } |
| break; |
| |
| case MST_DATA: |
| BUFTEST (mi); |
| mi->m_buf[mi->m_ptr++] = midic; |
| if ((--mi->m_left) <= 0) { |
| mi->m_state = MST_INIT; |
| do_midi_msg (synthdev, mi->m_buf, mi->m_ptr); |
| mi->m_ptr = 0; |
| } |
| break; |
| |
| default: |
| printk (KERN_ERR "Bad state %d ", mi->m_state); |
| mi->m_state = MST_INIT; |
| } |
| |
| return 1; |
| } |
| |
| static irqreturn_t |
| wf_mpuintr(int irq, void *dev_id, struct pt_regs *dummy) |
| |
| { |
| struct wf_mpu_config *physical_dev = dev_id; |
| static struct wf_mpu_config *input_dev; |
| struct midi_input_info *mi = &midi_devs[physical_dev->devno]->in_info; |
| int n; |
| |
| if (!input_avail()) { /* not for us */ |
| return IRQ_NONE; |
| } |
| |
| if (mi->m_busy) |
| return IRQ_HANDLED; |
| spin_lock(&lock); |
| mi->m_busy = 1; |
| |
| if (!input_dev) { |
| input_dev = physical_dev; |
| } |
| |
| n = 50; /* XXX why ? */ |
| |
| do { |
| unsigned char c = read_data (); |
| |
| if (phys_dev->isvirtual) { |
| |
| if (c == WF_EXTERNAL_SWITCH) { |
| input_dev = virt_dev; |
| continue; |
| } else if (c == WF_INTERNAL_SWITCH) { |
| input_dev = phys_dev; |
| continue; |
| } /* else just leave it as it is */ |
| |
| } else { |
| input_dev = phys_dev; |
| } |
| |
| if (input_dev->mode == MODE_SYNTH) { |
| |
| wf_mpu_input_scanner (input_dev->devno, |
| input_dev->synthno, c); |
| |
| } else if (input_dev->opened & OPEN_READ) { |
| |
| if (input_dev->inputintr) { |
| input_dev->inputintr (input_dev->devno, c); |
| } |
| } |
| |
| } while (input_avail() && n-- > 0); |
| |
| mi->m_busy = 0; |
| spin_unlock(&lock); |
| return IRQ_HANDLED; |
| } |
| |
| static int |
| wf_mpu_open (int dev, int mode, |
| void (*input) (int dev, unsigned char data), |
| void (*output) (int dev) |
| ) |
| { |
| struct wf_mpu_config *devc; |
| |
| if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) |
| return -(ENXIO); |
| |
| if (phys_dev->devno == dev) { |
| devc = phys_dev; |
| } else if (phys_dev->isvirtual && virt_dev->devno == dev) { |
| devc = virt_dev; |
| } else { |
| printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); |
| return -(EINVAL); |
| } |
| |
| if (devc->opened) { |
| return -(EBUSY); |
| } |
| |
| devc->mode = MODE_MIDI; |
| devc->opened = mode; |
| devc->synthno = 0; |
| |
| devc->inputintr = input; |
| return 0; |
| } |
| |
| static void |
| wf_mpu_close (int dev) |
| { |
| struct wf_mpu_config *devc; |
| |
| if (dev < 0 || dev >= num_midis || midi_devs[dev]==NULL) |
| return; |
| |
| if (phys_dev->devno == dev) { |
| devc = phys_dev; |
| } else if (phys_dev->isvirtual && virt_dev->devno == dev) { |
| devc = virt_dev; |
| } else { |
| printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); |
| return; |
| } |
| |
| devc->mode = 0; |
| devc->inputintr = NULL; |
| devc->opened = 0; |
| } |
| |
| static int |
| wf_mpu_out (int dev, unsigned char midi_byte) |
| { |
| int timeout; |
| unsigned long flags; |
| static int lastoutdev = -1; |
| unsigned char switchch; |
| |
| if (phys_dev->isvirtual && lastoutdev != dev) { |
| |
| if (dev == phys_dev->devno) { |
| switchch = WF_INTERNAL_SWITCH; |
| } else if (dev == virt_dev->devno) { |
| switchch = WF_EXTERNAL_SWITCH; |
| } else { |
| printk (KERN_ERR "WF-MPU: bad device number %d", dev); |
| return (0); |
| } |
| |
| /* XXX fix me */ |
| |
| for (timeout = 30000; timeout > 0 && !output_ready (); |
| timeout--); |
| |
| spin_lock_irqsave(&lock,flags); |
| |
| if (!output_ready ()) { |
| printk (KERN_WARNING "WF-MPU: Send switch " |
| "byte timeout\n"); |
| spin_unlock_irqrestore(&lock,flags); |
| return 0; |
| } |
| |
| write_data (switchch); |
| spin_unlock_irqrestore(&lock,flags); |
| } |
| |
| lastoutdev = dev; |
| |
| /* |
| * Sometimes it takes about 30000 loops before the output becomes ready |
| * (After reset). Normally it takes just about 10 loops. |
| */ |
| |
| /* XXX fix me */ |
| |
| for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); |
| |
| spin_lock_irqsave(&lock,flags); |
| if (!output_ready ()) { |
| spin_unlock_irqrestore(&lock,flags); |
| printk (KERN_WARNING "WF-MPU: Send data timeout\n"); |
| return 0; |
| } |
| |
| write_data (midi_byte); |
| spin_unlock_irqrestore(&lock,flags); |
| |
| return 1; |
| } |
| |
| static inline int wf_mpu_start_read (int dev) { |
| return 0; |
| } |
| |
| static inline int wf_mpu_end_read (int dev) { |
| return 0; |
| } |
| |
| static int wf_mpu_ioctl (int dev, unsigned cmd, void __user *arg) |
| { |
| printk (KERN_WARNING |
| "WF-MPU: Intelligent mode not supported by hardware.\n"); |
| return -(EINVAL); |
| } |
| |
| static int wf_mpu_buffer_status (int dev) |
| { |
| return 0; |
| } |
| |
| static struct synth_operations wf_mpu_synth_operations[2]; |
| static struct midi_operations wf_mpu_midi_operations[2]; |
| |
| static struct midi_operations wf_mpu_midi_proto = |
| { |
| .owner = THIS_MODULE, |
| .info = {"WF-MPU MIDI", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, |
| .in_info = {0}, /* in_info */ |
| .open = wf_mpu_open, |
| .close = wf_mpu_close, |
| .ioctl = wf_mpu_ioctl, |
| .outputc = wf_mpu_out, |
| .start_read = wf_mpu_start_read, |
| .end_read = wf_mpu_end_read, |
| .buffer_status = wf_mpu_buffer_status, |
| }; |
| |
| static struct synth_info wf_mpu_synth_info_proto = |
| {"WaveFront MPU-401 interface", 0, |
| SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT}; |
| |
| static struct synth_info wf_mpu_synth_info[2]; |
| |
| static int |
| wf_mpu_synth_ioctl (int dev, unsigned int cmd, void __user *arg) |
| { |
| int midi_dev; |
| int index; |
| |
| midi_dev = synth_devs[dev]->midi_dev; |
| |
| if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) |
| return -(ENXIO); |
| |
| if (midi_dev == phys_dev->devno) { |
| index = 0; |
| } else if (phys_dev->isvirtual && midi_dev == virt_dev->devno) { |
| index = 1; |
| } else { |
| return -(EINVAL); |
| } |
| |
| switch (cmd) { |
| |
| case SNDCTL_SYNTH_INFO: |
| if (copy_to_user(arg, |
| &wf_mpu_synth_info[index], |
| sizeof (struct synth_info))) |
| return -EFAULT; |
| return 0; |
| |
| case SNDCTL_SYNTH_MEMAVL: |
| return 0x7fffffff; |
| |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int |
| wf_mpu_synth_open (int dev, int mode) |
| { |
| int midi_dev; |
| struct wf_mpu_config *devc; |
| |
| midi_dev = synth_devs[dev]->midi_dev; |
| |
| if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) { |
| return -(ENXIO); |
| } |
| |
| if (phys_dev->devno == midi_dev) { |
| devc = phys_dev; |
| } else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) { |
| devc = virt_dev; |
| } else { |
| printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); |
| return -(EINVAL); |
| } |
| |
| if (devc->opened) { |
| return -(EBUSY); |
| } |
| |
| devc->mode = MODE_SYNTH; |
| devc->synthno = dev; |
| devc->opened = mode; |
| devc->inputintr = NULL; |
| return 0; |
| } |
| |
| static void |
| wf_mpu_synth_close (int dev) |
| { |
| int midi_dev; |
| struct wf_mpu_config *devc; |
| |
| midi_dev = synth_devs[dev]->midi_dev; |
| |
| if (phys_dev->devno == midi_dev) { |
| devc = phys_dev; |
| } else if (phys_dev->isvirtual && virt_dev->devno == midi_dev) { |
| devc = virt_dev; |
| } else { |
| printk (KERN_ERR "WF-MPU: unknown device number %d\n", dev); |
| return; |
| } |
| |
| devc->inputintr = NULL; |
| devc->opened = 0; |
| devc->mode = 0; |
| } |
| |
| #define _MIDI_SYNTH_C_ |
| #define MIDI_SYNTH_NAME "WaveFront (MIDI)" |
| #define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT |
| #include "midi_synth.h" |
| |
| static struct synth_operations wf_mpu_synth_proto = |
| { |
| .owner = THIS_MODULE, |
| .id = "WaveFront (ICS2115)", |
| .info = NULL, /* info field, filled in during configuration */ |
| .midi_dev = 0, /* MIDI dev XXX should this be -1 ? */ |
| .synth_type = SYNTH_TYPE_MIDI, |
| .synth_subtype = SAMPLE_TYPE_WAVEFRONT, |
| .open = wf_mpu_synth_open, |
| .close = wf_mpu_synth_close, |
| .ioctl = wf_mpu_synth_ioctl, |
| .kill_note = midi_synth_kill_note, |
| .start_note = midi_synth_start_note, |
| .set_instr = midi_synth_set_instr, |
| .reset = midi_synth_reset, |
| .hw_control = midi_synth_hw_control, |
| .load_patch = midi_synth_load_patch, |
| .aftertouch = midi_synth_aftertouch, |
| .controller = midi_synth_controller, |
| .panning = midi_synth_panning, |
| .bender = midi_synth_bender, |
| .setup_voice = midi_synth_setup_voice, |
| .send_sysex = midi_synth_send_sysex |
| }; |
| |
| static int |
| config_wf_mpu (struct wf_mpu_config *dev) |
| |
| { |
| int is_external; |
| char *name; |
| int index; |
| |
| if (dev == phys_dev) { |
| name = "WaveFront internal MIDI"; |
| is_external = 0; |
| index = 0; |
| memcpy ((char *) &wf_mpu_synth_operations[index], |
| (char *) &wf_mpu_synth_proto, |
| sizeof (struct synth_operations)); |
| } else { |
| name = "WaveFront external MIDI"; |
| is_external = 1; |
| index = 1; |
| /* no synth operations for an external MIDI interface */ |
| } |
| |
| memcpy ((char *) &wf_mpu_synth_info[dev->devno], |
| (char *) &wf_mpu_synth_info_proto, |
| sizeof (struct synth_info)); |
| |
| strcpy (wf_mpu_synth_info[index].name, name); |
| |
| wf_mpu_synth_operations[index].midi_dev = dev->devno; |
| wf_mpu_synth_operations[index].info = &wf_mpu_synth_info[index]; |
| |
| memcpy ((char *) &wf_mpu_midi_operations[index], |
| (char *) &wf_mpu_midi_proto, |
| sizeof (struct midi_operations)); |
| |
| if (is_external) { |
| wf_mpu_midi_operations[index].converter = NULL; |
| } else { |
| wf_mpu_midi_operations[index].converter = |
| &wf_mpu_synth_operations[index]; |
| } |
| |
| strcpy (wf_mpu_midi_operations[index].info.name, name); |
| |
| midi_devs[dev->devno] = &wf_mpu_midi_operations[index]; |
| midi_devs[dev->devno]->in_info.m_busy = 0; |
| midi_devs[dev->devno]->in_info.m_state = MST_INIT; |
| midi_devs[dev->devno]->in_info.m_ptr = 0; |
| midi_devs[dev->devno]->in_info.m_left = 0; |
| midi_devs[dev->devno]->in_info.m_prev_status = 0; |
| |
| devs[index].opened = 0; |
| devs[index].mode = 0; |
| |
| return (0); |
| } |
| |
| int virtual_midi_enable (void) |
| |
| { |
| if ((virt_dev->devno < 0) && |
| (virt_dev->devno = sound_alloc_mididev()) == -1) { |
| printk (KERN_ERR |
| "WF-MPU: too many midi devices detected\n"); |
| return -1; |
| } |
| |
| config_wf_mpu (virt_dev); |
| |
| phys_dev->isvirtual = 1; |
| return virt_dev->devno; |
| } |
| |
| int |
| virtual_midi_disable (void) |
| |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&lock,flags); |
| |
| wf_mpu_close (virt_dev->devno); |
| /* no synth on virt_dev, so no need to call wf_mpu_synth_close() */ |
| phys_dev->isvirtual = 0; |
| |
| spin_unlock_irqrestore(&lock,flags); |
| |
| return 0; |
| } |
| |
| int __init detect_wf_mpu (int irq, int io_base) |
| { |
| if (!request_region(io_base, 2, "wavefront midi")) { |
| printk (KERN_WARNING "WF-MPU: I/O port %x already in use.\n", |
| io_base); |
| return -1; |
| } |
| |
| phys_dev->base = io_base; |
| phys_dev->irq = irq; |
| phys_dev->devno = -1; |
| virt_dev->devno = -1; |
| |
| return 0; |
| } |
| |
| int __init install_wf_mpu (void) |
| { |
| if ((phys_dev->devno = sound_alloc_mididev()) < 0){ |
| |
| printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n"); |
| release_region(phys_dev->base, 2); |
| return -1; |
| } |
| |
| phys_dev->isvirtual = 0; |
| |
| if (config_wf_mpu (phys_dev)) { |
| |
| printk (KERN_WARNING |
| "WF-MPU: configuration for MIDI device %d failed\n", |
| phys_dev->devno); |
| sound_unload_mididev (phys_dev->devno); |
| |
| } |
| |
| /* OK, now we're configured to handle an interrupt ... */ |
| |
| if (request_irq (phys_dev->irq, wf_mpuintr, IRQF_DISABLED|IRQF_SHARED, |
| "wavefront midi", phys_dev) < 0) { |
| |
| printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n", |
| phys_dev->irq); |
| return -1; |
| |
| } |
| |
| /* This being a WaveFront (ICS-2115) emulated MPU-401, we have |
| to switch it into UART (dumb) mode, because otherwise, it |
| won't do anything at all. |
| */ |
| |
| start_uart_mode (); |
| |
| return phys_dev->devno; |
| } |
| |
| void |
| uninstall_wf_mpu (void) |
| |
| { |
| release_region (phys_dev->base, 2); |
| free_irq (phys_dev->irq, phys_dev); |
| sound_unload_mididev (phys_dev->devno); |
| |
| if (virt_dev->devno >= 0) { |
| sound_unload_mididev (virt_dev->devno); |
| } |
| } |
| |
| static void |
| start_uart_mode (void) |
| |
| { |
| int ok, i; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&lock,flags); |
| |
| /* XXX fix me */ |
| |
| for (i = 0; i < 30000 && !output_ready (); i++); |
| |
| outb (UART_MODE_ON, COMDPORT(phys_dev)); |
| |
| for (ok = 0, i = 50000; i > 0 && !ok; i--) { |
| if (input_avail ()) { |
| if (read_data () == MPU_ACK) { |
| ok = 1; |
| } |
| } |
| } |
| |
| spin_unlock_irqrestore(&lock,flags); |
| } |
| #endif |