/* * 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 * Substantial 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 #include "sound_config.h" #include "soundmodule.h" #include #if (defined(CONFIG_WAVEFRONT) && defined(CONFIG_MIDI)) || defined(MODULE) struct wf_mpu_config { int base; /* I/O base */ int irq; int opened; /* Open mode */ int devno; int synthno; int mode; #define MODE_MIDI 1 #define MODE_SYNTH 2 void (*inputintr) (int dev, unsigned char data); /* Virtual MIDI support */ char configured_for_virtual; /* setup for virtual completed */ char isvirtual; /* do virtual I/O stuff */ char isexternal; /* i am an external interface */ int internal; /* external interface midi_devno */ int external; /* external interface midi_devno */ }; #define DATAPORT(base) (base) #define COMDPORT(base) (base+1) #define STATPORT(base) (base+1) static void start_uart_mode (struct wf_mpu_config *devc); static int wf_mpu_status (struct wf_mpu_config *devc) { return inb (STATPORT (devc->base)); } static void wf_mpu_cmd (struct wf_mpu_config *devc, unsigned char cmd) { outb ((cmd), COMDPORT(devc->base)); } #define input_avail(devc) (!(wf_mpu_status(devc)&INPUT_AVAIL)) #define output_ready(devc) (!(wf_mpu_status(devc)&OUTPUT_READY)) static int read_data (struct wf_mpu_config *devc) { return inb (DATAPORT (devc->base)); } static void write_data (struct wf_mpu_config *devc, unsigned char byte) { outb (byte, DATAPORT (devc->base)); } #define OUTPUT_READY 0x40 #define INPUT_AVAIL 0x80 #define MPU_ACK 0xFE #define MPU_RESET 0xFF #define UART_MODE_ON 0x3F static struct wf_mpu_config dev_conf[MAX_MIDI_DEV] = { {0} }; static volatile int irq2dev[17] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; 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[MAX_MIDI_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 (struct wf_mpu_config *devc, unsigned char midic) { struct midi_input_info *mi; mi = &midi_devs[devc->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 " ", 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 (devc->synthno, 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: } } 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 (devc->synthno, 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 (devc->synthno, 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; } void wf_mpuintr (int irq, void *dev_id, struct pt_regs *dummy) { struct wf_mpu_config *devc; int dev; static struct wf_mpu_config *isrc = 0; int n; struct midi_input_info *mi; static int cnt = 0; if (irq < 0 || irq > 15) { printk (KERN_ERR "WF-MPU: bogus interrupt #%d", irq); return; } dev = irq2dev[irq]; mi = &midi_devs[dev]->in_info; if (mi->m_busy) return; mi->m_busy = 1; sti (); n = 50; /* guarantee that we're working with the "real" (internal) interface before doing anything physical. */ devc = &dev_conf[dev]; devc = &dev_conf[devc->internal]; if (isrc == 0) { /* This is just an initial setting. If Virtual MIDI mode is enabled on the ICS2115, we'll get a switch char before anything else, and if it isn't, then the guess will be correct for our purposes. */ isrc = &dev_conf[devc->internal]; } while (input_avail (devc) && n-- > 0) { unsigned char c = read_data (devc); if (devc->isvirtual) { if (c == WF_EXTERNAL_SWITCH) { isrc = &dev_conf[devc->external]; continue; } else if (c == WF_INTERNAL_SWITCH) { isrc = &dev_conf[devc->internal]; continue; } /* else just leave it as it is */ } else { isrc = &dev_conf[devc->internal]; } if (isrc->mode == MODE_SYNTH) { wf_mpu_input_scanner (isrc, c); } else if (isrc->opened & OPEN_READ) { if (isrc->inputintr) { isrc->inputintr (isrc->devno, c); } } } mi->m_busy = 0; } 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; devc = &dev_conf[dev]; 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; devc = &dev_conf[dev]; 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; struct wf_mpu_config *devc; unsigned char switchch; /* The actual output has to occur using the "internal" config info */ devc = &dev_conf[dev_conf[dev].internal]; if (devc->isvirtual && lastoutdev != dev) { if (dev == devc->internal) { switchch = WF_INTERNAL_SWITCH; } else if (dev == devc->external) { switchch = WF_EXTERNAL_SWITCH; } else { printk (KERN_ERR "WF-MPU: bad device number %d", dev); return (0); } for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--); save_flags (flags); cli (); if (!output_ready (devc)) { printk (KERN_WARNING "WF-MPU: Send switch " "byte timeout\n"); restore_flags (flags); return 0; } write_data (devc, switchch); restore_flags (flags); } lastoutdev = dev; /* * Sometimes it takes about 30000 loops before the output becomes ready * (After reset). Normally it takes just about 10 loops. */ for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--); save_flags (flags); cli (); if (!output_ready (devc)) { printk (KERN_WARNING "WF-MPU: Send data timeout\n"); restore_flags (flags); return 0; } write_data (devc, midi_byte); restore_flags (flags); return 1; } static int wf_mpu_start_read (int dev) { return 0; } static int wf_mpu_end_read (int dev) { return 0; } static int wf_mpu_ioctl (int dev, unsigned cmd, caddr_t arg) { printk (KERN_WARNING "WF-MPU: Intelligent mode not supported by hardware.\n"); return -(EINVAL); } static void wf_mpu_kick (int dev) { } static int wf_mpu_buffer_status (int dev) { return 0; /* * No data in buffers */ } static int wf_mpu_synth_ioctl (int dev, unsigned int cmd, caddr_t arg) { int midi_dev; midi_dev = synth_devs[dev]->midi_dev; if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev]==NULL) return -(ENXIO); switch (cmd) { case SNDCTL_SYNTH_INFO: copy_to_user (&((char *) arg)[0], &wf_mpu_synth_info[midi_dev], sizeof (struct synth_info)); return 0; break; case SNDCTL_SYNTH_MEMAVL: return 0x7fffffff; break; 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); } devc = &dev_conf[midi_dev]; 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; devc = &dev_conf[midi_dev]; devc->inputintr = NULL; devc->opened = 0; devc->mode = 0; } #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 = { "WaveFront (ICS2115)", NULL, /* info field, filled in during configuration */ 0, /* MIDI dev XXX should this be -1 ? */ SYNTH_TYPE_MIDI, SAMPLE_TYPE_WAVEFRONT, wf_mpu_synth_open, wf_mpu_synth_close, wf_mpu_synth_ioctl, midi_synth_kill_note, midi_synth_start_note, midi_synth_set_instr, midi_synth_reset, midi_synth_hw_control, midi_synth_load_patch, midi_synth_aftertouch, midi_synth_controller, midi_synth_panning, NULL, midi_synth_bender, NULL, /* alloc */ midi_synth_setup_voice, midi_synth_send_sysex }; static struct synth_operations wf_mpu_synth_operations[WAVEFRONT_MAX_DEVICES*2]; static struct midi_operations wf_mpu_midi_operations[WAVEFRONT_MAX_DEVICES*2]; static int wfmpu_cnt = 0; static struct midi_operations wf_mpu_midi_proto = { {"WF-MPU MIDI", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, NULL, /*converter*/ {0}, /* in_info */ wf_mpu_open, wf_mpu_close, wf_mpu_ioctl, wf_mpu_out, wf_mpu_start_read, wf_mpu_end_read, wf_mpu_kick, NULL, wf_mpu_buffer_status, NULL }; static int config_wf_mpu (int dev, struct address_info *hw_config) { struct wf_mpu_config *devc; int internal; if (wfmpu_cnt >= WAVEFRONT_MAX_DEVICES * 2) { printk (KERN_ERR "WF-MPU: eh ? more MPU devices " "than cards ?!!\n"); return (-1); } /* There is no synth available on the external interface, so do the synth stuff to the internal interface only. */ internal = dev_conf[dev].internal; devc = &dev_conf[internal]; if (!dev_conf[dev].isexternal) { memcpy ((char *) &wf_mpu_synth_operations[wfmpu_cnt], (char *) &wf_mpu_synth_proto, sizeof (struct synth_operations)); } memcpy ((char *) &wf_mpu_midi_operations[wfmpu_cnt], (char *) &wf_mpu_midi_proto, sizeof (struct midi_operations)); if (dev_conf[dev].isexternal) { wf_mpu_midi_operations[wfmpu_cnt].converter = NULL; } else { wf_mpu_midi_operations[wfmpu_cnt].converter = &wf_mpu_synth_operations[wfmpu_cnt]; } memcpy ((char *) &wf_mpu_synth_info[dev], (char *) &wf_mpu_synth_info_proto, sizeof (struct synth_info)); strcpy (wf_mpu_synth_info[dev].name, hw_config->name); strcpy (wf_mpu_midi_operations[wfmpu_cnt].info.name, hw_config->name); conf_printf (hw_config->name, hw_config); if (!dev_conf[dev].isexternal) { wf_mpu_synth_operations[wfmpu_cnt].midi_dev = dev; } wf_mpu_synth_operations[wfmpu_cnt].info = &wf_mpu_synth_info[dev]; midi_devs[dev] = &wf_mpu_midi_operations[wfmpu_cnt]; dev_conf[dev].opened = 0; dev_conf[dev].mode = 0; dev_conf[dev].configured_for_virtual = 0; dev_conf[dev].devno = dev; midi_devs[dev]->in_info.m_busy = 0; midi_devs[dev]->in_info.m_state = MST_INIT; midi_devs[dev]->in_info.m_ptr = 0; midi_devs[dev]->in_info.m_left = 0; midi_devs[dev]->in_info.m_prev_status = 0; wfmpu_cnt++; return (0); } int virtual_midi_enable (int dev, struct address_info *hw_config) { int idev; int edev; struct wf_mpu_config *devc; devc = &dev_conf[dev]; if (devc->configured_for_virtual) { idev = devc->internal; edev = devc->external; } else { if (hw_config == NULL) { printk (KERN_ERR "WF-MPU: virtual midi first " "enabled without hw_config!\n"); return -EINVAL; } idev = devc->internal; if ((edev = sound_alloc_mididev()) == -1) { printk (KERN_ERR "WF-MPU: too many midi devices detected\n"); return -1; } hw_config->slots[WF_EXTERNAL_MIDI_SLOT] = edev; } dev_conf[edev].isvirtual = 1; dev_conf[idev].isvirtual = 1; if (dev_conf[idev].configured_for_virtual) { return 0; } /* Configure external interface struct */ devc = &dev_conf[edev]; devc->internal = idev; devc->external = edev; devc->isexternal = 1; /* Configure external interface struct (devc->isexternal and devc->internal set in attach_wf_mpu()) */ devc = &dev_conf[idev]; devc->external = edev; /* Configure the tables for the external */ if (config_wf_mpu (edev, hw_config)) { printk (KERN_WARNING "WF-MPU: configuration for MIDI " "device %d failed\n", edev); return (-1); } /* Don't bother to do this again if we are toggled back and forth between virtual MIDI mode and "normal" operation. */ dev_conf[edev].configured_for_virtual = 1; dev_conf[idev].configured_for_virtual = 1; return 0; } void virtual_midi_disable (int dev) { struct wf_mpu_config *devc; unsigned long flags; save_flags (flags); cli(); /* Assumes for logical purposes that the caller has taken care of fiddling with WaveFront hardware commands to turn off Virtual MIDI mode. */ devc = &dev_conf[dev]; devc = &dev_conf[devc->internal]; devc->isvirtual = 0; devc = &dev_conf[devc->external]; devc->isvirtual = 0; restore_flags (flags); } void attach_wf_mpu (struct address_info *hw_config) { int m; struct wf_mpu_config *devc; if (request_irq (hw_config->irq, wf_mpuintr, 0, "WaveFront MIDI", NULL) < 0) { printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n", hw_config->irq); return; } if ((m = sound_alloc_mididev()) == -1){ printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n"); free_irq (hw_config->irq, NULL); release_region (hw_config->io_base, 2); return; } request_region (hw_config->io_base, 2, "WaveFront MPU"); hw_config->slots[WF_INTERNAL_MIDI_SLOT] = m; devc = &dev_conf[m]; devc->base = hw_config->io_base; devc->irq = hw_config->irq; devc->isexternal = 0; devc->internal = m; devc->external = -1; devc->isvirtual = 0; irq2dev[devc->irq] = m; if (config_wf_mpu (m, hw_config)) { printk (KERN_WARNING "WF-MPU: configuration for MIDI device %d failed\n", m); sound_unload_mididev (m); } /* 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 (devc); } int probe_wf_mpu (struct address_info *hw_config) { if (hw_config->irq < 0 || hw_config->irq > 16) { printk (KERN_WARNING "WF-MPU: bogus IRQ value requested (%d)\n", hw_config->irq); return 0; } if (check_region (hw_config->io_base, 2)) { printk (KERN_WARNING "WF-MPU: I/O port %x already in use\n\n", hw_config->io_base); return 0; } if (inb (hw_config->io_base + 1) == 0xff) { /* Just bus float? */ printk ("WF-MPU: Port %x looks dead.\n", hw_config->io_base); return 0; } return 1; } void unload_wf_mpu (struct address_info *hw_config) { release_region (hw_config->io_base, 2); sound_unload_mididev (hw_config->slots[WF_INTERNAL_MIDI_SLOT]); if (hw_config->irq > 0) { free_irq (hw_config->irq, NULL); } if (hw_config->slots[WF_EXTERNAL_MIDI_SLOT] > 0) { sound_unload_mididev (hw_config->slots[WF_EXTERNAL_MIDI_SLOT]); } } static void start_uart_mode (struct wf_mpu_config *devc) { int ok, timeout; unsigned long flags; save_flags (flags); cli (); for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--); wf_mpu_cmd (devc, UART_MODE_ON); for (ok = 0, timeout = 50000; timeout > 0 && !ok; timeout--) { if (input_avail (devc)) { if (read_data (devc) == MPU_ACK) { ok = 1; } } } restore_flags (flags); } #endif