📄 mpu401.c
字号:
devc->uart_mode = 0; return ok;}static voidset_uart_mode (int dev, struct mpu_config *devc, int arg){ if (!arg && devc->version == 0) { return; } if ((devc->uart_mode == 0) == (arg == 0)) { return; /* Already set */ } reset_mpu401 (devc); /* This exits the uart mode */ if (arg) { if (exec_cmd (dev, UART_MODE_ON, 0) < 0) { printk ("MPU%d: Can't enter UART mode\n", devc->devno); devc->uart_mode = 0; return; } } devc->uart_mode = arg;}intprobe_mpu401 (struct address_info *hw_config){ int ok = 0; struct mpu_config tmp_devc; tmp_devc.base = hw_config->io_base; tmp_devc.irq = hw_config->irq; tmp_devc.initialized = 0;#if !defined(EXCLUDE_AEDSP16) && defined(AEDSP16_MPU401) /* * Initialize Audio Excel DSP 16 to MPU-401, before any operation. */ InitAEDSP16_MPU401 (hw_config);#endif if (hw_config->always_detect) return 1; if (INB (hw_config->io_base + 1) == 0xff) return 0; /* Just bus float? */ ok = reset_mpu401 (&tmp_devc); return ok;}/***************************************************** * Timer stuff ****************************************************/#if !defined(EXCLUDE_SEQUENCER)static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0;static volatile int curr_tempo, curr_timebase, hw_timebase;static int max_timebase = 8; /* 8*24=192 ppqn */static volatile unsigned long next_event_time;static volatile unsigned long curr_ticks, curr_clocks;static unsigned long prev_event_time;static int metronome_mode;static unsigned longclocks2ticks (unsigned long clocks){ /* * The MPU-401 supports just a limited set of possible timebase values. * Since the applications require more choices, the driver has to * program the HW to do it's best and to convert between the HW and * actual timebases. */ return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase;}static voidset_timebase (int midi_dev, int val){ int hw_val; if (val < 48) val = 48; if (val > 1000) val = 1000; hw_val = val; hw_val = (hw_val + 23) / 24; if (hw_val > max_timebase) hw_val = max_timebase; if (exec_cmd (midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0) { printk ("MPU: Can't set HW timebase to %d\n", hw_val * 24); return; } hw_timebase = hw_val * 24; curr_timebase = val;}static voidtmr_reset (void){ unsigned long flags; DISABLE_INTR (flags); next_event_time = 0xffffffff; prev_event_time = 0; curr_ticks = curr_clocks = 0; RESTORE_INTR (flags);}static voidset_timer_mode (int midi_dev){ if (timer_mode & TMR_MODE_CLS) exec_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */ else if (timer_mode & TMR_MODE_SMPTE) exec_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */ if (timer_mode & TMR_INTERNAL) { exec_cmd (midi_dev, 0x80, 0); /* Use MIDI sync */ } else { if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS)) { exec_cmd (midi_dev, 0x82, 0); /* Use MIDI sync */ exec_cmd (midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */ } else if (timer_mode & TMR_MODE_FSK) exec_cmd (midi_dev, 0x81, 0); /* Use FSK sync */ }}static voidstop_metronome (int midi_dev){ exec_cmd (midi_dev, 0x84, 0); /* Disable metronome */}static voidsetup_metronome (int midi_dev){ int numerator, denominator; int clks_per_click, num_32nds_per_beat; int beats_per_measure; numerator = ((unsigned) metronome_mode >> 24) & 0xff; denominator = ((unsigned) metronome_mode >> 16) & 0xff; clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff; num_32nds_per_beat = (unsigned) metronome_mode & 0xff; beats_per_measure = (numerator * 4) >> denominator; if (!metronome_mode) exec_cmd (midi_dev, 0x84, 0); /* Disable metronome */ else { exec_cmd (midi_dev, 0xE4, clks_per_click); exec_cmd (midi_dev, 0xE6, beats_per_measure); exec_cmd (midi_dev, 0x83, 0); /* Enable metronome without accents */ }}static intstart_timer (int midi_dev){ tmr_reset (); set_timer_mode (midi_dev); if (tmr_running) return TIMER_NOT_ARMED; /* Already running */ if (timer_mode & TMR_INTERNAL) { exec_cmd (midi_dev, 0x02, 0); /* Send MIDI start */ tmr_running = 1; return TIMER_NOT_ARMED; } else { exec_cmd (midi_dev, 0x35, 0); /* Enable mode messages to PC */ exec_cmd (midi_dev, 0x38, 0); /* Enable sys common messages to PC */ exec_cmd (midi_dev, 0x39, 0); /* Enable real time messages to PC */ exec_cmd (midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */ } return TIMER_ARMED;}static intmpu_timer_open (int dev, int mode){ int midi_dev = sound_timer_devs[dev]->devlink; if (timer_open) return RET_ERROR (EBUSY); tmr_reset (); curr_tempo = 50; exec_cmd (midi_dev, 0xE0, 50); curr_timebase = hw_timebase = 120; set_timebase (midi_dev, 120); timer_open = 1; metronome_mode = 0; set_timer_mode (midi_dev); exec_cmd (midi_dev, 0xe7, 0x04); /* Send all clocks to host */ exec_cmd (midi_dev, 0x95, 0); /* Enable clock to host */ return 0;}static voidmpu_timer_close (int dev){ int midi_dev = sound_timer_devs[dev]->devlink; timer_open = tmr_running = 0; exec_cmd (midi_dev, 0x15, 0); /* Stop all */ exec_cmd (midi_dev, 0x94, 0); /* Disable clock to host */ exec_cmd (midi_dev, 0x8c, 0); /* Disable measure end messages to host */ stop_metronome (midi_dev);}static intmpu_timer_event (int dev, unsigned char *event){ unsigned char command = event[1]; unsigned long parm = *(unsigned int *) &event[4]; int midi_dev = sound_timer_devs[dev]->devlink; switch (command) { case TMR_WAIT_REL: parm += prev_event_time; case TMR_WAIT_ABS: if (parm > 0) { long time; if (parm <= curr_ticks) /* It's the time */ return TIMER_NOT_ARMED; time = parm; next_event_time = prev_event_time = time; return TIMER_ARMED; } break; case TMR_START: if (tmr_running) break; return start_timer (midi_dev); break; case TMR_STOP: exec_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */ stop_metronome (midi_dev); tmr_running = 0; break; case TMR_CONTINUE: if (tmr_running) break; exec_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */ setup_metronome (midi_dev); tmr_running = 1; break; case TMR_TEMPO: if (parm) { if (parm < 8) parm = 8; if (parm > 250) parm = 250; if (exec_cmd (midi_dev, 0xE0, parm) < 0) printk ("MPU: Can't set tempo to %d\n", (int) parm); curr_tempo = parm; } break; case TMR_ECHO: seq_copy_to_input (event, 8); break; case TMR_TIMESIG: if (metronome_mode) /* Metronome enabled */ { metronome_mode = parm; setup_metronome (midi_dev); } break; default:; } return TIMER_NOT_ARMED;}static unsigned longmpu_timer_get_time (int dev){ if (!timer_open) return 0; return curr_ticks;}static intmpu_timer_ioctl (int dev, unsigned int command, unsigned int arg){ int midi_dev = sound_timer_devs[dev]->devlink; switch (command) { case SNDCTL_TMR_SOURCE: { int parm = IOCTL_IN (arg) & timer_caps; if (parm != 0) { timer_mode = parm; if (timer_mode & TMR_MODE_CLS) exec_cmd (midi_dev, 0x3c, 0); /* Use CLS sync */ else if (timer_mode & TMR_MODE_SMPTE) exec_cmd (midi_dev, 0x3d, 0); /* Use SMPTE sync */ } return IOCTL_OUT (arg, timer_mode); } break; case SNDCTL_TMR_START: start_timer (midi_dev); return 0; break; case SNDCTL_TMR_STOP: tmr_running = 0; exec_cmd (midi_dev, 0x01, 0); /* Send MIDI stop */ stop_metronome (midi_dev); return 0; break; case SNDCTL_TMR_CONTINUE: if (tmr_running) return 0; tmr_running = 1; exec_cmd (midi_dev, 0x03, 0); /* Send MIDI continue */ return 0; break; case SNDCTL_TMR_TIMEBASE: { int val = IOCTL_IN (arg); if (val) set_timebase (midi_dev, val); return IOCTL_OUT (arg, curr_timebase); } break; case SNDCTL_TMR_TEMPO: { int val = IOCTL_IN (arg); int ret; if (val) { if (val < 8) val = 8; if (val > 250) val = 250; if ((ret = exec_cmd (midi_dev, 0xE0, val)) < 0) { printk ("MPU: Can't set tempo to %d\n", (int) val); return ret; } curr_tempo = val; } return IOCTL_OUT (arg, curr_tempo); } break; case SNDCTL_SEQ_CTRLRATE: if (IOCTL_IN (arg) != 0) /* Can't change */ return RET_ERROR (EINVAL); return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60); break; case SNDCTL_TMR_METRONOME: metronome_mode = IOCTL_IN (arg); setup_metronome (midi_dev); return 0; break; default: } return RET_ERROR (EINVAL);}static voidmpu_timer_arm (int dev, long time){ if (time < 0) time = curr_ticks + 1; else if (time <= curr_ticks) /* It's the time */ return; next_event_time = prev_event_time = time; return;}static struct sound_timer_operations mpu_timer ={ {"MPU-401 Timer", 0}, 10, /* Priority */ 0, /* Local device link */ mpu_timer_open, mpu_timer_close, mpu_timer_event, mpu_timer_get_time, mpu_timer_ioctl, mpu_timer_arm};static voidmpu_timer_interrupt (void){ if (!timer_open) return; if (!tmr_running) return; curr_clocks++; curr_ticks = clocks2ticks (curr_clocks); if (curr_ticks >= next_event_time) { next_event_time = 0xffffffff; sequencer_timer (); }}static voidtimer_ext_event (struct mpu_config *devc, int event, int parm){ int midi_dev = devc->devno; if (!devc->timer_flag) return; switch (event) { case TMR_CLOCK: printk ("<MIDI clk>"); break; case TMR_START: printk ("Ext MIDI start\n"); if (!tmr_running) if (timer_mode & TMR_EXTERNAL) { tmr_running = 1; setup_metronome (midi_dev); next_event_time = 0; STORE (SEQ_START_TIMER ()); } break; case TMR_STOP: printk ("Ext MIDI stop\n"); if (timer_mode & TMR_EXTERNAL) { tmr_running = 0; stop_metronome (midi_dev); STORE (SEQ_STOP_TIMER ()); } break; case TMR_CONTINUE: printk ("Ext MIDI continue\n"); if (timer_mode & TMR_EXTERNAL) { tmr_running = 1; setup_metronome (midi_dev); STORE (SEQ_CONTINUE_TIMER ()); } break; case TMR_SPP: printk ("Songpos: %d\n", parm); if (timer_mode & TMR_EXTERNAL) { STORE (SEQ_SONGPOS (parm)); } break; }}static voidmpu_timer_init (int midi_dev){ struct mpu_config *devc; int n; devc = &dev_conf[midi_dev]; if (timer_initialized) return; /* There is already a similar timer */ timer_initialized = 1; mpu_timer.devlink = midi_dev; dev_conf[midi_dev].timer_flag = 1;#if 1 if (num_sound_timers >= MAX_TIMER_DEV) n = 0; /* Overwrite the system timer */ else n = num_sound_timers++;#else n = 0;#endif sound_timer_devs[n] = &mpu_timer; if (devc->version < 0x20) /* Original MPU-401 */ timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI; else { /* * The version number 2.0 is used (at least) by the * MusicQuest cards and the Roland Super-MPU. * * MusicQuest has given a special meaning to the bits of the * revision number. The Super-MPU returns 0. */ if (devc->revision) timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI; if (devc->revision & 0x02) timer_caps |= TMR_MODE_CLS;#if 0 if (devc->revision & 0x04) timer_caps |= TMR_MODE_SMPTE;#endif if (devc->revision & 0x40) max_timebase = 10; /* Has the 216 and 240 ppqn modes */ } timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps;}#endif#endif#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -