| /* | 
 |  * 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, SA_INTERRUPT|SA_SHIRQ, | 
 | 			 "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 |