aboutsummaryrefslogblamecommitdiffstats
path: root/fs/binfmt_aout.c
blob: 813a887cd2b3257d4b4b039091d0194c3a232721 (plain) (tree)


































                                                                                
















































































































































































































































                                                                                                  
                                                                                                               


                                






                                                                              



































                                                                      
                                          
 

































































                                                                                             
                                                                       














































































                                                                                         
                                             












                                                                                  






                                                                              















                                                                                            
                                                                 




















































                                                                                       
/*
 *  linux/fs/binfmt_aout.c
 *
 *  Copyright (C) 1991, 1992, 1996  Linus Torvalds
 */

#include <linux/module.h>

#include <linux/time.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/a.out.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/slab.h>
#include <linux/binfmts.h>
#include <linux/personality.h>
#include <linux/init.h>

#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/cacheflush.h>

static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
static int load_aout_library(struct file*);
static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file);

static struct linux_binfmt aout_format = {
	.module		= THIS_MODULE,
	.load_binary	= load_aout_binary,
	.load_shlib	= load_aout_library,
	.core_dump	= aout_core_dump,
	.min_coredump	= PAGE_SIZE
};

#define BAD_ADDR(x)	((unsigned long)(x) >= TASK_SIZE)

static int set_brk(unsigned long start, unsigned long end)
{
	start = PAGE_ALIGN(start);
	end = PAGE_ALIGN(end);
	if (end > start) {
		unsigned long addr;
		down_write(&current->mm->mmap_sem);
		addr = do_brk(start, end - start);
		up_write(&current->mm->mmap_sem);
		if (BAD_ADDR(addr))
			return addr;
	}
	return 0;
}

/*
 * These are the only things you should do on a core-file: use only these
 * macros to write out all the necessary info.
 */

static int dump_write(struct file *file, const void *addr, int nr)
{
	return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
}

#define DUMP_WRITE(addr, nr)	\
	if (!dump_write(file, (void *)(addr), (nr))) \
		goto end_coredump;

#define DUMP_SEEK(offset) \
if (file->f_op->llseek) { \
	if (file->f_op->llseek(file,(offset),0) != (offset)) \
 		goto end_coredump; \
} else file->f_pos = (offset)

/*
 * Routine writes a core dump image in the current directory.
 * Currently only a stub-function.
 *
 * Note that setuid/setgid files won't make a core-dump if the uid/gid
 * changed due to the set[u|g]id. It's enforced by the "current->mm->dumpable"
 * field, which also makes sure the core-dumps won't be recursive if the
 * dumping of the process results in another error..
 */

static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file)
{
	mm_segment_t fs;
	int has_dumped = 0;
	unsigned long dump_start, dump_size;
	struct user dump;
#if defined(__alpha__)
#       define START_DATA(u)	(u.start_data)
#elif defined(__arm__)
#	define START_DATA(u)	((u.u_tsize << PAGE_SHIFT) + u.start_code)
#elif defined(__sparc__)
#       define START_DATA(u)    (u.u_tsize)
#elif defined(__i386__) || defined(__mc68000__) || defined(__arch_um__)
#       define START_DATA(u)	(u.u_tsize << PAGE_SHIFT)
#endif
#ifdef __sparc__
#       define START_STACK(u)   ((regs->u_regs[UREG_FP]) & ~(PAGE_SIZE - 1))
#else
#       define START_STACK(u)   (u.start_stack)
#endif

	fs = get_fs();
	set_fs(KERNEL_DS);
	has_dumped = 1;
	current->flags |= PF_DUMPCORE;
       	strncpy(dump.u_comm, current->comm, sizeof(dump.u_comm));
#ifndef __sparc__
	dump.u_ar0 = (void *)(((unsigned long)(&dump.regs)) - ((unsigned long)(&dump)));
#endif
	dump.signal = signr;
	dump_thread(regs, &dump);

/* If the size of the dump file exceeds the rlimit, then see what would happen
   if we wrote the stack, but not the data area.  */
#ifdef __sparc__
	if ((dump.u_dsize+dump.u_ssize) >
	    current->signal->rlim[RLIMIT_CORE].rlim_cur)
		dump.u_dsize = 0;
#else
	if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE >
	    current->signal->rlim[RLIMIT_CORE].rlim_cur)
		dump.u_dsize = 0;
#endif

/* Make sure we have enough room to write the stack and data areas. */
#ifdef __sparc__
	if ((dump.u_ssize) >
	    current->signal->rlim[RLIMIT_CORE].rlim_cur)
		dump.u_ssize = 0;
#else
	if ((dump.u_ssize+1) * PAGE_SIZE >
	    current->signal->rlim[RLIMIT_CORE].rlim_cur)
		dump.u_ssize = 0;
#endif

/* make sure we actually have a data and stack area to dump */
	set_fs(USER_DS);
#ifdef __sparc__
	if (!access_ok(VERIFY_READ, (void __user *)START_DATA(dump), dump.u_dsize))
		dump.u_dsize = 0;
	if (!access_ok(VERIFY_READ, (void __user *)START_STACK(dump), dump.u_ssize))
		dump.u_ssize = 0;
#else
	if (!access_ok(VERIFY_READ, (void __user *)START_DATA(dump), dump.u_dsize << PAGE_SHIFT))
		dump.u_dsize = 0;
	if (!access_ok(VERIFY_READ, (void __user *)START_STACK(dump), dump.u_ssize << PAGE_SHIFT))
		dump.u_ssize = 0;
#endif

	set_fs(KERNEL_DS);
/* struct user */
	DUMP_WRITE(&dump,sizeof(dump));
/* Now dump all of the user data.  Include malloced stuff as well */
#ifndef __sparc__
	DUMP_SEEK(PAGE_SIZE);
#endif
/* now we start writing out the user space info */
	set_fs(USER_DS);
/* Dump the data area */
	if (dump.u_dsize != 0) {
		dump_start = START_DATA(dump);
#ifdef __sparc__
		dump_size = dump.u_dsize;
#else
		dump_size = dump.u_dsize << PAGE_SHIFT;
#endif
		DUMP_WRITE(dump_start,dump_size);
	}
/* Now prepare to dump the stack area */
	if (dump.u_ssize != 0) {
		dump_start = START_STACK(dump);
#ifdef __sparc__
		dump_size = dump.u_ssize;
#else
		dump_size = dump.u_ssize << PAGE_SHIFT;
#endif
		DUMP_WRITE(dump_start,dump_size);
	}
/* Finally dump the task struct.  Not be used by gdb, but could be useful */
	set_fs(KERNEL_DS);
	DUMP_WRITE(current,sizeof(*current));
end_coredump:
	set_fs(fs);
	return has_dumped;
}

/*
 * create_aout_tables() parses the env- and arg-strings in new user
 * memory and creates the pointer tables from them, and puts their
 * addresses on the "stack", returning the new stack pointer value.
 */
static unsigned long __user *create_aout_tables(char __user *p, struct linux_binprm * bprm)
{
	char __user * __user *argv;
	char __user * __user *envp;
	unsigned long __user *sp;
	int argc = bprm->argc;
	int envc = bprm->envc;

	sp = (void __user *)((-(unsigned long)sizeof(char *)) & (unsigned long) p);
#ifdef __sparc__
	/* This imposes the proper stack alignment for a new process. */
	sp = (void __user *) (((unsigned long) sp) & ~7);
	if ((envc+argc+3)&1) --sp;
#endif
#ifdef __alpha__
/* whee.. test-programs are so much fun. */
	put_user(0, --sp);
	put_user(0, --sp);
	if (bprm->loader) {
		put_user(0, --sp);
		put_user(0x3eb, --sp);
		put_user(bprm->loader, --sp);
		put_user(0x3ea, --sp);
	}
	put_user(bprm->exec, --sp);
	put_user(0x3e9, --sp);
#endif
	sp -= envc+1;
	envp = (char __user * __user *) sp;
	sp -= argc+1;
	argv = (char __user * __user *) sp;
#if defined(__i386__) || defined(__mc68000__) || defined(__arm__) || defined(__arch_um__)
	put_user((unsigned long) envp,--sp);
	put_user((unsigned long) argv,--sp);
#endif
	put_user(argc,--sp);
	current->mm->arg_start = (unsigned long) p;
	while (argc-->0) {
		char c;
		put_user(p,argv++);
		do {
			get_user(c,p++);
		} while (c);
	}
	put_user(NULL,argv);
	current->mm->arg_end = current->mm->env_start = (unsigned long) p;
	while (envc-->0) {
		char c;
		put_user(p,envp++);
		do {
			get_user(c,p++);
		} while (c);
	}
	put_user(NULL,envp);
	current->mm->env_end = (unsigned long) p;
	return sp;
}

/*
 * These are the functions used to load a.out style executables and shared
 * libraries.  There is no binary dependent code anywhere else.
 */

static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
	struct exec ex;
	unsigned long error;
	unsigned long fd_offset;
	unsigned long rlim;
	int retval;

	ex = *((struct exec *) bprm->buf);		/* exec-header */
	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
	     N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||
	    N_TRSIZE(ex) || N_DRSIZE(ex) ||
	    i_size_read(bprm->file->f_path.dentry->d_inode) < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
		return -ENOEXEC;
	}

	/*
	 * Requires a mmap handler. This prevents people from using a.out
	 * as part of an exploit attack against /proc-related vulnerabilities.
	 */
	if (!bprm->file->f_op || !bprm->file->f_op->mmap)
		return -ENOEXEC;

	fd_offset = N_TXTOFF(ex);

	/* Check initial limits. This avoids letting people circumvent
	 * size limits imposed on them by creating programs with large
	 * arrays in the data or bss.
	 */
	rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
	if (rlim >= RLIM_INFINITY)
		rlim = ~0;
	if (ex.a_data + ex.a_bss > rlim)
		return -ENOMEM;

	/* Flush all traces of the currently running executable */
	retval = flush_old_exec(bprm);
	if (retval)
		return retval;

	/* OK, This is the point of no return */
#if defined(__alpha__)
	SET_AOUT_PERSONALITY(bprm, ex);
#elif defined(__sparc__)
	set_personality(PER_SUNOS);
#if !defined(__sparc_v9__)
	memcpy(&current->thread.core_exec, &ex, sizeof(struct exec));
#endif
#else
	set_personality(PER_LINUX);
#endif

	current->mm->end_code = ex.a_text +
		(current->mm->start_code = N_TXTADDR(ex));
	current->mm->end_data = ex.a_data +
		(current->mm->start_data = N_DATADDR(ex));
	current->mm->brk = ex.a_bss +
		(current->mm->start_brk = N_BSSADDR(ex));
	current->mm->free_area_cache = current->mm->mmap_base;
	current->mm->cached_hole_size = 0;

	current->mm->mmap = NULL;
	compute_creds(bprm);
 	current->flags &= ~PF_FORKNOEXEC;
#ifdef __sparc__
	if (N_MAGIC(ex) == NMAGIC) {
		loff_t pos = fd_offset;
		/* Fuck me plenty... */
		/* <AOL></AOL> */
		down_write(&current->mm->mmap_sem);	
		error = do_brk(N_TXTADDR(ex), ex.a_text);
		up_write(&current->mm->mmap_sem);
		bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
			  ex.a_text, &pos);
		down_write(&current->mm->mmap_sem);
		error = do_brk(N_DATADDR(ex), ex.a_data);
		up_write(&current->mm->mmap_sem);
		bprm->file->f_op->read(bprm->file, (char *) N_DATADDR(ex),
			  ex.a_data, &pos);
		goto beyond_if;
	}
#endif

	if (N_MAGIC(ex) == OMAGIC) {
		unsigned long text_addr, map_size;
		loff_t pos;

		text_addr = N_TXTADDR(ex);

#if defined(__alpha__) || defined(__sparc__)
		pos = fd_offset;
		map_size = ex.a_text+ex.a_data + PAGE_SIZE - 1;
#else
		pos = 32;
		map_size = ex.a_text+ex.a_data;
#endif
		down_write(&current->mm->mmap_sem);
		error = do_brk(text_addr & PAGE_MASK, map_size);
		up_write(&current->mm->mmap_sem);
		if (error != (text_addr & PAGE_MASK)) {
			send_sig(SIGKILL, current, 0);
			return error;
		}

		error = bprm->file->f_op->read(bprm->file,
			  (char __user *)text_addr,
			  ex.a_text+ex.a_data, &pos);
		if ((signed long)error < 0) {
			send_sig(SIGKILL, current, 0);
			return error;
		}
			 
		flush_icache_range(text_addr, text_addr+ex.a_text+ex.a_data);
	} else {
		static unsigned long error_time, error_time2;
		if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
		    (N_MAGIC(ex) != NMAGIC) && (jiffies-error_time2) > 5*HZ)
		{
			printk(KERN_NOTICE "executable not page aligned\n");
			error_time2 = jiffies;
		}

		if ((fd_offset & ~PAGE_MASK) != 0 &&
		    (jiffies-error_time) > 5*HZ)
		{
			printk(KERN_WARNING 
			       "fd_offset is not page aligned. Please convert program: %s\n",
			       bprm->file->f_path.dentry->d_name.name);
			error_time = jiffies;
		}

		if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) {
			loff_t pos = fd_offset;
			down_write(&current->mm->mmap_sem);
			do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);
			up_write(&current->mm->mmap_sem);
			bprm->file->f_op->read(bprm->file,
					(char __user *)N_TXTADDR(ex),
					ex.a_text+ex.a_data, &pos);
			flush_icache_range((unsigned long) N_TXTADDR(ex),
					   (unsigned long) N_TXTADDR(ex) +
					   ex.a_text+ex.a_data);
			goto beyond_if;
		}

		down_write(&current->mm->mmap_sem);
		error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text,
			PROT_READ | PROT_EXEC,
			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
			fd_offset);
		up_write(&current->mm->mmap_sem);

		if (error != N_TXTADDR(ex)) {
			send_sig(SIGKILL, current, 0);
			return error;
		}

		down_write(&current->mm->mmap_sem);
 		error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data,
				PROT_READ | PROT_WRITE | PROT_EXEC,
				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
				fd_offset + ex.a_text);
		up_write(&current->mm->mmap_sem);
		if (error != N_DATADDR(ex)) {
			send_sig(SIGKILL, current, 0);
			return error;
		}
	}
beyond_if:
	set_binfmt(&aout_format);

	retval = set_brk(current->mm->start_brk, current->mm->brk);
	if (retval < 0) {
		send_sig(SIGKILL, current, 0);
		return retval;
	}

	retval = setup_arg_pages(bprm, STACK_TOP, EXSTACK_DEFAULT);
	if (retval < 0) { 
		/* Someone check-me: is this error path enough? */ 
		send_sig(SIGKILL, current, 0); 
		return retval;
	}

	current->mm->start_stack =
		(unsigned long) create_aout_tables((char __user *) bprm->p, bprm);
#ifdef __alpha__
	regs->gp = ex.a_gpvalue;
#endif
	start_thread(regs, ex.a_entry, current->mm->start_stack);
	if (unlikely(current->ptrace & PT_PTRACED)) {
		if (current->ptrace & PT_TRACE_EXEC)
			ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP);
		else
			send_sig(SIGTRAP, current, 0);
	}
	return 0;
}

static int load_aout_library(struct file *file)
{
	struct inode * inode;
	unsigned long bss, start_addr, len;
	unsigned long error;
	int retval;
	struct exec ex;

	inode = file->f_path.dentry->d_inode;

	retval = -ENOEXEC;
	error = kernel_read(file, 0, (char *) &ex, sizeof(ex));
	if (error != sizeof(ex))
		goto out;

	/* We come in here for the regular a.out style of shared libraries */
	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
	    N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
	    i_size_read(inode) < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
		goto out;
	}

	/*
	 * Requires a mmap handler. This prevents people from using a.out
	 * as part of an exploit attack against /proc-related vulnerabilities.
	 */
	if (!file->f_op || !file->f_op->mmap)
		goto out;

	if (N_FLAGS(ex))
		goto out;

	/* For  QMAGIC, the starting address is 0x20 into the page.  We mask
	   this off to get the starting address for the page */

	start_addr =  ex.a_entry & 0xfffff000;

	if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
		static unsigned long error_time;
		loff_t pos = N_TXTOFF(ex);

		if ((jiffies-error_time) > 5*HZ)
		{
			printk(KERN_WARNING 
			       "N_TXTOFF is not page aligned. Please convert library: %s\n",
			       file->f_path.dentry->d_name.name);
			error_time = jiffies;
		}
		down_write(&current->mm->mmap_sem);
		do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss);
		up_write(&current->mm->mmap_sem);
		
		file->f_op->read(file, (char __user *)start_addr,
			ex.a_text + ex.a_data, &pos);
		flush_icache_range((unsigned long) start_addr,
				   (unsigned long) start_addr + ex.a_text + ex.a_data);

		retval = 0;
		goto out;
	}
	/* Now use mmap to map the library into memory. */
	down_write(&current->mm->mmap_sem);
	error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
			PROT_READ | PROT_WRITE | PROT_EXEC,
			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
			N_TXTOFF(ex));
	up_write(&current->mm->mmap_sem);
	retval = error;
	if (error != start_addr)
		goto out;

	len = PAGE_ALIGN(ex.a_text + ex.a_data);
	bss = ex.a_text + ex.a_data + ex.a_bss;
	if (bss > len) {
		down_write(&current->mm->mmap_sem);
		error = do_brk(start_addr + len, bss - len);
		up_write(&current->mm->mmap_sem);
		retval = error;
		if (error != start_addr + len)
			goto out;
	}
	retval = 0;
out:
	return retval;
}

static int __init init_aout_binfmt(void)
{
	return register_binfmt(&aout_format);
}

static void __exit exit_aout_binfmt(void)
{
	unregister_binfmt(&aout_format);
}

core_initcall(init_aout_binfmt);
module_exit(exit_aout_binfmt);
MODULE_LICENSE("GPL");
an class="hl kwa">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; spin_lock_irqsave(&devc->lock,flags); busy = devc->m_busy; devc->m_busy = 1; spin_unlock_irqrestore(&devc->lock,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; } static irqreturn_t mpuintr(int irq, void *dev_id) { struct mpu_config *devc; int dev = (int)(unsigned long) dev_id; int handled = 0; devc = &dev_conf[dev]; if (input_avail(devc)) { handled = 1; 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); } } return IRQ_RETVAL(handled); } 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; struct coproc_operations *coprocessor; 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 ( (coprocessor = midi_devs[dev]->coproc) != NULL ) { if (!try_module_get(coprocessor->owner)) { mpu401_close(dev); return -ENODEV; } if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0) { printk(KERN_WARNING "MPU-401: Can't access coprocessor device\n"); mpu401_close(dev); 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; struct coproc_operations *coprocessor; devc = &dev_conf[dev]; if (devc->uart_mode) reset_mpu401(devc); /* * This disables the UART mode */ devc->mode = 0; devc->inputintr = NULL; coprocessor = midi_devs[dev]->coproc; if (coprocessor) { coprocessor->close(coprocessor->devc, COPR_MIDI); module_put(coprocessor->owner); } 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--); spin_lock_irqsave(&devc->lock,flags); if (!output_ready(devc)) { printk(KERN_WARNING "mpu401: Send data timeout\n"); spin_unlock_irqrestore(&devc->lock,flags); return 0; } write_data(devc, midi_byte); spin_unlock_irqrestore(&devc->lock,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) { printk(KERN_WARNING "mpu401: Command (0x%x) timeout\n", (int) cmd->cmd); return -EIO; } spin_lock_irqsave(&devc->lock,flags); if (!output_ready(devc)) { spin_unlock_irqrestore(&devc->lock,flags); goto retry; } write_command(devc, cmd->cmd); ok = 0; for (timeout = 50000; timeout > 0 && !ok; timeout--) { if (input_avail(devc)) { if (devc->opened && devc->mode == MODE_SYNTH) { if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK) ok = 1; } else { /* Device is not currently open. Use simpler method */ if (read_data(devc) == MPU_ACK) ok = 1; } } } if (!ok) { spin_unlock_irqrestore(&devc->lock,flags); return -EIO; } if (cmd->nr_args) { for (i = 0; i < cmd->nr_args; i++) { for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--); if (!mpu401_out(dev, cmd->data[i])) { spin_unlock_irqrestore(&devc->lock,flags); printk(KERN_WARNING "mpu401: Command (0x%x), parm send failed.\n", (int) cmd->cmd); return -EIO; } } } ret = 0; cmd->data[0] = 0; if (cmd->nr_returns) { for (i = 0; i < cmd->nr_returns; i++) { ok = 0; for (timeout = 5000; timeout > 0 && !ok; timeout--) if (input_avail(devc)) { cmd->data[i] = read_data(devc); ok = 1; } if (!ok) { spin_unlock_irqrestore(&devc->lock,flags); return -EIO; } } } spin_unlock_irqrestore(&devc->lock,flags); return ret; } static int mpu_cmd(int dev, int cmd, int data) { int ret; static mpu_command_rec rec; rec.cmd = cmd & 0xff; rec.nr_args = ((cmd & 0xf0) == 0xE0); rec.nr_returns = ((cmd & 0xf0) == 0xA0); rec.data[0] = data & 0xff; if ((ret = mpu401_command(dev, &rec)) < 0) return ret; return (unsigned char) rec.data[0]; } static int mpu401_prefix_cmd(int dev, unsigned char status) { struct mpu_config *devc = &dev_conf[dev]; if (devc->uart_mode) return 1; if (status < 0xf0) { if (mpu_cmd(dev, 0xD0, 0) < 0) return 0; return 1; } switch (status) { case 0xF0: if (mpu_cmd(dev, 0xDF, 0) < 0) return 0; return 1; default: return 0; } } static int mpu401_start_read(int dev) { return 0; } static int mpu401_end_read(int dev) { return 0; } static int mpu401_ioctl(int dev, unsigned cmd, void __user *arg) { struct mpu_config *devc; mpu_command_rec rec; int val, ret; devc = &dev_conf[dev]; switch (cmd) { case SNDCTL_MIDI_MPUMODE: if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */ printk(KERN_WARNING "mpu401: Intelligent mode not supported by the HW\n"); return -EINVAL; } if (get_user(val, (int __user *)arg)) return -EFAULT; set_uart_mode(dev, devc, !val); return 0; case SNDCTL_MIDI_MPUCMD: if (copy_from_user(&rec, arg, sizeof(rec))) return -EFAULT; if ((ret = mpu401_command(dev, &rec)) < 0) return ret; if (copy_to_user(arg, &rec, sizeof(rec))) return -EFAULT; return 0; default: return -EINVAL; } } static void mpu401_kick(int dev) { } static int mpu401_buffer_status(int dev) { return 0; /* * No data in buffers */ } static int mpu_synth_ioctl(int dev, unsigned int cmd, void __user *arg) { int midi_dev; struct 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]; switch (cmd) { case SNDCTL_SYNTH_INFO: if (copy_to_user(arg, &mpu_synth_info[midi_dev], sizeof(struct synth_info))) return -EFAULT; return 0; case SNDCTL_SYNTH_MEMAVL: return 0x7fffffff; default: return -EINVAL; } } static int mpu_synth_open(int dev, int mode) { int midi_dev, err; struct mpu_config *devc; struct coproc_operations *coprocessor; 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]; /* * 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 (devc->opened) return -EBUSY; devc->mode = MODE_SYNTH; devc->synthno = dev; devc->inputintr = NULL; coprocessor = midi_devs[midi_dev]->coproc; if (coprocessor) { if (!try_module_get(coprocessor->owner)) return -ENODEV; if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0) { printk(KERN_WARNING "mpu401: Can't access coprocessor device\n"); return err; } } devc->opened = mode; reset_mpu401(devc); if (mode & OPEN_READ) { mpu_cmd(midi_dev, 0x8B, 0); /* Enable data in stop mode */ mpu_cmd(midi_dev, 0x34, 0); /* Return timing bytes in stop mode */ mpu_cmd(midi_dev, 0x87, 0); /* Enable pitch & controller */ } return 0; } static void mpu_synth_close(int dev) { int midi_dev; struct mpu_config *devc; struct coproc_operations *coprocessor; midi_dev = synth_devs[dev]->midi_dev; devc = &dev_conf[midi_dev]; mpu_cmd(midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */ mpu_cmd(midi_dev, 0x8a, 0); /* Disable data in stopped mode */ devc->inputintr = NULL; coprocessor = midi_devs[midi_dev]->coproc; if (coprocessor) { coprocessor->close(coprocessor->devc, COPR_MIDI); module_put(coprocessor->owner); } devc->opened = 0; devc->mode = 0; } #define MIDI_SYNTH_NAME "MPU-401 UART Midi" #define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT #include "midi_synth.h" static struct synth_operations mpu401_synth_proto = { .owner = THIS_MODULE, .id = "MPU401", .info = NULL, .midi_dev = 0, .synth_type = SYNTH_TYPE_MIDI, .synth_subtype = 0, .open = mpu_synth_open, .close = mpu_synth_close, .ioctl = mpu_synth_ioctl, .kill_note = midi_synth_kill_note, .start_note = midi_synth_start_note, .set_instr = midi_synth_set_instr, .reset = midi_synth_reset, .hw_control = midi_synth_hw_control, .load_patch = midi_synth_load_patch, .aftertouch = midi_synth_aftertouch, .controller = midi_synth_controller, .panning = midi_synth_panning, .bender = midi_synth_bender, .setup_voice = midi_synth_setup_voice, .send_sysex = midi_synth_send_sysex }; static struct synth_operations *mpu401_synth_operations[MAX_MIDI_DEV]; static struct midi_operations mpu401_midi_proto = { .owner = THIS_MODULE, .info = {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, .in_info = {0}, .open = mpu401_open, .close = mpu401_close, .ioctl = mpu401_ioctl, .outputc = mpu401_out, .start_read = mpu401_start_read, .end_read = mpu401_end_read, .kick = mpu401_kick, .buffer_status = mpu401_buffer_status, .prefix_cmd = mpu401_prefix_cmd }; static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV]; static void mpu401_chk_version(int n, struct mpu_config *devc) { int tmp; unsigned long flags; devc->version = devc->revision = 0; spin_lock_irqsave(&devc->lock,flags); if ((tmp = mpu_cmd(n, 0xAC, 0)) < 0) { spin_unlock_irqrestore(&devc->lock,flags); return; } if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */ { spin_unlock_irqrestore(&devc->lock,flags); return; } devc->version = tmp; if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0) { devc->version = 0; spin_unlock_irqrestore(&devc->lock,flags); return; } devc->revision = tmp; spin_unlock_irqrestore(&devc->lock,flags); } int attach_mpu401(struct address_info *hw_config, struct module *owner) { unsigned long flags; char revision_char; int m, ret; struct mpu_config *devc; hw_config->slots[1] = -1; m = sound_alloc_mididev(); if (m == -1) { printk(KERN_WARNING "MPU-401: Too many midi devices detected\n"); ret = -ENOMEM; goto out_err; } devc = &dev_conf[m]; devc->base = hw_config->io_base; devc->osp = hw_config->osp; devc->irq = hw_config->irq; devc->opened = 0; devc->uart_mode = 0; devc->initialized = 0; devc->version = 0; devc->revision = 0; devc->capabilities = 0; devc->timer_flag = 0; devc->m_busy = 0; devc->m_state = ST_INIT; devc->shared_irq = hw_config->always_detect; devc->irq = hw_config->irq; spin_lock_init(&devc->lock); if (devc->irq < 0) { devc->irq *= -1; devc->shared_irq = 1; } if (!hw_config->always_detect) { /* Verify the hardware again */ if (!reset_mpu401(devc)) { printk(KERN_WARNING "mpu401: Device didn't respond\n"); ret = -ENODEV; goto out_mididev; } if (!devc->shared_irq) { if (request_irq(devc->irq, mpuintr, 0, "mpu401", hw_config) < 0) { printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq); ret = -ENOMEM; goto out_mididev; } } spin_lock_irqsave(&devc->lock,flags); mpu401_chk_version(m, devc); if (devc->version == 0) mpu401_chk_version(m, devc); spin_unlock_irqrestore(&devc->lock,flags); } if (devc->version != 0) if (mpu_cmd(m, 0xC5, 0) >= 0) /* Set timebase OK */ if (mpu_cmd(m, 0xE0, 120) >= 0) /* Set tempo OK */ devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */ mpu401_synth_operations[m] = kmalloc(sizeof(struct synth_operations), GFP_KERNEL); if (mpu401_synth_operations[m] == NULL) { printk(KERN_ERR "mpu401: Can't allocate memory\n"); ret = -ENOMEM; goto out_irq; } if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */ { memcpy((char *) mpu401_synth_operations[m], (char *) &std_midi_synth, sizeof(struct synth_operations)); } else { memcpy((char *) mpu401_synth_operations[m], (char *) &mpu401_synth_proto, sizeof(struct synth_operations)); } if (owner) mpu401_synth_operations[m]->owner = owner; memcpy((char *) &mpu401_midi_operations[m], (char *) &mpu401_midi_proto, sizeof(struct midi_operations)); mpu401_midi_operations[m].converter = mpu401_synth_operations[m]; memcpy((char *) &mpu_synth_info[m], (char *) &mpu_synth_info_proto, sizeof(struct synth_info)); n_mpu_devs++; if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */ { int ports = (devc->revision & 0x08) ? 32 : 16; devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE | MPU_CAP_CLS | MPU_CAP_2PORT; revision_char = (devc->revision == 0x7f) ? 'M' : ' '; sprintf(mpu_synth_info[m].name, "MQX-%d%c MIDI Interface #%d", ports, revision_char, n_mpu_devs); } else { revision_char = devc->revision ? devc->revision + '@' : ' '; if ((int) devc->revision > ('Z' - '@')) revision_char = '+'; devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK; if (hw_config->name) sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name); else sprintf(mpu_synth_info[m].name, "MPU-401 %d.%d%c Midi interface #%d", (int) (devc->version & 0xf0) >> 4, devc->version & 0x0f, revision_char, n_mpu_devs); } strcpy(mpu401_midi_operations[m].info.name, mpu_synth_info[m].name); conf_printf(mpu_synth_info[m].name, hw_config); mpu401_synth_operations[m]->midi_dev = devc->devno = m; mpu401_synth_operations[devc->devno]->info = &mpu_synth_info[devc->devno]; if (devc->capabilities & MPU_CAP_INTLG) /* Intelligent mode */ hw_config->slots[2] = mpu_timer_init(m); midi_devs[m] = &mpu401_midi_operations[devc->devno]; if (owner) midi_devs[m]->owner = owner; hw_config->slots[1] = m; sequencer_init(); return 0; out_irq: free_irq(devc->irq, hw_config); out_mididev: sound_unload_mididev(m); out_err: release_region(hw_config->io_base, 2); return ret; } static int reset_mpu401(struct mpu_config *devc) { unsigned long flags; int ok, timeout, n; int timeout_limit; /* * Send the RESET command. Try again if no success at the first time. * (If the device is in the UART mode, it will not ack the reset cmd). */ ok = 0; timeout_limit = devc->initialized ? 30000 : 100000; devc->initialized = 1; for (n = 0; n < 2 && !ok; n++) { for (timeout = timeout_limit; timeout > 0 && !ok; timeout--) ok = output_ready(devc); write_command(devc, MPU_RESET); /* * Send MPU-401 RESET Command */ /* * Wait at least 25 msec. This method is not accurate so let's make the * loop bit longer. Cannot sleep since this is called during boot. */ for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--) { spin_lock_irqsave(&devc->lock,flags); if (input_avail(devc)) if (read_data(devc) == MPU_ACK) ok = 1; spin_unlock_irqrestore(&devc->lock,flags); } } devc->m_state = ST_INIT; devc->m_ptr = 0; devc->m_left = 0; devc->last_status = 0; devc->uart_mode = 0; return ok; } static void set_uart_mode(int dev, struct mpu_config *devc, int arg) { if (!arg && (devc->capabilities & MPU_CAP_INTLG)) return; if ((devc->uart_mode == 0) == (arg == 0)) return; /* Already set */ reset_mpu401(devc); /* This exits the uart mode */ if (arg) { if (mpu_cmd(dev, UART_MODE_ON, 0) < 0) { printk(KERN_ERR "mpu401: Can't enter UART mode\n"); devc->uart_mode = 0; return; } } devc->uart_mode = arg; } int probe_mpu401(struct address_info *hw_config, struct resource *ports) { 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; 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]; if (n != -1) { release_region(hw_config->io_base, 2); if (hw_config->always_detect == 0 && hw_config->irq > 0) free_irq(hw_config->irq, hw_config); p=mpu401_synth_operations[n]; sound_unload_mididev(n); sound_unload_timerdev(hw_config->slots[2]); 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(struct mpu_config *devc) { unsigned long flags; spin_lock_irqsave(&devc->lock,flags); next_event_time = (unsigned long) -1; prev_event_time = 0; curr_ticks = curr_clocks = 0; spin_unlock_irqrestore(&devc->lock,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) { struct mpu_config *devc= &dev_conf[midi_dev]; tmr_reset(devc); 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; struct mpu_config *devc= &dev_conf[midi_dev]; if (timer_open) return -EBUSY; tmr_reset(devc); 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, void __user *arg) { int midi_dev = sound_timer_devs[dev]->devlink; int __user *p = (int __user *)arg; switch (command) { case SNDCTL_TMR_SOURCE: { int parm; if (get_user(parm, p)) return -EFAULT; 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 */ } if (put_user(timer_mode, p)) return -EFAULT; return 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; if (get_user(val, p)) return -EFAULT; if (val) set_timebase(midi_dev, val); if (put_user(curr_timebase, p)) return -EFAULT; return curr_timebase; } break; case SNDCTL_TMR_TEMPO: { int val; int ret; if (get_user(val, p)) return -EFAULT; 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; } if (put_user(curr_tempo, p)) return -EFAULT; return curr_tempo; } break; case SNDCTL_SEQ_CTRLRATE: { int val; if (get_user(val, p)) return -EFAULT; if (val != 0) /* Can't change */ return -EINVAL; val = ((curr_tempo * curr_timebase) + 30)/60; if (put_user(val, p)) return -EFAULT; return val; } break; case SNDCTL_SEQ_GETTIME: if (put_user(curr_ticks, p)) return -EFAULT; return curr_ticks; case SNDCTL_TMR_METRONOME: if (get_user(metronome_mode, p)) return -EFAULT; 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); static struct address_info cfg; static int io = -1; static int irq = -1; module_param(irq, int, 0); module_param(io, int, 0); static int __init init_mpu401(void) { int ret; /* Can be loaded either for module use or to provide functions to others */ if (io != -1 && irq != -1) { struct resource *ports; cfg.irq = irq; cfg.io_base = io; ports = request_region(io, 2, "mpu401"); if (!ports) return -EBUSY; if (probe_mpu401(&cfg, ports) == 0) { release_region(io, 2); return -ENODEV; } if ((ret = attach_mpu401(&cfg, THIS_MODULE))) return ret; } return 0; } static 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 MODULE static 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); #endif MODULE_LICENSE("GPL");