📄 mpu401.c
字号:
/* * sound/mpu401.c * * The low level driver for Roland MPU-401 compatible Midi cards. *//* * Copyright (C) by Hannu Savolainen 1993-1997 * * OSS/Free 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. * * * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) * Alan Cox modularisation, use normal request_irq, use dev_id * Bartlomiej Zolnierkiewicz removed some __init to allow using many drivers */#include <linux/module.h>#include <linux/init.h>#define USE_SEQ_MACROS#define USE_SIMPLE_MACROS#include "sound_config.h"#include "coproc.h"#include "mpu401.h"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; int *osp; };#define DATAPORT(base) (base)#define COMDPORT(base) (base+1)#define STATPORT(base) (base+1)static int mpu401_status(struct mpu_config *devc){ return inb(STATPORT(devc->base));}#define input_avail(devc) (!(mpu401_status(devc)&INPUT_AVAIL))#define output_ready(devc) (!(mpu401_status(devc)&OUTPUT_READY))static void write_command(struct mpu_config *devc, unsigned char cmd){ outb(cmd, COMDPORT(devc->base));}static int read_data(struct mpu_config *devc){ return inb(DATAPORT(devc->base));}static void write_data(struct 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 0x3Fstatic struct mpu_config dev_conf[MAX_MIDI_DEV] ={ {0}};static int n_mpu_devs = 0;static int reset_mpu401(struct mpu_config *devc);static void set_uart_mode(int dev, struct mpu_config *devc, int arg);static int 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, MIDI_TYPE_MPU401, 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 int mpu_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; 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 = ((int) (midic & 0xf0) >> 4); devc->m_state = ST_DATABYTE; if (msg < 8) /* Data byte */ { /* printk( "midi msg (running status) "); */ msg = ((int) (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 void mpu401_input_loop(struct mpu_config *devc){ unsigned long flags; int busy; int n; save_flags(flags); cli(); busy = devc->m_busy; devc->m_busy = 1; restore_flags(flags); if (busy) /* Already inside the scanner */ return; n = 50; while (input_avail(devc) && n-- > 0) { unsigned char c = read_data(devc); 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;}int intchk_mpu401(void *dev_id){ struct mpu_config *devc; int dev = (int) dev_id; devc = &dev_conf[dev]; return input_avail(devc);}void mpuintr(int irq, void *dev_id, struct pt_regs *dummy){ struct mpu_config *devc; int dev = (int) dev_id; sti(); devc = &dev_conf[dev]; if (input_avail(devc)) { 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); } }}static int mpu401_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 || midi_devs[dev] == NULL) return -ENXIO; devc = &dev_conf[dev]; if (devc->opened) return -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 downloading its microcode. */ if (!devc->initialized) { if (mpu401_status(devc) == 0xff) /* Bus float */ { printk(KERN_ERR "mpu401: Device not initialized properly\n"); return -EIO; } reset_mpu401(devc); } if (midi_devs[dev]->coproc) { if ((err = midi_devs[dev]->coproc-> open(midi_devs[dev]->coproc->devc, COPR_MIDI)) < 0) { 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 void mpu401_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; devc->inputintr = NULL; if (midi_devs[dev]->coproc) midi_devs[dev]->coproc->close(midi_devs[dev]->coproc->devc, COPR_MIDI); devc->opened = 0;}static int mpu401_out(int dev, unsigned char midi_byte){ int timeout; unsigned long flags; struct mpu_config *devc; devc = &dev_conf[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 "mpu401: Send data timeout\n"); restore_flags(flags); return 0; } write_data(devc, midi_byte); restore_flags(flags); return 1;}static int mpu401_command(int dev, mpu_command_rec * cmd){ int i, timeout, ok; int ret = 0; unsigned long flags; struct mpu_config *devc; devc = &dev_conf[dev]; if (devc->uart_mode) /* * Not possible in UART mode */ { printk(KERN_WARNING "mpu401: commands not possible in the UART mode\n"); return -EINVAL; } /* * Test for input since pending input seems to block the output. */ if (input_avail(devc)) mpu401_input_loop(devc); /* * Sometimes it takes about 50000 loops before the output becomes ready * (After reset). Normally it takes just about 10 loops. */ timeout = 50000;retry: if (timeout-- <= 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -