linux设备驱动之键盘驱动分析 - linux设备模型 - Linux内核学习
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
.disconnect = atkbd_disconnect,
.cleanup = atkbd_cleanup,
};
先来看一下它的id_table成员.这个成员决定着serio port与serio driver是否匹配成功.如下:
static struct serio_device_id atkbd_serio_ids[] = {
…… serio _ register_ po rt(i80 4 2_ po rts[i].serio ); …… } 将这个serio 注册到了总线. 根据我们之前分析的serio总线的相关知识.注册serio port的时候会产生serio port与serio driver的匹配事件 那serio driver在什么地方呢? 这就是今天要讨论的键盘驱动了.
switch (serio->id.type) {
case SERIO_8042_XL: atkbd->translated = 1;
case SERIO_8042: if (serio->write) atkbd->write = 1; break;
}
atkbd->softraw = atkbd_softraw; atkbd->softrepeat = atkbd_softrepeat; atkbd->scroll = atkbd_scroll;
} else { atkbd->set = 2; atkbd->id = 0xab00;
}
atkbd_ set_keyco de_ table(atkbd); atkbd_set_device_attrs(atkbd);
err = sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group); if (err)
-----------------------------------------本文系本站原创,欢迎转载! 转载请注明出处:http://ericxiao.cublog.cn/ ------------------------------------------
一:前言: 在分析intel8042芯片驱动的时候,对其中断处理的后续流程还没有讨论完.在本章以键盘通道为索引讲述intel8042的后续处理,这部份内容 实际上是独立的键盘驱动部份,多种型号的键盘都有自己的驱动程序,但原理都是一样的,都承接着intel8042芯片的后续处理.所以在这里为了 讨论的方便,将其以单独小节的方式给出分析. 下面以基于2.6.25kernel的atkbd.c键盘驱动为例进行分析.
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;
goto fail1;
atkbd->dev = dev; ps2_init(&atkbd->ps2dev, serio); INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work); mutex_init(&atkbd->event_mutex);
fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL);
fail1: input_free_device(dev);
kfree(atkbd);
return err;
} 这段代码第一次看的时候可能觉得比较恐怖,很多陌生的接口,其实,这里面处理的事情并不多,首先它调用atkbd_ set_ keyco de_ table()根 据设备的类型选择一套扫描码.什么叫扫描码?扫描码就是指根据从intel8042中读取到的数据转换为我们所用的字符的过程.例如,在第一套 扫描码中,数字1的扫描码就是0x2.具体的知识请查阅相关资料,在这里不做详细分析,值得注意的是.,在这里.键盘的类型通常为 SERIO_8042_XL型,因为在intel8042驱动中,默认是不支持i8042_direct.的 然后申请一个input_dev. Input_dev是输出子系统的一个概念.所谓输出子系统,是处理I/O设备与上层应用的一个中间层,它接收下层驱动的 相关事件(例如键盘按键,鼠标移动等)发送到上层.这个子系统我们下一节再给出详细的分析,在这里只要知道它的概念就可以了. 接下来,调用input_register_device()将input_dev注册到了输入子系统,注意在这之前还会调用atkbd_set_device_attrs()设置iput_dev 的相关属性,在这一节里,这并不是我们所分析的重点.在此不详细讨论.
goto fail3;
atkbd_enable(atkbd);
err = input_register_device(atkbd->dev); if (err)
goto fail4;
return 0;
fail4: sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
三:键盘驱动入口 在atkbd.c中,module的入口函数为:
static int __init atkbd_init(void)
…chinaunix.net/…/showart_1089643.h…
1/6
2011/5/26
linux设备驱动之键盘驱动分析 - linux设…
{
dmi_check_system(atkbd_dmi_quirk_table);
2011/5/26
博客首页 注册 建议与交流 排行榜 加入友情链接
linux设备驱动之键盘驱动分析 - linux设…
推荐 投诉 搜索:
搜索 帮助
Linux内核学习
戒骄,戒躁,静心,静气......
ericxiao .cublo g.cn
首页
文章
相册
音乐
博客圈 收藏夹
留言 发表文章
linux设备驱动之键盘驱动分析
return serio_register_driver(&atkbd_drv);
}
在这个初始化函数里,注册了一个serio driver.即atkbd_drv.这就是上在讨论的serio driver的由来了.它的结构如下:
static struct serio_driver atkbd_drv = {
i80 4 2_ create_kbd_ po rt()函数中,对serio 的id成员赋值如下:
serio ->id.type
= i8042_direct ? SERIO_8042 : SERIO_8042_XL;
也就是说,由i8042产生的serio类型为SERIO_8042或者 SERIO_8042_XL.都是适用这个驱动的.
二:intel8042中断处理回顾 记得在intel8042的i8042_probe()处理的中断处理中,注册了几个serio port.以kbd通道为例,再次将其列出. static int __devinit i8042_probe (void) {
…… i8042_setup_kbd () …… i8042_register_ports() …… } 在i8042_setup_kbd () -> i8042_create_kbd_port()中: static int __devinit i8042_create_kbd_port(void) { struct serio *serio; struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO];
if (atkbd->softrepeat) atkbd->softraw = 1;
…chinaunix.net/…/showart_1089643.h…
2/6
2011/5/26
linux设备驱动之键盘驱动分析 - linux设…
serio_set_drvdata(serio, atkbd);
err = serio_open(serio, drv); if (err)
.driver = {
.name = "atkbd",
},
.description = DRIVER_DESC,
.id_table = atkbd_serio_ids,
.interrupt = atkbd_interrupt,
.connect = atkbd_connect,
.reconnect = atkbd_reconnect,
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));
就这样,我们找到了由intel8042产生serio port对应的驱动了.
四:serio drver的connect函数
在之前分析的serio总线中得知.如果serio port与驱动匹配成功,就会调用驱动的connect函数,在我们分析的这个键盘驱动里,这个成员函数
对应是atkbd_connect().代码如下:
static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct atkbd *atkbd;
struct input_dev *dev;
int err = -ENOMEM;
atkbd = kzalloc(sizeof(struct atkbd), GFP_KERNEL); dev = input_allocate_device(); if (!atkbd || !dev)
strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
port->serio = serio; port->irq = I8042_KBD_IRQ;
return 0; } 初始化了一个serio 结构. 在i8 04 2 _register_po rts()中: static void __devinit i8042_register_ports(void) {
{
.type = SERIO_8042,
.proto = SERIO_ANY,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{
.type = SERIO_8042_XL,
.proto = SERIO_ANY,
.id = SERIO_ANY,
.extra = SERIO_ANY,
五:键盘的中断处理 在intel8042接收到中断之后,会调用驱动的interrupt接口来处理数据,在这里,这个接口为atkbd_interrupt().代码如下: static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
goto fail2;
if (atkbd->write) {
if (atkbd_probe(atkbd)) { err = -ENODEV; goto fail3;
}
atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra); atkbd_activate(atkbd);
},
{
.type = SERIO_RS232,
.proto = SERIO_PS2SER,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{0}
};
由此看出,所有类型为SERIO_8042, SERIO_8042_XL, SERIO_RS232的serio port都适用于此驱动.回顾前面分析的