|  | /* | 
|  | * sound/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 |