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

📄 i8042.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  i8042 keyboard and mouse controller driver for Linux * *  Copyright (c) 1999-2002 Vojtech Pavlik *//* * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */#include <linux/delay.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/config.h>#include <linux/reboot.h>#include <linux/init.h>#include <linux/sysdev.h>#include <linux/pm.h>#include <linux/serio.h>#include <asm/io.h>MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver");MODULE_LICENSE("GPL");static unsigned int i8042_noaux;module_param_named(noaux, i8042_noaux, bool, 0);MODULE_PARM_DESC(noaux, "Do not probe or use AUX (mouse) port.");static unsigned int i8042_nomux;module_param_named(nomux, i8042_nomux, bool, 0);MODULE_PARM_DESC(nomux, "Do not check whether an active multiplexing conrtoller is present.");static unsigned int i8042_unlock;module_param_named(unlock, i8042_unlock, bool, 0);MODULE_PARM_DESC(unlock, "Ignore keyboard lock.");static unsigned int i8042_reset;module_param_named(reset, i8042_reset, bool, 0);MODULE_PARM_DESC(reset, "Reset controller during init and cleanup.");static unsigned int i8042_direct;module_param_named(direct, i8042_direct, bool, 0);MODULE_PARM_DESC(direct, "Put keyboard port into non-translated mode.");static unsigned int i8042_dumbkbd;module_param_named(dumbkbd, i8042_dumbkbd, bool, 0);MODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard");__obsolete_setup("i8042_noaux");__obsolete_setup("i8042_nomux");__obsolete_setup("i8042_unlock");__obsolete_setup("i8042_reset");__obsolete_setup("i8042_direct");__obsolete_setup("i8042_dumbkbd");#undef DEBUG#include "i8042.h"spinlock_t i8042_lock = SPIN_LOCK_UNLOCKED;struct i8042_values {	int irq;	unsigned char disable;	unsigned char irqen;	unsigned char exists;	signed char mux;	unsigned char *name;	unsigned char *phys;};static struct serio i8042_kbd_port;static struct serio i8042_aux_port;static unsigned char i8042_initial_ctr;static unsigned char i8042_ctr;static unsigned char i8042_mux_open;static unsigned char i8042_mux_present;static unsigned char i8042_sysdev_initialized;static struct pm_dev *i8042_pm_dev;struct timer_list i8042_timer;/* * Shared IRQ's require a device pointer, but this driver doesn't support * multiple devices */#define i8042_request_irq_cookie (&i8042_timer)static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs);/* * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to * be ready for reading values from it / writing values to it. */static int i8042_wait_read(void){	int i = 0;	while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {		udelay(50);		i++;	}	return -(i == I8042_CTL_TIMEOUT);}static int i8042_wait_write(void){	int i = 0;	while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {		udelay(50);		i++;	}	return -(i == I8042_CTL_TIMEOUT);}/* * i8042_flush() flushes all data that may be in the keyboard and mouse buffers * of the i8042 down the toilet. */static int i8042_flush(void){	unsigned long flags;	unsigned char data;	int i = 0;	spin_lock_irqsave(&i8042_lock, flags);	while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_SIZE)) {		data = i8042_read_data();		dbg("%02x <- i8042 (flush, %s)", data,			i8042_read_status() & I8042_STR_AUXDATA ? "aux" : "kbd");	}	spin_unlock_irqrestore(&i8042_lock, flags);	return i;}/* * i8042_command() executes a command on the i8042. It also sends the input * parameter(s) of the commands to it, and receives the output value(s). The * parameters are to be stored in the param array, and the output is placed * into the same array. The number of the parameters and output values is * encoded in bits 8-11 of the command number. */static int i8042_command(unsigned char *param, int command){	unsigned long flags;	int retval = 0, i = 0;	spin_lock_irqsave(&i8042_lock, flags);	retval = i8042_wait_write();	if (!retval) {		dbg("%02x -> i8042 (command)", command & 0xff);		i8042_write_command(command & 0xff);	}	if (!retval)		for (i = 0; i < ((command >> 12) & 0xf); i++) {			if ((retval = i8042_wait_write())) break;			dbg("%02x -> i8042 (parameter)", param[i]);			i8042_write_data(param[i]);		}	if (!retval)		for (i = 0; i < ((command >> 8) & 0xf); i++) {			if ((retval = i8042_wait_read())) break;			if (i8042_read_status() & I8042_STR_AUXDATA)				param[i] = ~i8042_read_data();			else				param[i] = i8042_read_data();			dbg("%02x <- i8042 (return)", param[i]);		}	spin_unlock_irqrestore(&i8042_lock, flags);	if (retval)		dbg("     -- i8042 (timeout)");	return retval;}/* * i8042_kbd_write() sends a byte out through the keyboard interface. */static int i8042_kbd_write(struct serio *port, unsigned char c){	unsigned long flags;	int retval = 0;	spin_lock_irqsave(&i8042_lock, flags);	if(!(retval = i8042_wait_write())) {		dbg("%02x -> i8042 (kbd-data)", c);		i8042_write_data(c);	}	spin_unlock_irqrestore(&i8042_lock, flags);	return retval;}/* * i8042_aux_write() sends a byte out through the aux interface. */static int i8042_aux_write(struct serio *port, unsigned char c){	struct i8042_values *values = port->driver;	int retval;/* * Send the byte out. */	if (values->mux == -1)		retval = i8042_command(&c, I8042_CMD_AUX_SEND);	else		retval = i8042_command(&c, I8042_CMD_MUX_SEND + values->mux);/* * Make sure the interrupt happens and the character is received even * in the case the IRQ isn't wired, so that we can receive further * characters later. */	i8042_interrupt(0, NULL, NULL);	return retval;}/* * i8042_activate_port() enables port on a chip. */static int i8042_activate_port(struct serio *port){	struct i8042_values *values = port->driver;	i8042_flush();	/*	 * Enable port again here because it is disabled if we are	 * resuming (normally it is enabled already).	 */	i8042_ctr &= ~values->disable;	i8042_ctr |= values->irqen;	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {		i8042_ctr &= ~values->irqen;		return -1;	}	return 0;}/* * i8042_open() is called when a port is open by the higher layer. * It allocates the interrupt and calls i8042_enable_port. */static int i8042_open(struct serio *port){	struct i8042_values *values = port->driver;	if (values->mux != -1)		if (i8042_mux_open++)			return 0;	if (request_irq(values->irq, i8042_interrupt,			SA_SHIRQ, "i8042", i8042_request_irq_cookie)) {		printk(KERN_ERR "i8042.c: Can't get irq %d for %s, unregistering the port.\n", values->irq, values->name);		goto irq_fail;	}	if (i8042_activate_port(port)) {		printk(KERN_ERR "i8042.c: Can't activate %s, unregistering the port\n", values->name);		goto activate_fail;	}	i8042_interrupt(0, NULL, NULL);	return 0;activate_fail:	free_irq(values->irq, i8042_request_irq_cookie);irq_fail:	values->exists = 0;	serio_unregister_port_delayed(port);	return -1;}/* * i8042_close() frees the interrupt, so that it can possibly be used * by another driver. We never know - if the user doesn't have a mouse, * the BIOS could have used the AUX interrupt for PCI. */static void i8042_close(struct serio *port){	struct i8042_values *values = port->driver;	if (values->mux != -1)		if (--i8042_mux_open)			return;	i8042_ctr &= ~values->irqen;	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {		printk(KERN_ERR "i8042.c: Can't write CTR while closing %s.\n", values->name);		return;	}	free_irq(values->irq, i8042_request_irq_cookie);	i8042_flush();}/* * Structures for registering the devices in the serio.c module. */static struct i8042_values i8042_kbd_values = {	.irqen =	I8042_CTR_KBDINT,	.disable =	I8042_CTR_KBDDIS,	.name =		"KBD",	.mux =		-1,};static struct serio i8042_kbd_port ={	.type =		SERIO_8042_XL,	.write =	i8042_kbd_write,	.open =		i8042_open,	.close =	i8042_close,	.driver =	&i8042_kbd_values,	.name =		"i8042 Kbd Port",	.phys =		I8042_KBD_PHYS_DESC,};static struct i8042_values i8042_aux_values = {	.irqen =	I8042_CTR_AUXINT,	.disable =	I8042_CTR_AUXDIS,	.name =		"AUX",	.mux =		-1,};static struct serio i8042_aux_port ={	.type =		SERIO_8042,	.write =	i8042_aux_write,	.open =		i8042_open,	.close =	i8042_close,	.driver =	&i8042_aux_values,	.name =		"i8042 Aux Port",	.phys =		I8042_AUX_PHYS_DESC,};static struct i8042_values i8042_mux_values[4];static struct serio i8042_mux_port[4];static char i8042_mux_names[4][32];static char i8042_mux_short[4][16];static char i8042_mux_phys[4][32];/* * i8042_interrupt() is the most important function in this driver - * it handles the interrupts from the i8042, and sends incoming bytes * to the upper layers. */static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs){	unsigned long flags;	unsigned char str, data = 0;	unsigned int dfl;	int ret;	mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);	spin_lock_irqsave(&i8042_lock, flags);	str = i8042_read_status();	if (str & I8042_STR_OBF)		data = i8042_read_data();	spin_unlock_irqrestore(&i8042_lock, flags);	if (~str & I8042_STR_OBF) {		if (irq) dbg("Interrupt %d, without any data", irq);		ret = 0;		goto out;	}	dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |	      ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);	if (i8042_mux_values[0].exists && (str & I8042_STR_AUXDATA)) {		if (str & I8042_STR_MUXERR) {			switch (data) {				case 0xfd:				case 0xfe: dfl = SERIO_TIMEOUT; break;				case 0xff: dfl = SERIO_PARITY; break;			}			data = 0xfe;		} else dfl = 0;		dbg("%02x <- i8042 (interrupt, aux%d, %d%s%s)",			data, (str >> 6), irq,			dfl & SERIO_PARITY ? ", bad parity" : "",			dfl & SERIO_TIMEOUT ? ", timeout" : "");		serio_interrupt(i8042_mux_port + ((str >> 6) & 3), data, dfl, regs);		goto irq_ret;	}	dbg("%02x <- i8042 (interrupt, %s, %d%s%s)",		data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq,		dfl & SERIO_PARITY ? ", bad parity" : "",		dfl & SERIO_TIMEOUT ? ", timeout" : "");	if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) {		serio_interrupt(&i8042_aux_port, data, dfl, regs);		goto irq_ret;	}	if (!i8042_kbd_values.exists)		goto irq_ret;	serio_interrupt(&i8042_kbd_port, data, dfl, regs);irq_ret:	ret = 1;out:	return IRQ_RETVAL(ret);}/* * i8042_enable_mux_mode checks whether the controller has an active * multiplexor and puts the chip into Multiplexed (as opposed to * Legacy) mode. */static int i8042_enable_mux_mode(struct i8042_values *values, unsigned char *mux_version){	unsigned char param;/* * Get rid of bytes in the queue. */	i8042_flush();/* * Internal loopback test - send three bytes, they should come back from the * mouse interface, the last should be version. Note that we negate mouseport * command responses for the i8042_check_aux() routine. */	param = 0xf0;	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0x0f)		return -1;	param = 0x56;	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0xa9)		return -1;	param = 0xa4;	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param == 0x5b) {/* * Do another loop test with the 0x5a value. Doing anything else upsets * Profusion/ServerWorks OSB4 chipsets. */		param = 0x5a;		i8042_command(&param, I8042_CMD_AUX_LOOP);		return -1;	}	if (mux_version)		*mux_version = ~param;	return 0;}/* * i8042_enable_mux_ports enables 4 individual AUX ports after * the controller has been switched into Multiplexed mode */static int i8042_enable_mux_ports(struct i8042_values *values){	unsigned char param;	int i;/* * Disable all muxed ports by disabling AUX. */	i8042_ctr |= I8042_CTR_AUXDIS;	i8042_ctr &= ~I8042_CTR_AUXINT;	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {		printk(KERN_ERR "i8042.c: Failed to disable AUX port, can't use MUX.\n");		return -1;

⌨️ 快捷键说明

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