- 论坛徽章:
- 0
|
最近公司的键盘驱动出了点问题,便看起了Red Hat 9 里键盘驱动的实现:
编写硬件相关的初始化程序,以获得的扫描码为参数调用drivers/char/keyboard.c中的handle_scancode函数。
以上是基于linux 2.4的键盘驱动实现。
由于增加了输入子系统,2.6版本的内核在编写键盘驱动上似乎不那么明朗了。这几天看了下输入子系统的实现,虽然研究得不够深入,但理清了输入子系统的层次关系,算是一个小收获。
拿键盘来说,整个输入子系统的架构如下:
keyboard handler
|
|
|
|
input core
|
|
|
|
device driver
很明显,这里的主角是input core。下面,我为你慢慢解释整个输入子系统的核心到底都干了些什么“你不知道的事”......
它主要位于drivers/input/input.c
涉及到的主要数据结构:
struct input_handler *handler,struct input_dev *dev,struct input_handle *handle(这个和前面那家伙有点像,也只是长得有点像而已......)
input.c维护着整个子系统的事件注册,管理以及分发,是整个子系统的精华所在。
如果你配置了CONFIG_VT(没有不配置这个的吧...?)在tty模块初始化(也就是在tty_init函数里,位于drivers/char/tty_io.c)的时候会调用vty_init():
static int __init tty_init(void)
{
cdev_init(&tty_cdev, &tty_fops);
if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") #ifdef CONFIG_VT
cdev_init(&vc0_cdev, &console_fops);
if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0")
return 0;
}
module_init(tty_init);
下面是 [color="#ff0000"] vty_init()位于drivers/char/vt.c[color="#ff0000"]:
int __init vty_init(void)
{
vcs_init();
…………
…………
[color="#ff0000"] [color="#ff0000"]kbd_init();
console_map_init();
#ifdef CONFIG_PROM_CONSOLE
prom_con_init();
#endif
#ifdef CONFIG_MDA_CONSOLE
mda_console_init();
#endif
return 0;
}
你看到什么了?哈哈,就是那个kdb_init,位于drivers/char/keyboard.c的家伙:
在这里,我们初始化了子系统上层事件处理层模块:
int __init kbd_init(void)
{
int i;
int error;
for (i = 0; i [color="#ff0000"]error = input_register_handler(&kbd_handler);
if (error)
return error;
tasklet_enable(&keyboard_tasklet);
tasklet_schedule(&keyboard_tasklet);
return 0;
}
input_handler是输入设备事件接口的实现,定义于include/linux/input.h :
/**
* struct input_handler - implements one of interfaces for input devices
* @private: driver-specific data
* @event: event handler
* @connect: called when attaching a handler to an input device
* @disconnect: disconnects a handler from input device
* @start: starts handler for given handle. This function is called by
* input core right after connect() method and also when a process
* that "grabbed" a device releases it
* @fops: file operations this driver implements
* @minor: beginning of range of 32 minors for devices this driver
* can provide
* @name: name of the handler, to be shown in /proc/bus/input/handlers
* @id_table: pointer to a table of input_device_ids this driver can
* handle
* @blacklist: prointer to a table of input_device_ids this driver should
* ignore even if they match @id_table
* @h_list: list of input handles associated with the handler
* @node: for placing the driver onto input_handler_list
*/
struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
const struct file_operations *fops;
int minor;
const char *name;
const struct input_device_id *id_table;
const struct input_device_id *blacklist;
struct list_head h_list;
struct list_head node;
};
对于我们的键盘,按键事件的处理可都在这个家伙里面啊!
input_register_handler是子系统中的一个非常重要的函数,定义于drivers/input/input.c
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5])
return -EBUSY;
input_table[handler->minor >> 5] = handler;
}
//将注册的handler加入到输入子系统维护的input_handler_list链表
list_add_tail(&handler->node, &input_handler_list);
//针对子系统中的设备链表,则遍历并试图匹配
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
return 0;
}
EXPORT_SYMBOL(input_register_handler);
在说input_attach_handler这个大腕的前面,我们先点一下那个叫input_table的数组,这是一个在本文件里定义的一个
struct input_handler结构的数组,我们在注册struct
input_handler的时候会根据设备的次设备号对32的倍数做为下标,将其input_handler放入数组.为什么是32?为什么要对32
除?
在此文件开始处有一个宏定义#define INPUT_DEVICES 256我们的子系统最多支持256个设备,而我们的input_handler结构的数组最多处理8类事件,所以分给每一类的次设备号段就是32个了。
input_attach_handler定义于drivers/input/input.c :
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error;
//在这里匹配主要是调用 input_match_device来进行的,但是handler结构体里面还有个"黑名单"字段,意思是说虽然咱俩认识,但是你在我的黑名单里,照样拜拜...
if (handler->blacklist && input_match_device(handler->blacklist, dev))
return -ENODEV;
//handler结构体里的id_table指针表明了可以与此驱动联姻的设备种类,根据这个字段我们调用这个函数
id = input_match_device(handler->id_table, dev);
if (!id)
return -ENODEV;
//这个函数如果返回不为空那么我们就为他们正式举行婚礼...而此时,我们调用的这个connect就是在keyboard.从中定义的那个handler结构体中的那个connect函数.是不是有点明白了?
error = handler->connect(handler, dev, id);
//结婚也有逃婚的...
if (error && error != -ENODEV)
printk(KERN_ERR
"input: failed to attach handler %s to device %s, "
"error: %d\n",
handler->name, kobject_name(&dev->cdev.kobj), error);
return error;
}
我们来看一下匹配是如何进行的,此函数在同一文件中:
static const struct input_device_id *input_match_device(const struct input_device_id *id,
struct input_dev *dev)
{
int i;
for (; id->flags || id->driver_info; id++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
if (id->bustype != dev->id.bustype)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
if (id->vendor != dev->id.vendor)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
if (id->product != dev->id.product)
continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
if (id->version != dev->id.version)
continue;
MATCH_BIT(evbit, EV_MAX);
MATCH_BIT(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
MATCH_BIT(absbit, ABS_MAX);
MATCH_BIT(mscbit, MSC_MAX);
MATCH_BIT(ledbit, LED_MAX);
MATCH_BIT(sndbit, SND_MAX);
MATCH_BIT(ffbit, FF_MAX);
MATCH_BIT(swbit, SW_MAX);
return id;
}
return NULL;
}
MATCH_BIT宏:
#define MATCH_BIT(bit, max) \
for (i = 0; i bit & dev->bit) != id->bit) \
break; \
if (i != NBITS(max)) \
continue;
这个宏的意思是,对于struct input_device_id中的每个数组,如果其中的元素某个位置位,input_dev中的相应数组的相应元素相应位也要置位,否则就不能匹配.
struct input_dev定义于input.h:
struct input_dev {
void *private;
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
/*
* 这里是根据输入信号的类型建立的各种数组,
* 数组的每1bit代表一种信号类型,
*/
unsigned long evbit[NBITS(EV_MAX)];
unsigned long keybit[NBITS(KEY_MAX)];
unsigned long relbit[NBITS(REL_MAX)];
unsigned long absbit[NBITS(ABS_MAX)];
unsigned long mscbit[NBITS(MSC_MAX)];
unsigned long ledbit[NBITS(LED_MAX)];
unsigned long sndbit[NBITS(SND_MAX)];
unsigned long ffbit[NBITS(FF_MAX)];
unsigned long swbit[NBITS(SW_MAX)];
unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);
struct ff_device *ff;
unsigned int repeat_key;
struct timer_list timer;
int state;
int sync;
int abs[ABS_MAX + 1];
int rep[REP_MAX + 1];
unsigned long key[NBITS(KEY_MAX)];
unsigned long led[NBITS(LED_MAX)];
unsigned long snd[NBITS(SND_MAX)];
unsigned long sw[NBITS(SW_MAX)];
int absmax[ABS_MAX + 1];
int absmin[ABS_MAX + 1];
int absfuzz[ABS_MAX + 1];
int absflat[ABS_MAX + 1];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle *grab;
struct mutex mutex; /* serializes open and close operations */
unsigned int users;
struct class_device cdev;
union { /* temporarily so while we switching to struct device */
struct device *parent;
} dev;
struct list_head h_list;
struct list_head node;
};
这个结构体是以链表的形式存在于输入子系统中,也就是struct input_handler千辛万苦要寻找的另一半.
当这一切都成功地完成了以后,handler中的connect函数调用:
/*
* When a keyboard (or other input device) is found, the kbd_connect
* function is called. The function then looks at the device, and if it
* likes it, it can open it and get events from it. In this (kbd_connect)
* function, we should decide which VT to bind that keyboard to initially.
*/
static int kbd_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct input_handle *handle;
int error;
int i;
for (i = KEY_RESERVED; i keybit))
break;
if (i == BTN_MISC && !test_bit(EV_SND, dev->evbit))
return -ENODEV;
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
handle->dev = dev;
handle->handler = handler;
handle->name = "kbd";
error = input_register_handle(handle);
if (error)
goto err_free_handle;
error = input_open_device(handle);
if (error)
goto err_unregister_handle;
return 0;
err_unregister_handle:
input_unregister_handle(handle);
err_free_handle:
kfree(handle);
return error;
}
这个函数流程很清晰,初始化并注册一个struct input_handle,然后调用input_open_device(handle).
还记得上面我说struct input_handle和struct input_handler很像么?
struct input_handle就是用来连接struct input_handler和struct input_dev的结构.
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
//将此结构放入相应的handler和dev的h_list链表末尾
//一个handle结构对应一个handler,而一个handler不一定对应一个handle
list_add_tail(&handle->d_node, &handle->dev->h_list);
list_add_tail(&handle->h_node, &handler->h_list);
//这里还要检查下start函数,不为空在这里要被调用
if (handler->start)
handler->start(handle);
return 0;
}
input_open_device定义于input.c:
int input_open_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
int err;
err = mutex_lock_interruptible(&dev->mutex);
if (err)
return err;
handle->open++;
//如果是第一次打开,dev->open不为空就调用设备的open函数
if (!dev->users++ && dev->open)
err = dev->open(dev);
if (err)
handle->open--;
mutex_unlock(&dev->mutex);
return err;
}
到了这一步,可以说从handler与dev的配对已经完成了,我们也搞清楚了keyboard handler与input core之间的联系,想必大家现在对输入子系统已经有了一个框架的概念了吧?
但是,你有没有想过如果input_dev_list中的元素是怎么来的?也就是说假如我们只初始化一个handler,而没有相应的input_dev,那不是一点意义都没有?
虽然我们在以键盘的眼光在看输入子系统,可是我们对按键中断,键值的处理到现在还没有给出一个概念,我们的子系统到现在也没有与实实在在的键盘驱动联系起来.
下面我们将以普通PC上的PS/2接口的键盘驱动为切入点来一窥整个子系统运转的真正面目.
郁闷,竟然说我的文章太长,没办法,分开吧...
关于键盘么,我们插点背景来娱乐一下,就像听着许茹芸的声音,总会感觉很舒服:
我们常用的键盘一般包括:
USB 键盘 - 最近为所有的新机器所支持(Macintosh and IBM/compatible).
IBM/兼容 键盘 - 也称 "AT keyboards" 或者 "PS/2 keyboards", 现代pc都支持. 本文的主题.
ADB 键盘 - Apple Desktop Bus of older Macintosh systems.
原来的IBM 以及兼容机使用一种称作 "XT "的键盘. 现在不多见了,我们不介绍.后来IBM引入 AT 系统, AT之后是IBM PS/2.
AT 键盘和 PS/2 键盘类似,是我们常用的键盘. PS/2 设备使用更小的连接器,支持更多一点的特性. 同时兼容AT.
而说到键盘驱动,我们就不能不提那个Intel 的8042控制器.
键盘包含一个由 keys组成的矩阵. 所有的键都为一个板上处理器监控,称作键盘编码器, (一般是i8048? 见下表).虽然这种芯片挺多,但是其职
能基本如下:
监控是哪个或那几个键被按下/释放,把相应的数据送到主板. 这个处理器处理所有的 debouncing(?what!)
,把数据缓存到他的16-byte 的缓冲区中. 在IBM兼容机上,主板也有一个板上芯片,称作键盘控制器.一般是8042(就是我们说的那个了).
他负责解码从键盘来的信息,通知系统软件各种事件.在host 和主板的通讯中 都使用IBM 协议.
[color="#008000"] 现代键盘的encoders:
[color="#0000ff"]Holtek: HT82K28A, HT82K628A, HT82K68A, HT82K68E?EMC: EM83050, EM83050H, EM83052H, EM83053H,?Intel: 8048, 8049
[color="#0000ff"]Motorola: 6868, 68HC11, 6805
[color="#0000ff"]Zilog: Z8602, Z8614, Z8615, Z86C15, Z86E23
键盘处理器(encoder)大部分时间在"扫描", 监视着键矩阵.
一旦发现有键被按下,释放,或被按住不放,encoder就会向计算机发送一个数据包,称为扫描码. 有两种不同的扫描码, "make codes"
和 "break codes". make code 是键被按下,按住不放是产生的. break code 是键被释放时产生的.
每个键都有自己唯一的make code 和 break code. make code 和 break codes 的集合称为扫描码集.
共有三种标准的扫描码集.所有现代的键盘默认使用扫描码集 set 2.
以上的讨论都是针对硬件,其实,如果写一个底层的键盘相关的软件for PC,是不该直接和键盘通信的.
主板上一般都有键盘控制器,它在键盘和外设总线间是一个接口. 这个控制器处理 signal-level的东东和协议的细节
,同时提供转换,解释,处理扫描码,执行命令的功能.
PC 的键盘一般使用Intel 8042/兼容 的微控制器.现代计算机上,这个功能一般集成到南桥 . 然而,这个设备逻辑上仍然叫做 "the
8042".基于主板的不同,键盘控制器可以工作于:"AT-兼容" 模式, 或者 "PS/2-兼容" 模式. 如果主板支持 PS/2
鼠标就会使用后者. 这时, 8042 既是键盘控制器又是鼠标控制器. 键盘控制器根据硬连线的方式自动决定工作于哪种模式.
在看驱动之前,我们很有必要看一下目录drivers/input/keyboard下面的Kconfig,就像复旦人甲说的那样,这玩意的作用就像路标:
config KEYBOARD_ATKBD
tristate "AT keyboard" if EMBEDDED || !X86_PC
default y
select SERIO
select SERIO_LIBPS2
select SERIO_I8042 if X86_PC
select SERIO_GSCPS2 if GSC
help
Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
you'll need this, unless you have a different type keyboard (USB, ADB
or other). This also works for AT and PS/2 keyboards connected over a
PS/2 to serial converter.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called atkbd.
这个就是我们需要的,根据相应的makefile中的内容,我们知道目标就是drivers/input/keyboard/atkbd.c.
看看他的初始化:
static int __init atkbd_init(void)
{
return serio_register_driver(&atkbd_drv);
}
static void __exit atkbd_exit(void)
{
serio_unregister_driver(&atkbd_drv);
}
module_init(atkbd_init);
module_exit(atkbd_exit);
哇哇,超级简单,可简单的背后,却不一定能看到简单的实质.
static struct serio_driver atkbd_drv = {
.driver = {
.name = "atkbd",
},
.description = DRIVER_DESC,
.id_table = atkbd_serio_ids,
.interrupt = atkbd_interrupt,
.connect = atkbd_connect,
.reconnect = atkbd_reconnect,
.disconnect = atkbd_disconnect,
.cleanup = atkbd_cleanup,
};
像键盘,鼠标,触摸屏的底层驱动,一般都是用这个struct serio_driver来实现(内部封装了一个device_driver结构).
serio_register_driver定义于serio.h:
static inline int serio_register_driver(struct serio_driver *drv)
{
return __serio_register_driver(drv, THIS_MODULE, KBUILD_MODNAME);
}
__serio_register_driver定义于serio.c:
int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name)
{
//struct serio_driver结构中的一个字段,用来指明与设备绑定是手动还是自动
int manual_bind = drv->manual_bind;
int error;
//驱动的总线类型是serio_bus
drv->driver.bus = &serio_bus;
drv->driver.owner = owner;
drv->driver.mod_name = mod_name;
/*
* Temporarily disable automatic binding because probing
* takes long time and we are better off doing it in kseriod
*/
drv->manual_bind = 1;
//注册驱动
error = driver_register(&drv->driver);
if (error) {
printk(KERN_ERR
"serio: driver_register() failed for %s, error: %d\n",
drv->driver.name, error);
return error;
}
/*
* Restore original bind mode and let kseriod bind the
* driver to free ports
*/
//看到英文注释了吧?关于kseriod这个内核线程,我们会在下面看到
if (!manual_bind) {
drv->manual_bind = 0;
error = serio_queue_event(drv, NULL, SERIO_ATTACH_DRIVER);
if (error) {
driver_unregister(&drv->driver);
return error;
}
}
return 0;
}
很
明显,我们下一步要看的就是那个driver_register.其实很多东西都是后来慢慢才有体会.我记得当时看linux那些事的时候,对这些总线,
驱动,设备基本不懂,看着文章好玩,可看完又没能体会其中真正的意义.慢慢地,内核逐渐熟悉了以后,才逐渐明白这个模型.
就如同他们说的那样,总
线是一条主线,上面挂着2条子链,一条是设备的,一条是驱动的.他们3者的结构体里面都分别有指向对方的指针.每当有一个驱动要挂到总线的驱动链上去,他
都会到总线的设备链上一一查看,看是否有能和自己"配对"的设备,像这个操作,一般都是由函数名里面包含probe这样的函数完成的.设备也是如此.
当明白了这个模型以后,我们再看代码就会发现清晰了很多.
"我们不光要看代码,还要看代码背后的哲学"
driver_register定义于driver.c
/**
* driver_register - register driver with bus
* @drv: driver to register
*
* We pass off most of the work to the bus_add_driver() call,
* since most of the things we have to do deal with the bus
* structures.
*/
int driver_register(struct device_driver * drv)
{
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown)) {
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
}
klist_init(&drv->klist_devices, NULL, NULL);
return bus_add_driver(drv);
}
英文注释说的很明白他的主要功能是由bus_add_driver这个函数完成的,定义于bus.c:
int bus_add_driver(struct device_driver *drv)
{
struct bus_type * bus = get_bus(drv->bus);
int error = 0;
if (!bus)
return -EINVAL;
//针对基类kobject以及sysfs的操作(推荐看linux那些事之--我是sysfs)
pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
error = kobject_set_name(&drv->kobj, "%s", drv->name);
if (error)
goto out_put_bus;
drv->kobj.kset = &bus->drivers;
if ((error = kobject_register(&drv->kobj)))
goto out_put_bus;
//在注册serio_bus的时候,这个字段被置为1
if (drv->bus->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
//将驱动挂到总线上
klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
module_add_driver(drv->owner, drv);
//设置属性
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__FUNCTION__, drv->name);
}
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__FUNCTION__, drv->name);
}
return error;
out_unregister:
kobject_unregister(&drv->kobj);
out_put_bus:
put_bus(bus);
return error;
}
大家都是明眼人,知道我们的重点是要说driver_attach(drv):
/**
* driver_attach - try to bind driver to devices.
* @drv: driver.
*
* Walk the list of devices that the bus has on it and try to
* match the driver with each one. If driver_probe_device()
* returns 0 and the @dev->driver is set, we've found a
* compatible pair.
*/
int driver_attach(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
简洁!明了!
/**
* bus_for_each_dev - device iterator.
* @bus: bus type.
* @start: device to start iterating from.
* @data: data for the callback.
* @fn: function to be called for each device.
*
* Iterate over @bus's list of devices, and call @fn for each,
* passing it @data. If @start is not NULL, we use that device to
* begin iterating from.
*
* We check the return of @fn each time. If it returns anything
* other than 0, we break out and return that value.
*
* NOTE: The device that returns a non-zero value is not retained
* in any way, nor is its refcount incremented. If the caller needs
* to retain this data, it should do, and increment the reference
* count in the supplied callback.
*/
int bus_for_each_dev(struct bus_type * bus, struct device * start,
void * data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device * dev;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->klist_devices, &i,
(start ? &start->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
说
实话,我对内核里那些有关链表的宏实在是很佩服,我估计把这些宏拿出来,应该不比STL差吧?还有那些红黑树,堆排序...本科的时候天天听老师说数据结
构是多么多么重要,一直没当回事,直到失去才后悔莫及...如果给我一个再来一次的机会,我会问一句,那些写内核的是不是小学就开始学数据结构了?
这个函数就是对总线上的每个设备都执行fn(dev, data),也就是__driver_attach这个函数
static int __driver_attach(struct device * dev, void * data)
{
struct device_driver * drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
//在这里,我们获得了设备的信号量,下面我们会讲到
down(&dev->sem);
//哟......还单身着哪?师太,你就从了老衲吧...
if (!dev->driver)
driver_probe_device(drv, dev);
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}
/**
* driver_probe_device - attempt to bind device & driver together
* @drv: driver to bind a device to
* @dev: device to try to bind to the driver
*
* First, we call the bus's match function, if one present, which should
* compare the device IDs the driver supports with the device IDs of the
* device. Note we don't do this ourselves because we don't know the
* format of the ID structures, nor what is to be considered a match and
* what is not.
*
* This function returns 1 if a match is found, -ENODEV if the device is
* not registered, and 0 otherwise.
*
* This function must be called with @dev->sem held. When called for a
* USB interface, @dev->parent->sem must be held as well.
*/
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
//从不从得看咱俩合不合适,俺虽然不算沉鱼落雁,但也不是人人都跟的哈
if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
pr_debug("%s: Matched Device %s with Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
[color="#ff0000"] ret = really_probe(dev, drv);
done:
return ret;
}
注释是好东西啊,连怎么用都给你说好了,看着啊,调用之前我们一定要获得设备的信号量,我们在刚才获得信号量的时候提了一下.然后,回调了serio_bus的match函数,要记住,我们之前所做的每一步都在为未来做铺垫...这句话是不是傻了点?
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("%s: Probing driver %s with device %s\n",
drv->bus->name, drv->name, dev->bus_id);
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__FUNCTION__, dev->bus_id);
goto probe_failed;
}
//总线或者驱动里有探测函数的,我们就要探测下,此时也就回调了驱动或者总线里的probe函数,像我们键盘里的atkbd_connect,就会在
probe里调用,到这里,我们才算完成了驱动和设备的匹配,所以说啊,感情的路上一直都是很坎坷的,配个对还那么麻烦...
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug("%s: Bound Device %s to Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev->bus_id, ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
如果出了错,就要进行一些清理工作,释放资源,sysfs也不会再容你存在,相应的计数也要平衡.
我们来看一下dev->bus->probe:
static int serio_driver_probe(struct device *dev)
{
struct serio *serio = to_serio_port(dev);
struct serio_driver *drv = to_serio_driver(dev->driver);
return [color="#ff0000"]serio_connect_driver(serio, drv);
}
这里调用了一个serio_connect_driver:
static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
{
int retval;
mutex_lock(&serio->drv_mutex);
[color="#ff0000"]retval = drv->connect(serio, drv);
mutex_unlock(&serio->drv_mutex);
return retval;
}
恩恩,千呼万唤始出来,我们的 static struct serio_driver atkbd_drv = {
.driver = {
.name = "atkbd",
},
.description = DRIVER_DESC,
.id_table = atkbd_serio_ids,
.interrupt = atkbd_interrupt,
.connect = atkbd_connect,
.reconnect = atkbd_reconnect,
.disconnect = atkbd_disconnect,
.cleanup = atkbd_cleanup,
};
结构里的atkbd_connect终于在这里被调用了:
static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
{
struct atkbd *atkbd;
struct input_dev *dev;
int err = -ENOMEM;
//1.分配并清零一个struct atkbd结构
//2.分配并初始化一个input_dev结构
atkbd = kzalloc(sizeof(struct atkbd), GFP_KERNEL);
dev = input_allocate_device();
if (!atkbd || !dev)
goto fail1;
atkbd->dev = dev;
ps2_init(&atkbd->ps2dev, serio);
INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work);
mutex_init(&atkbd->event_mutex);
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;
if (atkbd->softrepeat)
atkbd->softraw = 1;
serio_set_drvdata(serio, atkbd);
err = serio_open(serio, drv);
if (err)
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);
} else {
atkbd->set = 2;
atkbd->id = 0xab00;
}
atkbd_set_keycode_table(atkbd);
//在这里对atkbd->dev进行进一步填充,设置其属性(不然你拿上面那个几乎空白的input_dev跟谁匹配去啊...)
atkbd_set_device_attrs(atkbd);
err = sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group);
if (err)
goto fail3;
atkbd_enable(atkbd);
err = [color="#ff0000"]input_register_device(atkbd->dev);
if (err)
goto fail4;
return 0;
fail4: sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL);
fail1: input_free_device(dev);
kfree(atkbd);
return err;
}
我们很有必要对函数里发红的那家伙说点什么,恩恩,哈哈,走了千万里,终于找到了你,就是在这里,我们将一个input_dev注册到输入子系统的input_dev_list链表中去:
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
const char *path;
int error;
//设置evbit[0]的第_SYN位,还记得原来我们说的么,每一位代表一个事件
set_bit(EV_SYN, dev->evbit);
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
init_timer(&dev->timer);
//这些如果为空就设置位默认值,在我们这里是不需要的,已经被初始化了,是在atkbd_set_device_attrs(atkbd)里//进行的
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
//插入到input_dev_list链表
list_add_tail(&dev->node, &input_dev_list);
snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
if (!dev->cdev.dev)
dev->cdev.dev = dev->dev.parent;
error = class_device_add(&dev->cdev);
if (error)
return error;
path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);
//每次注册一个input_dev设备,我们同样要对handler链表遍历,进行匹配
list_for_each_entry(handler, &input_handler_list, node)
[color="#ff0000"] input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
return 0;
}
到了这里,我们已经将驱动与设备联系了起来,也将驱动与输入子系统连接了起来.
第三部分
到了这里,也许有人问,那个serio结构体是干什么的呢?
在回答你这个问题之前,我们先来看看drivers/input/serio/下面的Kconfig(怎么又是他?你说不是他还能是谁...?)
config SERIO
tristate "Serial I/O support" if EMBEDDED || !X86
default y
---help---
Say Yes here if you have any input device that uses serial I/O to
communicate with the system. This includes the
* standard AT keyboard and PS/2 mouse *
as well as serial mice, Sun keyboards, some joysticks and 6dof
devices and more.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called serio.
if SERIO
config SERIO_I8042
tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86
default y
depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && !M68K
---help---
i8042 is the chip over which the standard AT keyboard and PS/2
mouse are connected to the computer. If you use these devices,
you'll need to say Y here.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called i8042.
看见了吧,符合这些规定的都使用serio来作为底层驱动的实现.
我们来小看一下serio.c,什么?你怕?怕什么,有我呢,我都不怕你怕啥,我们的空15军都敢从5000米高空盲降,你还有什么好怕的呢?
static int __init serio_init(void)
{
int error;
error = bus_register(&serio_bus);
if (error) {
printk(KERN_ERR "serio: failed to register serio bus, error: %d\n", error);
return error;
}
serio_task = kthread_run(serio_thread, NULL, "kseriod");
if (IS_ERR(serio_task)) {
bus_unregister(&serio_bus);
error = PTR_ERR(serio_task);
printk(KERN_ERR "serio: Failed to start kseriod, error: %d\n", error);
return error;
}
return 0;
}
static void __exit serio_exit(void)
{
bus_unregister(&serio_bus);
kthread_stop(serio_task);
}
subsys_initcall(serio_init);
module_exit(serio_exit);
serio_init就干了两件事:
bus_register(&serio_bus)
kthread_run(serio_thread, NULL, "kseriod")
通俗点来说就是注册一个总线并产生一个线程.
int bus_register(struct bus_type * bus)
{
int retval;
BLOCKING_INIT_NOTIFIER_HEAD(&bus->bus_notifier);
retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
subsys_set_kset(bus, bus_subsys);
//向全局的bus->subsys"登记"
//subsystem_register() -> kset_add() -> kobject_add()
retval = subsystem_register(&bus->subsys);
if (retval)
goto out;
//有没有发现对设备的操作和对驱动的操作很像?
//kset_register(&bus->devices) 和set_register(&bus->drivers)
//作用类似,把bus->devices这个kset加入到bus- >subsys这个//subsystem中去。
kobject_set_name(&bus->devices.kobj, "devices");
bus->devices.kobj.parent = &bus->subsys.kobj;
retval = kset_register(&bus->devices);
if (retval)
goto bus_devices_fail;
kobject_set_name(&bus->drivers.kobj, "drivers");
bus->drivers.kobj.parent = &bus->subsys.kobj;
bus->drivers.ktype = &ktype_driver;
retval = kset_register(&bus->drivers);
if (retval)
goto bus_drivers_fail;
klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&bus->klist_drivers, NULL, NULL);
bus->drivers_autoprobe = 1;
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
retval = bus_add_attrs(bus);
if (retval)
goto bus_attrs_fail;
pr_debug("bus type '%s' registered\n", bus->name);
return 0;
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(&bus->drivers);
bus_drivers_fail:
kset_unregister(&bus->devices);
bus_devices_fail:
subsystem_unregister(&bus->subsys);
out:
return retval;
}
关于这些结构以及他们之间的继承和连接关系,大家可以去参考下linux那些事儿(好像不是第一次说了吧?)
总线注册成功了以后,这里会产生一个内核线程其实就是通过kthread_create产生的:
static int serio_thread(void *nothing)
{
do {
serio_handle_event();
wait_event_interruptible(serio_wait,
kthread_should_stop() || !list_empty(&serio_event_list));
try_to_freeze();
} while (!kthread_should_stop());
printk(KERN_DEBUG "serio: kseriod exiting\n");
return 0;
}
这个线程要做的事情很简单,他就一直睡,等到serio_event_list不空或者该退出的时候就会醒来去做他该做的事.
static void serio_handle_event(void)
{
struct serio_event *event;
mutex_lock(&serio_mutex);
/*
* Note that we handle only one event here to give swsusp
* a chance to freeze kseriod thread. Serio events should
* be pretty rare so we are not concerned about taking
* performance hit.
*/
if ((event = serio_get_event())) {
switch (event->type) {
case SERIO_REGISTER_PORT:
serio_add_port(event->object);
break;
case SERIO_RECONNECT_PORT:
serio_reconnect_port(event->object);
break;
case SERIO_RESCAN_PORT:
serio_disconnect_port(event->object);
serio_find_driver(event->object);
break;
case SERIO_ATTACH_DRIVER:
serio_attach_driver(event->object);
break;
default:
break;
}
serio_remove_duplicate_events(event);
serio_free_event(event);
}
mutex_unlock(&serio_mutex);
}
看到这里面宏事件的时候,你会不会想起我们在前面说__serio_register_driver的时候已经提到他了?对,如果
serio_driver的manual_bind是0的话(这个字段的字面意思就是手动绑定),我们就会调用serio_queue_event
(drv, NULL, SERIO_ATTACH_DRIVER)在事件队列里放入一个事件:
static int serio_queue_event(void *object, struct module *owner,
enum serio_event_type event_type)
{
unsigned long flags;
struct serio_event *event;
int retval = 0;
spin_lock_irqsave(&serio_event_lock, flags);
/*
* Scan event list for the other events for the same serio port,
* starting with the most recent one. If event is the same we
* do not need add new one. If event is of different type we
* need to add this event and should not look further because
* we need to preseve sequence of distinct events.
*/
//这里对链表的遍历是倒序进行的,注释已经说的很清楚了
list_for_each_entry_reverse(event, &serio_event_list, node) {
if (event->object == object) {
if (event->type == event_type)
goto out;
break;
}
}
//申请空间
event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC);
if (!event) {
printk(KERN_ERR
"serio: Not enough memory to queue event %d\n",
event_type);
retval = -ENOMEM;
goto out;
}
//模块计数
if (!try_module_get(owner)) {
printk(KERN_WARNING
"serio: Can't get module reference, dropping event %d\n",
event_type);
kfree(event);
retval = -EINVAL;
goto out;
}
event->type = event_type;
event->object = object;
event->owner = owner;
//加入事件队列
list_add_tail(&event->node, &serio_event_list);
//唤醒守护线程
wake_up(&serio_wait);
out:
spin_unlock_irqrestore(&serio_event_lock, flags);
return retval;
}
第四部分
该来的终究要来,该说的还是要说.
对一个完整的键盘来说,是不是感觉还少了点什么?我们的中断呢?我们的扫描码从哪里出来的?我们程序难道是一个空中楼阁,然后那些乱糟糟的扫描码就自动出来了?
你以为linux操作系统是智能的啊?你以为同样的硬件跑了一个开源的os就神奇了啊?你以为你拿linus做偶像时髦啊?你以为......
哥们,别激动,我要说的就是,你的怀疑是正确的,那些更低级,更脏,更晦涩的活还是要由程序员一点一点做的,不是像孙猴子那样从石头里蹦出来的.
我们在二里面介绍了一些键盘的背景,里面有提到i8042芯片.
还记得那句话么:我们所做的一切都是在为未来做准备!
我们在三里面说serio结构体的时候已经看过了Kconfig的内容,知道里面这么说:
对于config SERIO_I8042
i8042 is the chip over which the standard AT keyboard and PS/2
mouse are connected to the computer. If you use these devices,
you'll need to say Y here.
而相应的Makefile里
obj-$(CONFIG_SERIO_I8042) += i8042.o
OK,目标很简单:i8042.c
先来初始化:
static int __init i8042_init(void)
{
int err;
dbg_init();
//这里,主要是将i8042_reset全局变量置1
err = i8042_platform_init();
if (err)
return err;
//清空控制器的缓冲区
err = i8042_controller_check();
if (err)
goto err_platform_exit;
//注册控制器驱动
[color="#ff0000"]err = platform_driver_register(&i8042_driver);
if (err)
goto err_platform_exit;
//分配一个platform_device并注册
i8042_platform_device = platform_device_alloc("i8042", -1);
if (!i8042_platform_device) {
err = -ENOMEM;
goto err_unregister_driver;
}
[color="#ff0000"] 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;
}
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
};
int platform_driver_register(struct platform_driver *drv)
{
//驱动的总线类型为platform_bus_type
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
//在这里我们又看到了熟悉的driver_register
return [color="#ff0000"]driver_register(&drv->driver);
}
只不过这条路走下去并不会有什么结果,因为现在platform_bus_type上挂的还没有能和此驱动配对的设备.
貌似有套路可循?恩...人类的进化史不就是一个不断发现规律并利用规律的历史么.
没有设备是吧,我们现在就整设备!
platform_device_add里会调用device_add来向总线注册设备,并完成设备的继承关系,因为里面调用流程比较长而且复杂,咱也不拿来充篇幅了,这年头,人要厚道...
注册了设备之后,就如同我们当年注册驱动一样,要遍历总线上的驱动那条线,找到合适的驱动之后(当然是调用bus里的match来看合不合适)会调用platform_drv_probe然后最终调用我们的i8042_driver里的probe函数.
static int __devinit i8042_probe(struct platform_device *dev)
{
int error;
//做一些自测与初始化工作
error = i8042_controller_selftest();
if (error)
return error;
error = i8042_controller_init();
if (error)
return error;
//这里,2个看着差不多的部分,就是这个函数的核心了
if (!i8042_noaux) {
[color="#ff0000"]error = i8042_setup_aux();
if (error && error != -ENODEV && error != -EBUSY)
goto out_fail;
}
if (!i8042_nokbd) {
[color="#ff0000"]error = i8042_setup_kbd();
if (error)
goto out_fail;
}
/*
* 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_setup_aux与i8042_setup_kbd性质与流程基本一样,一个是针对鼠标一个是针对我们键盘的:
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;
}
什么?你没发现什么感兴趣的?我们千辛万苦要寻找的中断就是在这里注册的啊!
在注册驱动之前,我们针对键盘所属的控制器端口分配资源并做一些初始化,主要分配了struct i8042_port结构体中的struct serio结构并设置中断号.然后注册中断.
/*
* 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 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();
//unlikely宏一般处理不太可能发生的事,也就是说中断来了却发现没有数据要接收,你不耍我么?直接推出!
//伤自尊了...
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用来记录奇偶校验错误和超时2个状态
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
//port_no指明是键盘还是鼠标的端口号,这将作为控制器端口数组下标
port_no = (str & I8042_STR_AUXDATA) ?
I8042_AUX_PORT_NO : I8042_KBD_PORT_NO;
}
port = &i8042_ports[port_no];
dbg("%02x exists))
[color="#ff0000"]serio_interrupt(port->serio, data, dfl);
out:
return IRQ_RETVAL(ret);
}
关于那个exists字段,是在i8042_register_ports里设置的.他调用了serio_register_port进而又调用
__serio_register_port往serio_event_list队列里放一个SERIO_REGISTER_PORT事件.这一操作唤醒
了serio_thread守护线程,在serio_handle_event里的SERIO_REGISTER_PORT事件分支调用
serio_add_port,进而调用serio->start(serio)来使得我们的i8042_start函数被成功回调:
static int i8042_start(struct serio *serio)
{
struct i8042_port *port = serio->port_data;
[color="#ff0000"]port->exists = 1;
mb();
return 0;
}
是不是非常坎坷?如果把这一串串的代码贴出来,不知道大家会不会想打瞌睡呢?
已经有朋友批评我的文章又臭又长了,没办法,咱不是想让大家伙搞得清楚一些么,没想到搞成了反作用.
其实简单点也好,在这里我帮大家整理一个框架出来,大家可以更好地理解内核,以免被我的一面之解误导.
就像交大人甲说的那样,其实,你只要看进去了,看代码就跟看故事会一样...
好了,我们还是来继续说我们的中断,看看数据怎么由最底层的控制器一直跑到keyboard handler里面去的
irqreturn_t serio_interrupt(struct serio *serio,
unsigned char data, unsigned int dfl)
{
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
spin_lock_irqsave(&serio->lock, flags);
if (likely(serio->drv)) {
[color="#ff0000"]ret = serio->drv->interrupt(serio, data, dfl);
} else if (!dfl && serio->registered) {
serio_rescan(serio);
ret = IRQ_HANDLED;
}
spin_unlock_irqrestore(&serio->lock, flags);
return ret;
}
然后struct serio_driver atkbd_drv中的atkbd_interrupt被调用:
/*
* atkbd_interrupt(). Here takes place processing of data received from
* the keyboard into events.
*/
static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
unsigned int flags)
{
struct atkbd *atkbd = serio_get_drvdata(serio);
struct input_dev *dev = atkbd->dev;
unsigned int code = data;
int scroll = 0, hscroll = 0, click = -1, add_release_event = 0;
int value;
unsigned char keycode;
#ifdef ATKBD_DEBUG
printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);
#endif
#if !defined(__i386__) && !defined (__x86_64__)
if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags
& SERIO_TIMEOUT) && !atkbd->resend &&
atkbd->write) {
printk(KERN_WARNING "atkbd.c: frame/parity error: %02x\n", flags);
serio_write(serio, ATKBD_CMD_RESEND);
atkbd->resend = 1;
goto out;
}
if (!flags && data == ATKBD_RET_ACK)
atkbd->resend = 0;
#endif
if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_ACK))
if (ps2_handle_ack(&atkbd->ps2dev, data))
goto out;
if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_CMD))
if (ps2_handle_response(&atkbd->ps2dev, data))
goto out;
if (!atkbd->enabled)
goto out;
[color="#ff0000"] input_event(dev, EV_MSC, MSC_RAW, code);
if (atkbd->translated) {
if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) {
atkbd->release = code >> 7;
code &= 0x7f;
}
if (!atkbd->emul)
atkbd_calculate_xl_bit(atkbd, data);
}
switch (code) {
case ATKBD_RET_BAT:
atkbd->enabled = 0;
serio_reconnect(atkbd->ps2dev.serio);
goto out;
case ATKBD_RET_EMUL0:
atkbd->emul = 1;
goto out;
case ATKBD_RET_EMUL1:
atkbd->emul = 2;
goto out;
case ATKBD_RET_RELEASE:
atkbd->release = 1;
goto out;
case ATKBD_RET_ACK:
case ATKBD_RET_NAK:
if (printk_ratelimit())
printk(KERN_WARNING "atkbd.c: Spurious %s on %s. "
"Some program might be trying access hardware directly.\n",
data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
goto out;
case ATKBD_RET_HANGEUL:
case ATKBD_RET_HANJA:
/*
* These keys do not report release and thus need to be
* flagged properly
*/
add_release_event = 1;
break;
case ATKBD_RET_ERR:
atkbd->err_count++;
#ifdef ATKBD_DEBUG
printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports too many keys pressed.\n", serio->phys);
#endif
goto out;
}
code = atkbd_compat_scancode(atkbd, code);
if (atkbd->emul && --atkbd->emul)
goto out;
keycode = atkbd->keycode[code];
if (keycode != ATKBD_KEY_NULL)
input_event(dev, EV_MSC, MSC_SCAN, code);
switch (keycode) {
case ATKBD_KEY_NULL:
break;
case ATKBD_KEY_UNKNOWN:
printk(KERN_WARNING
"atkbd.c: Unknown key %s (%s set %d, code %#x on %s).\n",
atkbd->release ? "released" : "pressed",
atkbd->translated ? "translated" : "raw",
atkbd->set, code, serio->phys);
printk(KERN_WARNING
"atkbd.c: Use 'setkeycodes %s%02x ' to make it known.\n",
code & 0x80 ? "e0" : "", code & 0x7f);
input_sync(dev);
break;
case ATKBD_SCR_1:
scroll = 1 - atkbd->release * 2;
break;
case ATKBD_SCR_2:
scroll = 2 - atkbd->release * 4;
break;
case ATKBD_SCR_4:
scroll = 4 - atkbd->release * 8;
break;
case ATKBD_SCR_8:
scroll = 8 - atkbd->release * 16;
break;
case ATKBD_SCR_CLICK:
click = !atkbd->release;
break;
case ATKBD_SCR_LEFT:
hscroll = -1;
break;
case ATKBD_SCR_RIGHT:
hscroll = 1;
break;
default:
if (atkbd->release) {
value = 0;
atkbd->last = 0;
} else if (!atkbd->softrepeat && test_bit(keycode, dev->key)) {
/* Workaround Toshiba laptop multiple keypress */
value = time_before(jiffies, atkbd->time) && atkbd->last == code ? 1 : 2;
} else {
value = 1;
atkbd->last = code;
atkbd->time = jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]) / 2;
}
[color="#ff0000"] input_event(dev, EV_KEY, keycode, value);
input_sync(dev);
if (value && add_release_event) {
input_report_key(dev, keycode, 0);
input_sync(dev);
}
}
if (atkbd->scroll) {
if (click != -1)
input_report_key(dev, BTN_MIDDLE, click);
input_report_rel(dev, REL_WHEEL, scroll);
input_report_rel(dev, REL_HWHEEL, hscroll);
input_sync(dev);
}
atkbd->release = 0;
out:
return IRQ_HANDLED;
}
在这里,杂七杂八的事情被不同分支处理,然后调用input_event将将数据以事件的形式通知给输入子系统,然后在Input.c中根据事件的类型,将需要反馈给物理设备的事件通过调用物理设备的Event函数传给设备驱动处理.
/**
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input devices
* See also input_inject_event()
*/
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
if (type > EV_MAX || !test_bit(type, dev->evbit))
return;
add_input_randomness(type, code, value);
switch (type) {
……
case EV_KEY:
if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
return;
if (value == 2)
break;
change_bit(code, dev->key);
if (test_bit(EV_REP, dev->evbit) &&
dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
dev->timer.data && value) {
dev->repeat_key = code;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
}
break;
……
}
if (type != EV_SYN)
dev->sync = 0;
if (dev->grab)
dev->grab->handler->event(dev->grab, type, code, value);
else
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
}
首先根据不同的事件,对设备的事件对应的位进行检查并设置.对于我们的键盘可以看到,如果按键对应的位没有置位,我们就不予理睬.
最后会调用handle->handler->event(handle, type, code, value).
到了这里,就到了我们最初的那个 struct input_handler kbd_handler里面了,kbd_event在此被调用.
至此,我们的扫描码处理流程被我唠唠叨叨地说了一遍,从8042控制器驱动到我们的键盘驱动,然后是输入子系统,最后是我们的响应处理.
整个输入子系统的原理就是这样,当你对整个框架熟悉了以后,你会发现你也可以模仿着写一些驱动放进去了,反正那些基本的函数都做好了,你组织组织调用调用.地基都打好了,你想盖什么样的大楼,自己爱咋整就咋整去吧.
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/49742/showart_1914216.html |
|