- 浏览: 16343 次
最新评论
Intel8042芯片驱动分析
2011年11月03日
重要提醒:系统检测到您的帐号可能存在被盗风险,请尽快查看风险提示,并立即修改密码。 | 关闭
网易博客安全提醒:系统检测到您当前密码的安全性较低,为了您的账号安全,建议您适时修改密码 立即修改 | 关闭
------------------------------------------ 本文系本站原创,转载请注明出处:http://ericxiao.cublog.cn/----------------------- -------------------
一:intel8042芯片概述
Intel8024是intel公司的一款键盘控制器芯片,它为x86系统中的标准配置.虽然名为键盘控制器,但是鼠标也是由其控制的.
分配给键盘控制器的I/O端口有四个,分别是0x60~0x64.在大部分情况中,只会使用到0x60和0x64.其余0x61~0x64的存在主要是为了兼容XT.可以将0x64看做是状态寄存器.0x60看成是数据寄存器.有时在给键盘控制器下指令的时候,这两个端口都要用到.两者配合来达到下指令与参数的目的.
在微机原理中学过,键盘通常使用IRQ1.鼠标通常使用IRQ12.其它IRQ1和IRQ12都是连接在键盘控制器上的.对应intel8042的两个端口.
Intel8024的指令类型:
1:取得控制器状态.通过读取0x64中的值
2:键盘控制命令:将指令写入0x60中.如果该指令带有参数,也写入到0x60中.通过在20ms内.键盘会给一个应答(0xfa)
3:键盘控制器命令:用来控制键盘控制器.将指令写入0x64.将参数写入0x60中
有关更详细的说明请参阅有关intel 8042芯片的相关手册.
二:架构概述
先来看硬件的连接层次.如下图所示:
红线部份标识的serio总线在硬件上是不存在的.这是Linux内核为了简化串行的输入输出设备添加一个中间层.
如上层所示.串行键盘鼠标等外设都是连接在i8042控制器上的.串行I/O设备的输入输出数据以及设备的控制都是通过i8042进行的.结合我们之前分析过的serio总线代码.serio总线在这里正好为i8042和外设之间提供了一个通信层, 串行外设的驱动也是基于serio结构的.
当i8042检测到一个外部设备,它生成一个serio device.然后将其注册到serio总线.这时就会去匹配注册到serio总线上的serio driver.
当一个中断到来时,i8042会检测serio device设备是否已经关联到驱动,如果已经关联了,直接调用驱动中的中断处理函数,如果没有.手动使device去匹配serio driver.
由此可以看出.这种架构模型极大的简化了驱动的设计,它会串行驱动设计者提供了一个统一的接口.
在接下来的驱动代码分析中,会涉及到我们之前分析过的platform.serio等.
三:intel8042驱动分析
以下的代码分析是基于linux kernel 2.6.25 intel8042的驱动位于linux-2.6.25/drivers/input/serio/ i8042.c
驱动对应的初始化入口如下:
static int __init i8042_init(void)
{
int err;
dbg_init();
err = i8042_platform_init();
if (err)
return err;
err = i8042_controller_check();
if (err)
goto err_platform_exit;
err = platform_driver_register(&i8042_driver);
if (err)
goto err_platform_exit;
i8042_platform_device = platform_device_alloc("i8042", -1);
if (!i8042_platform_device) {
err = -ENOMEM;
goto err_unregister_driver;
}
err = platform_device_add(i8042_platform_device);
if (err)
goto err_free_device;
panic_blink = i8042_panic_blink;
return 0;
err_free_device:
platform_device_put(i8042_platform_device);
err_unregister_driver:
platform_driver_unregister(&i8042_driver);
err_platform_exit:
i8042_platform_exit();
return err;
}
在初始化入口里,调用i8042_platform_init()来进行i8024驱动的一些初始化.然后调用i8042_controller_check()来检查8042芯片是否正常.如果一切正常,注意一个platform总线的驱动和设备.关于platform总线,参考 linux设备模型之platform总线>>
挨个分析初始化入口里所调用的子函数.
i8042_platform_init()用来进行一系统的初始化,代码如下:
static int __init i8042_platform_init(void)
{
int retval;
/*
* On ix86 platforms touching the i8042 data register region can do really
* bad things. Because of this the region is always reserved on ix86 boxes.
*
* if (!request_region(I8042_DATA_REG, 16, "i8042"))
* return -EBUSY;
*/
//键盘通道所对应的IRQ
i8042_kbd_irq = I8042_MAP_IRQ(1);
//鼠标通道所对应的IRQ
i8042_aux_irq = I8042_MAP_IRQ(12);
//PNP选择编译部份
retval = i8042_pnp_init();
if (retval)
return retval;
#if defined(__ia64__)
i8042_reset = 1;
#endif
//DMI选择编译部份
#if defined(__i386__) || defined(__x86_64__)
if (dmi_check_system(i8042_dmi_noloop_table))
i8042_noloop = 1;
if (dmi_check_system(i8042_dmi_nomux_table))
i8042_nomux = 1;
#endif
#ifdef CONFIG_X86
if (dmi_check_system(i8042_dmi_dritek_table))
i8042_dritek = 1;
#endif /* CONFIG_X86 */
return retval;
}
在这里,主要指定了键盘接口和鼠标接口的IRQ.其它部份为选择编译部份,忽略.
i8042_controller_check()用来检查8042是否正常,代码如下:
static int i8042_controller_check(void)
{
if (i8042_flush() == I8042_BUFFER_SIZE) {
printk(KERN_ERR "i8042.c: No controller found.\n");
return -ENODEV;
}
return 0;
}
当i8042_flush()返回I8042_BUFFER_SIZE的时候,会提示末找到8042控制器.看这个函数的名称是刷新什么东西.转进去看看
static int i8042_flush(void)
{
unsigned long flags;
unsigned char data, str;
int i = 0;
spin_lock_irqsave(&i8042_lock, flags);
while (((str = i8042_read_status()) & I8042_STR_OBF) && (i
udelay(50);
data = i8042_read_data();
i++;
dbg("%02x
str & I8042_STR_AUXDATA ? "aux" : "kbd");
}
spin_unlock_irqrestore(&i8042_lock, flags);
return i;
}
从代码中可以看出.读取8042状态寄存器的值,如果返回值含有I8042_STR_OBF位.则延迟之后再读取,这样一直尝试I8042_BUFFER_SIZE次.如果读出来的值一直都包含I8042_STR_OBF位的话,那i8042_controller_check()就会返回错误了.
I8042_STR_OBF定义如下:
#define I8042_STR_OBF 0x01
对应为寄存器的第1位,
对于0x64第1位的定义为:如果此位被置,将表示数据端口0x60有数据.这样,对上面代码的逻辑含义就很好理解了:
如果数据端口一直的数据,就说明此时可能出现了异常
然后在初始化函数里,注册了一个platform 的驱动i8042_driver,如下:
static struct platform_driver i8042_driver = {
.driver = {
.name = "i8042",
.owner = THIS_MODULE,
},
.probe = i8042_probe,
.remove = __devexit_p(i8042_remove),
.shutdown = i8042_shutdown,
#ifdef CONFIG_PM
.suspend = i8042_suspend,
.resume = i8042_resume,
#endif
};
紧接着,又注册了一个名为i8042的platform设备,根据之前研究的platform总线的知识,可得知,进行设备与驱动匹配的时候,首先会判断设备和驱动的name是否相同.在这里是相同的.接着会调用platform driver的probe接口,在这里,这个接口对应为:
i8042_probe().代码分段如下
static int __devinit i8042_probe(struct platform_device *dev)
{
int error;
error = i8042_controller_selftest();
if (error)
return error;
i8042_controller_selftest()用于键盘控控制器的自检.控制器自检的指令为0xaa.若自检成功,返回0x55
error = i8042_controller_init();
if (error)
return error;
进行键盘控制器的初始化。
if (!i8042_noaux) {
error = i8042_setup_aux();
if (error && error != -ENODEV && error != -EBUSY)
goto out_fail;
}
if (!i8042_nokbd) {
error = i8042_setup_kbd();
if (error)
goto out_fail;
}
建立两个通道。一个是aux.为鼠标使用。另一个是kbd.为键盘使用
#ifdef CONFIG_X86
if (i8042_dritek) {
char param = 0x90;
error = i8042_command(¶m, 0x1059);
if (error)
goto out_fail;
}
#endif
/*
* Ok, everything is ready, let's register all serio ports
*/
i8042_register_ports();
注册端口。端口是在建立通道的时候建立的。
return 0;
out_fail:
i8042_free_aux_ports(); /* in case KBD failed but AUX not */
i8042_free_irqs();
i8042_controller_reset();
return error;
}:
I8042设置键盘和鼠标接口的过程非常复杂.我们在下面分段对这两个过程进行讲解.在讲解之前,我们先对i8042_register_ports()有一个了解.代码如下:
static void __devinit i8042_register_ports(void)
{
int i;
for (i = 0; i
if (i8042_ports.serio) {
printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n",
i8042_ports.serio->name,
(unsigned long) I8042_DATA_REG,
(unsigned long) I8042_COMMAND_REG,
i8042_ports.irq);
serio_register_port(i8042_ports.serio);
}
}
}
显然,这段代码是对i8042_ports[]数组中被初始化的项进行处理,调用serio_register_port()将其注册到虚拟总线..
i8042_setup_aux()用来设置鼠标通道.代码如下:
static int __devinit i8042_setup_aux(void)
{
int (*aux_enable)(void);
int error;
int i;
if (i8042_check_aux())
return -ENODEV;
i8042_check_aux()检查8042是否支持aux通道
if (i8042_nomux || i8042_check_mux()) {
error = i8042_create_aux_port(-1);
if (error)
goto err_free_ports;
aux_enable = i8042_enable_aux_port;
} else {
for (i = 0; i
error = i8042_create_aux_port(i);
if (error)
goto err_free_ports;
}
aux_enable = i8042_enable_mux_ports;
}
一般情况下,流程会转入elsa中.即会通过i8042_create_aux_port()在i8042_ports[]数组中进行相关项的初始化.
error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED,
"i8042", i8042_platform_device);
if (error)
goto err_free_ports;
if (aux_enable())
goto err_free_irq;
i8042_aux_irq_registered = 1;
return 0;
为鼠标对应的IRQ设置中断处理例程.并在8042中启用aux
err_free_irq:
free_irq(I8042_AUX_IRQ, i8042_platform_device);
err_free_ports:
i8042_free_aux_ports();
return error;
}
i8042_create_aux_port()代码如下:
static int __devinit i8042_create_aux_port(int idx)
{
struct serio *serio;
int port_no = idx
struct i8042_port *port = &i8042_ports[port_no];
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!serio)
return -ENOMEM;
serio->id.type = SERIO_8042;
serio->write = i8042_aux_write;
serio->start = i8042_start;
serio->stop = i8042_stop;
serio->port_data = port;
serio->dev.parent = &i8042_platform_device->dev;
if (idx
strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name));
strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
} else {
snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx);
snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1);
}
port->serio = serio;
port->mux = idx;
port->irq = I8042_AUX_IRQ;
return 0;
}
就是通过这个接口为aux通道在i8042_ports[]中初始化相关项.
Kdb通道的处理也差不多.代码如下:
static int __devinit i8042_setup_kbd(void)
{
int error;
error = i8042_create_kbd_port();
if (error)
return error;
error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED,
"i8042", i8042_platform_device);
if (error)
goto err_free_port;
error = i8042_enable_kbd_port();
if (error)
goto err_free_irq;
i8042_kbd_irq_registered = 1;
return 0;
err_free_irq:
free_irq(I8042_KBD_IRQ, i8042_platform_device);
err_free_port:
i8042_free_kbd_port();
return error;
}
它先调用i8042_create_kbd_port()在i8042_ports[]初始化关于kdb通道的相关项.然后为键盘IRQ注册中断处理例程,最后在8042芯片中开通此通道.
来流览下i8042_create_kdb_port()的代码:
static int __devinit i8042_create_kbd_port(void)
{
struct serio *serio;
struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO];
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!serio)
return -ENOMEM;
serio->id.type = i8042_direct ? SERIO_8042 : SERIO_8042_XL;
serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write;
serio->start = i8042_start;
serio->stop = i8042_stop;
serio->port_data = port;
serio->dev.parent = &i8042_platform_device->dev;
strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name));
strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
port->serio = serio;
port->irq = I8042_KBD_IRQ;
return 0;
}
最后在调用i8042_register_ports()在serio总线中注册端口的时候,就会将设备与serio中驱动关联起来了.
来看下8024为键盘IRQ和鼠标IRQ注册的两个中断处理例程:
static irqreturn_t i8042_interrupt(int irq, void *dev_id)
{
struct i8042_port *port;
unsigned long flags;
unsigned char str, data;
unsigned int dfl;
unsigned int port_no;
int ret = 1;
spin_lock_irqsave(&i8042_lock, flags);
str = i8042_read_status();
//如果有输出缓存中有数据,0位会被置为1
if (unlikely(~str & I8042_STR_OBF)) {
spin_unlock_irqrestore(&i8042_lock, flags);
if (irq) dbg("Interrupt %d, without any data", irq);
ret = 0;
goto out;
}
data = i8042_read_data();
spin_unlock_irqrestore(&i8042_lock, flags);
if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
static unsigned long last_transmit;
static unsigned char last_str;
dfl = 0;
if (str & I8042_STR_MUXERR) {
dbg("MUX error, status is %02x, data is %02x", str, data);
/*
* When MUXERR condition is signalled the data register can only contain
* 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately
* it is not always the case. Some KBCs also report 0xfc when there is
* nothing connected to the port while others sometimes get confused which
* port the data came from and signal error leaving the data intact. They
* _do not_ revert to legacy mode (actually I've never seen KBC reverting
* to legacy mode yet, when we see one we'll add proper handling).
* Anyway, we process 0xfc, 0xfd, 0xfe and 0xff as timeouts, and for the
* rest assume that the data came from the same serio last byte
* was transmitted (if transmission happened not too long ago).
*/
switch (data) {
default:
if (time_before(jiffies, last_transmit + HZ/10)) {
str = last_str;
break;
}
/* fall through - report timeout */
case 0xfc:
case 0xfd:
case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;
case 0xff: dfl = SERIO_PARITY; data = 0xfe; break;
}
}
port_no = I8042_MUX_PORT_NO + ((str >> 6) & 3);
last_str = str;
last_transmit = jiffies;
} else {
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
port_no = (str & I8042_STR_AUXDATA) ?
I8042_AUX_PORT_NO : I8042_KBD_PORT_NO;
}
port = &i8042_ports[port_no];
dbg("%02x
data, port_no, irq,
dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "");
if (unlikely(i8042_suppress_kbd_ack))
if (port_no == I8042_KBD_PORT_NO &&
(data == 0xfa || data == 0xfe)) {
i8042_suppress_kbd_ack--;
goto out;
}
if (likely(port->exists))
serio_interrupt(port->serio, data, dfl);
out:
return IRQ_RETVAL(ret);
}
在这个处理例程里,它会判断是键盘的IRQ还是鼠标的IRQ.然后转向serio_interrupt().在serio总线中我们分析过这个接口.如果serio设备没有被驱动绑定,则重新扫描一下驱动,否则,调用驱动的interrupt处理函数.
四:小结
在本节里,简单的分析了i8042芯片驱动的代码.本节中所涉及到的控制器驱动架构对其它的总线设备也是类似的,如pci,usb等.相信经过这一节的分析过后,我们对linux的设备模型理解会更新的深刻了.
发表评论
-
评论 (
2012-01-20 11:16 585评论 ( 2011年12月01日 滑雪 ... -
2012届高考英语语法专题复习单项选择详解试题
2012-01-20 11:15 6952012届高考英语语法专题 ... -
2012年管理咨询师考试实务模拟试题2
2012-01-20 11:15 7202012年管理咨询师考试实务模拟试题2 17小时前 管理 ... -
部分经济学术语英文简写
2012-01-20 11:15 1587部分经济学术语英文简写 21小时前 a/c, A/C a ... -
高二地理月测试卷
2012-01-20 11:14 615高二地理月测试卷 20小时前 出题人: 班级 ... -
Linux设备模型---总线、设备、驱动、设备类的相关操作
2012-01-19 16:04 845Linux设备模型---总线、设备、驱动、设备类的相关操作 ... -
Actionscript3 文件上传组件制作
2012-01-19 16:04 952Actionscript3 文件上传组件制作 31分钟前 ... -
常见汇编编译错误中英文对照
2012-01-19 16:04 2075常见汇编编译错误中英文对照 1小时前 FATAL 严重 ... -
Linux input子系统分析【转】
2012-01-19 16:04 955Linux input子系统分析【 ... -
31种方法让你变聪明
2012-01-17 05:56 44431种方法让你变聪明 201 ... -
什么啊.0.000
2012-01-17 05:56 598什么啊.0.000 2011年03月20日 ... -
无法使用千千静听的日子,太不爽了……哪个TMD的在千千网页里放了木马
2012-01-17 05:56 719无法使用千千静听的日子,太不爽了……哪个TMD的在千千网页里放 ... -
TWWWSCAN漏洞扫描器CGI漏洞攻击手册
2012-01-17 05:56 1004TWWWSCAN漏洞扫描器CGI漏洞攻击手册 2011年07 ... -
contentEditable和document.designMode的区别
2012-01-17 05:55 843contentEditable和document.design ... -
夏商与西周》第四十二章之昭王伐楚(二)
2012-01-16 04:42 529夏商与西周》第四十二章之昭王伐楚(二) 2012年01月08 ... -
稼领日志贫巳邢蔗
2012-01-16 04:42 608稼领日志贫巳邢蔗 2012 ... -
龄欣哔日志蹈诒刺陕疥赤
2012-01-16 04:42 540龄欣哔日志蹈诒刺陕疥赤 2012年01月09日 ... -
古玩辨伪之青铜器
2012-01-16 04:42 548古玩辨伪之青铜器 2012年01月09日 ... -
日本法定假日简介
2012-01-16 04:42 392日本法定假日简介 2012 ...
相关推荐
Intel英特尔芯片组Intel Chipset Device Software驱动9.3.0.1019版For WinXP-32/WinXP-64/Vista-32/Vista-64/Win7-32/Win7-64(2012年1月30日发布) Intel的芯片组主要解决主板芯片组的PCI和USB驱动安装问题,不包含...
芯片组 INF 实用程序 Windows 10,32 位* Windows 10,64 位* Windows Server 2019* Windows Server 2016* Windows* Server 2012 R2 适用于 Intel® B360 Chipset ...英特尔® 芯片组软件安装实用程序
intelh61主板驱动是由intel官方推出的一款非常重要的主板驱动程序,用于解决intelh61主板驱动缺失和损坏的问题,欢迎有需要的朋友下载使用!官方介绍intelh61是一款CPU处理器,适用于台式电脑。可扩展性成本优化的高...
英特尔C600芯片组驱动,用于戴尔服务器主板芯片组驱动安装
Intel英特尔芯片组IntelChipsetDeviceSoftware驱动最新8.4.0.1016官方正式版ForWin2000/XP/2003/XP-64/2003-64/Vista/Vista-64(2007年9月5日发布)英特尔发布新款硬件产品的速度是有目共睹的,最新的科技产品虽然让...
英特尔HM77芯片组主板intel(hm77)驱动
01 INTEL 芯片组驱动程序 02 Intel Chipset Driver 03 INTEL 板载显卡驱动程序 for WinXP 04 NVIDIA显卡驱动程序 for WinXP 05 REALTEK 声卡驱动程序 for WINXP 06 REALTEK网卡驱动程序 07 商用键盘程序forWinXP
USB 3.0 可扩展主机控制器驱动程序包含以下 Intel? 芯片组和 Intel? 处理器支持 ︰ ·Intel? 7 系列 Chipsets/Intel? C216 芯片组 ·第三代 Intel? Core? 处理器家族 ·第二代 Intel? Core? i3 处理器 ·第二代 ...
EPoX主板 Intel英特尔芯片组驱动:Intel Chipset Software Installation Utility驱动6.3.0.1007版For Win98SE/ME/2000/XP(2005年1月20日发布) 具体支持型号如下:INTEL 810/820/815/850/845/848/865/875/915/925.....
intel英特尔g系列芯片组显示驱动适用于Intel英特尔G41/G43/G45/Q43/Q45/GL40/GM45/GS45/PentiumG6950/Corei3/Corei5/Corei7系列芯片组显示驱动,是非常好用的主板驱动,需要的朋友赶紧下载吧驱动说明目前Intel的集成...
Chipset英特尔芯片组主板驱动
Intel英特尔芯片组Intel Chipset Device Software驱动最新9.1.0.1012官方正式版For Win2000/XP/2003/XP-64/2003-64/Vista/Vista-64/2008/2008-64(2009年1月13日发布)Intel Chipset Device Software驱动是英特尔...
p35 主板芯片驱动 有急需intel p35主板芯片驱动的朋友过来下载!
英特尔G系列芯片组显卡驱动是一款专门为Intel英特尔g系列显卡打造的驱动程序,这款驱动程序支持IntelG35/G33/G31/G945/G965/Q963/Q965/GM965系列芯片组集成显卡,有需要的朋友欢迎下载使用!英特尔G系列芯片组介绍在...
Intel英特尔芯片组Intel Chipset Device Software驱动9.4.0.1027版For WinXP-32/WinXP-64/Vista-32/Vista-64/Win7-32/Win7-64/Win8-32/Win8-64/Win8.1-32/Win8.1-64(2013年9月5日发布) Intel芯片组驱动更新速度...
intel芯片组驱动是一款好用的英特尔芯片组设备软件,可以轻松准确的识别intel芯片组,让主板性能更好的发挥,欢迎有需要的朋友下载使用!官方介绍英特尔芯片组设备软件是Intel官方提供的芯片组驱动,适用于WinXP32位...
Software\声卡驱动\Intel英特尔芯片
英特尔4系芯片组显卡驱动是一款专门针对英特尔4系列cpu开发的电脑驱动,以便配合cpu正常工作,同时还能够保证该系列cpu保持正常...Intel的4系芯片组介绍Intel的4系芯片组绝对可以称得上Intel芯片组家族中,欢迎下载体验
Intel Chipset Device Software驱动是英特尔旗下所有驱动程序中最重要的一环,是英特尔芯片组稳定运行的基石。对于使用英特尔芯片组的用户来说,当安装完操作系统后,首先需要安装的就是此款驱动,而后才能安装其它...
intel芯片主板使用的驱动程序,通用型。