⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mpu401.c

📁 iis s3c2410-uda1341语音系统的 开发
💻 C
📖 第 1 页 / 共 3 页
字号:
	if (check_region(hw_config->io_base, 2))	{		printk(KERN_ERR "mpu401: I/O port %x already in use\n\n", hw_config->io_base);		return 0;	}	tmp_devc.base = hw_config->io_base;	tmp_devc.irq = hw_config->irq;	tmp_devc.initialized = 0;	tmp_devc.opened = 0;	tmp_devc.osp = hw_config->osp;	if (hw_config->always_detect)		return 1;	if (inb(hw_config->io_base + 1) == 0xff)	{		DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base));		return 0;	/* Just bus float? */	}	ok = reset_mpu401(&tmp_devc);	if (!ok)	{		DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base));	}	return ok;}void unload_mpu401(struct address_info *hw_config){	void *p;	int n=hw_config->slots[1];		release_region(hw_config->io_base, 2);	if (hw_config->always_detect == 0 && hw_config->irq > 0)		free_irq(hw_config->irq, (void *)n);	p=mpu401_synth_operations[n];	sound_unload_mididev(n);	sound_unload_timerdev(hw_config->slots[2]);	if(p)		kfree(p);}/***************************************************** *      Timer stuff ****************************************************/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 long clocks2ticks(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 its best and to convert between the HW and	 * actual timebases.	 */	return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase;}static void set_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 + 12) / 24;	if (hw_val > max_timebase)		hw_val = max_timebase;	if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0)	{		printk(KERN_WARNING "mpu401: Can't set HW timebase to %d\n", hw_val * 24);		return;	}	hw_timebase = hw_val * 24;	curr_timebase = val;}static void tmr_reset(void){	unsigned long flags;	save_flags(flags);	cli();	next_event_time = (unsigned long) -1;	prev_event_time = 0;	curr_ticks = curr_clocks = 0;	restore_flags(flags);}static void set_timer_mode(int midi_dev){	if (timer_mode & TMR_MODE_CLS)		mpu_cmd(midi_dev, 0x3c, 0);	/* Use CLS sync */	else if (timer_mode & TMR_MODE_SMPTE)		mpu_cmd(midi_dev, 0x3d, 0);	/* Use SMPTE sync */	if (timer_mode & TMR_INTERNAL)	{		  mpu_cmd(midi_dev, 0x80, 0);	/* Use MIDI sync */	}	else	{		if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS))		{			mpu_cmd(midi_dev, 0x82, 0);		/* Use MIDI sync */			mpu_cmd(midi_dev, 0x91, 0);		/* Enable ext MIDI ctrl */		}		else if (timer_mode & TMR_MODE_FSK)			mpu_cmd(midi_dev, 0x81, 0);	/* Use FSK sync */	}}static void stop_metronome(int midi_dev){	mpu_cmd(midi_dev, 0x84, 0);	/* Disable metronome */}static void setup_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)		mpu_cmd(midi_dev, 0x84, 0);	/* Disable metronome */	else	{		mpu_cmd(midi_dev, 0xE4, clks_per_click);		mpu_cmd(midi_dev, 0xE6, beats_per_measure);		mpu_cmd(midi_dev, 0x83, 0);	/* Enable metronome without accents */	}}static int mpu_start_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)	{		mpu_cmd(midi_dev, 0x02, 0);	/* Send MIDI start */		tmr_running = 1;		return TIMER_NOT_ARMED;	}	else	{		mpu_cmd(midi_dev, 0x35, 0);	/* Enable mode messages to PC */		mpu_cmd(midi_dev, 0x38, 0);	/* Enable sys common messages to PC */		mpu_cmd(midi_dev, 0x39, 0);	/* Enable real time messages to PC */		mpu_cmd(midi_dev, 0x97, 0);	/* Enable system exclusive messages to PC */	}	return TIMER_ARMED;}static int mpu_timer_open(int dev, int mode){	int midi_dev = sound_timer_devs[dev]->devlink;	if (timer_open)		return -EBUSY;	tmr_reset();	curr_tempo = 50;	mpu_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);	mpu_cmd(midi_dev, 0xe7, 0x04);	/* Send all clocks to host */	mpu_cmd(midi_dev, 0x95, 0);	/* Enable clock to host */	return 0;}static void mpu_timer_close(int dev){	int midi_dev = sound_timer_devs[dev]->devlink;	timer_open = tmr_running = 0;	mpu_cmd(midi_dev, 0x15, 0);	/* Stop all */	mpu_cmd(midi_dev, 0x94, 0);	/* Disable clock to host */	mpu_cmd(midi_dev, 0x8c, 0);	/* Disable measure end messages to host */	stop_metronome(midi_dev);}static int mpu_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 mpu_start_timer(midi_dev);		case TMR_STOP:			mpu_cmd(midi_dev, 0x01, 0);	/* Send MIDI stop */			stop_metronome(midi_dev);			tmr_running = 0;			break;		case TMR_CONTINUE:			if (tmr_running)				break;			mpu_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 (mpu_cmd(midi_dev, 0xE0, parm) < 0)					printk(KERN_WARNING "mpu401: 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 long mpu_timer_get_time(int dev){	if (!timer_open)		return 0;	return curr_ticks;}static int mpu_timer_ioctl(int dev, unsigned int command, caddr_t arg){	int midi_dev = sound_timer_devs[dev]->devlink;	switch (command)	{		case SNDCTL_TMR_SOURCE:			{				int parm;					parm = *(int *) arg;				parm &= timer_caps;				if (parm != 0)				{					timer_mode = parm;						if (timer_mode & TMR_MODE_CLS)						mpu_cmd(midi_dev, 0x3c, 0);		/* Use CLS sync */					else if (timer_mode & TMR_MODE_SMPTE)						mpu_cmd(midi_dev, 0x3d, 0);		/* Use SMPTE sync */				}				return (*(int *) arg = timer_mode);			}			break;		case SNDCTL_TMR_START:			mpu_start_timer(midi_dev);			return 0;		case SNDCTL_TMR_STOP:			tmr_running = 0;			mpu_cmd(midi_dev, 0x01, 0);	/* Send MIDI stop */			stop_metronome(midi_dev);			return 0;		case SNDCTL_TMR_CONTINUE:			if (tmr_running)				return 0;			tmr_running = 1;			mpu_cmd(midi_dev, 0x03, 0);	/* Send MIDI continue */			return 0;		case SNDCTL_TMR_TIMEBASE:			{				int val;				val = *(int *) arg;				if (val)					set_timebase(midi_dev, val);				return (*(int *) arg = curr_timebase);			}			break;		case SNDCTL_TMR_TEMPO:			{				int val;				int ret;				val = *(int *) arg;				if (val)				{					if (val < 8)						val = 8;					if (val > 250)						val = 250;					if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0)					{						printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) val);						return ret;					}					curr_tempo = val;				}				return (*(int *) arg = curr_tempo);			}			break;		case SNDCTL_SEQ_CTRLRATE:			{				int val;				val = *(int *) arg;				if (val != 0)		/* Can't change */					return -EINVAL;				return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);			}			break;		case SNDCTL_SEQ_GETTIME:			return (*(int *) arg = curr_ticks);		case SNDCTL_TMR_METRONOME:			metronome_mode = *(int *) arg;			setup_metronome(midi_dev);			return 0;		default:;	}	return -EINVAL;}static void mpu_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 ={	owner:		THIS_MODULE,	info:		{"MPU-401 Timer", 0},	priority:	10,	/* Priority */	devlink:	0,	/* Local device link */	open:		mpu_timer_open,	close:		mpu_timer_close,	event:		mpu_timer_event,	get_time:	mpu_timer_get_time,	ioctl:		mpu_timer_ioctl,	arm_timer:	mpu_timer_arm};static void mpu_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 = (unsigned long) -1;		sequencer_timer(0);	}}static void timer_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 int mpu_timer_init(int midi_dev){	struct mpu_config *devc;	int n;	devc = &dev_conf[midi_dev];	if (timer_initialized)		return -1;	/* There is already a similar timer */	timer_initialized = 1;	mpu_timer.devlink = midi_dev;	dev_conf[midi_dev].timer_flag = 1;	n = sound_alloc_timerdev();	if (n == -1)		n = 0;	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 (devc->revision & 0x40)			max_timebase = 10;	/* Has the 216 and 240 ppqn modes */	}	timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps;	return n;}EXPORT_SYMBOL(probe_mpu401);EXPORT_SYMBOL(attach_mpu401);EXPORT_SYMBOL(unload_mpu401);EXPORT_SYMBOL(intchk_mpu401);EXPORT_SYMBOL(mpuintr);static struct address_info cfg;static int __initdata io = -1;static int __initdata irq = -1;MODULE_PARM(irq, "i");MODULE_PARM(io, "i");int __init init_mpu401(void){	/* Can be loaded either for module use or to provide functions	   to others */	if (io != -1 && irq != -1) {	        cfg.irq = irq;		cfg.io_base = io;		if (probe_mpu401(&cfg) == 0)			return -ENODEV;		attach_mpu401(&cfg, THIS_MODULE);	}		return 0;}void __exit cleanup_mpu401(void){	if (io != -1 && irq != -1) {		/* Check for use by, for example, sscape driver */		unload_mpu401(&cfg);	}}module_init(init_mpu401);module_exit(cleanup_mpu401);#ifndef MODULEstatic int __init setup_mpu401(char *str){        /* io, irq */	int ints[3];		str = get_options(str, ARRAY_SIZE(ints), ints);		io = ints[1];	irq = ints[2];	return 1;}__setup("mpu401=", setup_mpu401);#endifMODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -