📄 mpu401.c
字号:
/* * sound/mpu401.c * * The low level driver for Roland MPU-401 compatible Midi cards. * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Modified: * Riccardo Facchetti 24 Mar 1995 * - Added the Audio Excel DSP 16 initialization routine. */#define USE_SEQ_MACROS#define USE_SIMPLE_MACROS#include <i386/isa/sound/sound_config.h>#ifdef CONFIGURE_SOUNDCARD#if (!defined(EXCLUDE_MPU401) || !defined(EXCLUDE_MPU_EMU)) && !defined(EXCLUDE_MIDI)#include <i386/isa/sound/coproc.h>static int init_sequence[20]; /* NOTE! pos 0 = len, start pos 1. */static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL;struct mpu_config { int base; /* * I/O base */ int irq; int opened; /* * Open mode */ int devno; int synthno; int uart_mode; int initialized; int mode;#define MODE_MIDI 1#define MODE_SYNTH 2 unsigned char version, revision; unsigned int capabilities;#define MPU_CAP_INTLG 0x10000000#define MPU_CAP_SYNC 0x00000010#define MPU_CAP_FSK 0x00000020#define MPU_CAP_CLS 0x00000040#define MPU_CAP_SMPTE 0x00000080#define MPU_CAP_2PORT 0x00000001 int timer_flag;#define MBUF_MAX 10#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \ {printk("MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;} int m_busy; unsigned char m_buf[MBUF_MAX]; int m_ptr; int m_state; int m_left; unsigned char last_status; void (*inputintr) (int dev, unsigned char data); int shared_irq; };#define DATAPORT(base) (base)#define COMDPORT(base) (base+1)#define STATPORT(base) (base+1)#define mpu401_status(base) INB(STATPORT(base))#define input_avail(base) (!(mpu401_status(base)&INPUT_AVAIL))#define output_ready(base) (!(mpu401_status(base)&OUTPUT_READY))#define write_command(base, cmd) OUTB(cmd, COMDPORT(base))#define read_data(base) INB(DATAPORT(base))#define write_data(base, byte) OUTB(byte, DATAPORT(base))#define OUTPUT_READY 0x40#define INPUT_AVAIL 0x80#define MPU_ACK 0xF7#define MPU_RESET 0xFF#define UART_MODE_ON 0x3Fstatic struct mpu_config dev_conf[MAX_MIDI_DEV] ={ {0}};static int n_mpu_devs = 0;static int irq2dev[16] ={-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};static int reset_mpu401 (struct mpu_config *devc);static void set_uart_mode (int dev, struct mpu_config *devc, int arg);static void mpu_timer_init (int midi_dev);static void mpu_timer_interrupt (void);static void timer_ext_event (struct mpu_config *devc, int event, int parm);static struct synth_info mpu_synth_info_proto ={"MPU-401 MIDI interface", 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, SYNTH_CAP_INPUT};static struct synth_info mpu_synth_info[MAX_MIDI_DEV];/* * States for the input scanner */#define ST_INIT 0 /* Ready for timing byte or msg */#define ST_TIMED 1 /* Leading timing byte rcvd */#define ST_DATABYTE 2 /* Waiting for (nr_left) data bytes */#define ST_SYSMSG 100 /* System message (sysx etc). */#define ST_SYSEX 101 /* System exclusive msg */#define ST_MTC 102 /* Midi Time Code (MTC) qframe msg */#define ST_SONGSEL 103 /* Song select */#define ST_SONGPOS 104 /* Song position pointer */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 */};#define STORE(cmd) \{ \ int len; \ unsigned char obuf[8]; \ cmd; \ seq_input_event(obuf, len); \}#define _seqbuf obuf#define _seqbufptr 0#define _SEQ_ADVBUF(x) len=xstatic intmpu_input_scanner (struct mpu_config *devc, unsigned char midic){ switch (devc->m_state) { case ST_INIT: switch (midic) { case 0xf8: /* Timer overflow */ break; case 0xfc: printk ("<all end>"); break; case 0xfd: if (devc->timer_flag) mpu_timer_interrupt (); break; case 0xfe: return MPU_ACK; break; case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7: printk ("<Trk data rq #%d>", midic & 0x0f); break; case 0xf9: printk ("<conductor rq>"); break; case 0xff: devc->m_state = ST_SYSMSG; break; default: if (midic <= 0xef) { /* printk("mpu time: %d ", midic); */ devc->m_state = ST_TIMED; } else printk ("<MPU: Unknown event %02x> ", midic); } break; case ST_TIMED: { int msg = (midic & 0xf0) >> 4; devc->m_state = ST_DATABYTE; if (msg < 8) /* Data byte */ { /* printk("midi msg (running status) "); */ msg = (devc->last_status & 0xf0) >> 4; msg -= 8; devc->m_left = len_tab[msg] - 1; devc->m_ptr = 2; devc->m_buf[0] = devc->last_status; devc->m_buf[1] = midic; if (devc->m_left <= 0) { devc->m_state = ST_INIT; do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr); devc->m_ptr = 0; } } else if (msg == 0xf) /* MPU MARK */ { devc->m_state = ST_INIT; switch (midic) { case 0xf8: /* printk("NOP "); */ break; case 0xf9: /* printk("meas end "); */ break; case 0xfc: /* printk("data end "); */ break; default: printk ("Unknown MPU mark %02x\n", midic); } } else { devc->last_status = midic; /* printk ("midi msg "); */ msg -= 8; devc->m_left = len_tab[msg]; devc->m_ptr = 1; devc->m_buf[0] = midic; if (devc->m_left <= 0) { devc->m_state = ST_INIT; do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr); devc->m_ptr = 0; } } } break; case ST_SYSMSG: switch (midic) { case 0xf0: printk ("<SYX>"); devc->m_state = ST_SYSEX; break; case 0xf1: devc->m_state = ST_MTC; break; case 0xf2: devc->m_state = ST_SONGPOS; devc->m_ptr = 0; break; case 0xf3: devc->m_state = ST_SONGSEL; break; case 0xf6: /* printk("tune_request\n"); */ devc->m_state = ST_INIT; /* * Real time messages */ case 0xf8: /* midi clock */ devc->m_state = ST_INIT; timer_ext_event (devc, TMR_CLOCK, 0); break; case 0xfA: devc->m_state = ST_INIT; timer_ext_event (devc, TMR_START, 0); break; case 0xFB: devc->m_state = ST_INIT; timer_ext_event (devc, TMR_CONTINUE, 0); break; case 0xFC: devc->m_state = ST_INIT; timer_ext_event (devc, TMR_STOP, 0); break; case 0xFE: /* active sensing */ devc->m_state = ST_INIT; break; case 0xff: /* printk("midi hard reset"); */ devc->m_state = ST_INIT; break; default: printk ("unknown MIDI sysmsg %0x\n", midic); devc->m_state = ST_INIT; } break; case ST_MTC: devc->m_state = ST_INIT; printk ("MTC frame %x02\n", midic); break; case ST_SYSEX: if (midic == 0xf7) { printk ("<EOX>"); devc->m_state = ST_INIT; } else printk ("%02x ", midic); break; case ST_SONGPOS: BUFTEST (devc); devc->m_buf[devc->m_ptr++] = midic; if (devc->m_ptr == 2) { devc->m_state = ST_INIT; devc->m_ptr = 0; timer_ext_event (devc, TMR_SPP, ((devc->m_buf[1] & 0x7f) << 7) | (devc->m_buf[0] & 0x7f)); } break; case ST_DATABYTE: BUFTEST (devc); devc->m_buf[devc->m_ptr++] = midic; if ((--devc->m_left) <= 0) { devc->m_state = ST_INIT; do_midi_msg (devc->synthno, devc->m_buf, devc->m_ptr); devc->m_ptr = 0; } break; default: printk ("Bad state %d ", devc->m_state); devc->m_state = ST_INIT; } return 1;}static voidmpu401_input_loop (struct mpu_config *devc){ unsigned long flags; int busy; int n; DISABLE_INTR (flags); busy = devc->m_busy; devc->m_busy = 1; RESTORE_INTR (flags); if (busy) /* Already inside the scanner */ return; n = 50; while (input_avail (devc->base) && n-- > 0) { unsigned char c = read_data (devc->base); if (devc->mode == MODE_SYNTH) { mpu_input_scanner (devc, c); } else if (devc->opened & OPEN_READ && devc->inputintr != NULL) devc->inputintr (devc->devno, c); } devc->m_busy = 0;}voidmpuintr (INT_HANDLER_PARMS (irq, dummy)){ struct mpu_config *devc; int dev;#ifdef linux sti ();#endif if (irq < 1 || irq > 15) { printk ("MPU-401: Interrupt #%d?\n", irq); return; } dev = irq2dev[irq]; if (dev == -1) { /* printk ("MPU-401: Interrupt #%d?\n", irq); */ return; } devc = &dev_conf[dev]; if (input_avail (devc->base)) if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH)) mpu401_input_loop (devc); else { /* Dummy read (just to acknowledge the interrupt) */ read_data (devc->base); }}static intmpu401_open (int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev)){ int err; struct mpu_config *devc; if (dev < 0 || dev >= num_midis) return RET_ERROR (ENXIO); devc = &dev_conf[dev]; if (devc->opened) { printk ("MPU-401: Midi busy\n"); return RET_ERROR (EBUSY); } /* * Verify that the device is really running. * Some devices (such as Ensoniq SoundScape don't * work before the on board processor (OBP) is initialized * by downloadin it's microcode. */ if (!devc->initialized) { if (mpu401_status (devc->base) == 0xff) /* Bus float */ { printk ("MPU-401: Device not initialized properly\n"); return RET_ERROR (EIO); } reset_mpu401 (devc); } irq2dev[devc->irq] = dev; if (devc->shared_irq == 0) if ((err = snd_set_irq_handler (devc->irq, mpuintr, midi_devs[dev]->info.name) < 0)) { return err; } if (midi_devs[dev]->coproc) if ((err = midi_devs[dev]->coproc-> open (midi_devs[dev]->coproc->devc, COPR_MIDI)) < 0) { if (devc->shared_irq == 0) snd_release_irq (devc->irq); printk ("MPU-401: Can't access coprocessor device\n"); return err; } set_uart_mode (dev, devc, 1); devc->mode = MODE_MIDI; devc->synthno = 0; mpu401_input_loop (devc); devc->inputintr = input; devc->opened = mode; return 0;}static voidmpu401_close (int dev){ struct mpu_config *devc; devc = &dev_conf[dev]; if (devc->uart_mode) reset_mpu401 (devc); /* * This disables the UART mode */ devc->mode = 0; if (devc->shared_irq == 0) snd_release_irq (devc->irq); devc->inputintr = NULL; if (midi_devs[dev]->coproc) midi_devs[dev]->coproc->close (midi_devs[dev]->coproc->devc, COPR_MIDI); devc->opened = 0;}static intmpu401_out (int dev, unsigned char midi_byte){ int timeout; unsigned long flags; struct mpu_config *devc; devc = &dev_conf[dev];#if 0 /* * Test for input since pending input seems to block the output. */ if (input_avail (devc->base)) { mpu401_input_loop (devc); }#endif /* * Sometimes it takes about 13000 loops before the output becomes ready * (After reset). Normally it takes just about 10 loops. */ for (timeout = 3000; timeout > 0 && !output_ready (devc->base); timeout--); DISABLE_INTR (flags); if (!output_ready (devc->base)) { printk ("MPU-401: Send data timeout\n"); RESTORE_INTR (flags); return 0; } write_data (devc->base, midi_byte); RESTORE_INTR (flags); return 1;}static intmpu401_command (int dev, mpu_command_rec * cmd)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -