oldfish_events是键盘驱动,跟其它驱动一样,Goldfish_events也是从模块初始化开始:
module_init(events_init);
static int __devinit events_init(void)
{
return platform_driver_register(&events_driver);
}
初始的过程就是一个注册的过程:
由于
static struct platform_driver events_driver = {
.probe = events_probe,
.driver = {
.name = "goldfish_events",
},
};
所以,在platform_driver_register函数中,会执行:
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
所以,最后调用到platform_drv_probe函数:
static int platform_drv_probe(struct device *_dev)
{
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev);
return drv->probe(dev);
to_platform_driver(X)个宏,而这个宏最终由另外一个经典的宏来完成,container_of(X)。
to_platform_driver(_dev->driver);的作用是返回一个platform_driver型的指针,而to_platform_device(_dev);则是返回一个platform_device的指针,这个从它赋给的drv和dev的类型就可以知道。
因此,最终从platform_drv_probe返回return drv->probe(dev);到platform_driver_register。
从platform_driver_register返回return driver_register(&drv->driver);到events_init。这样就完成了一初始化过程。
由上易知,return drv->probe(dev);将获得的参数dev传到了events_probe。events_probe函数完成的功能如下:
input_dev = input_allocate_device();
addr = (unsigned) ioremap(res->start, 4096); //获取虚拟地址
irq = platform_get_irq(pdev, 0); //获取中断
edev->input = input_dev;
edev->addr = addr;
edev->irq = irq;
ret = input_register_device(input_dev); //向input.c中注册输入设备(键盘)
request_irq(edev->irq, events_interrupt, 0, "goldfish-events-keypad", edev)
//申请中断
中断申请过程中,传入了一个很关键的参数:events_interrupt,这个参数是个函数名,函数原型为:
static irqreturn_t events_interrupt(int irq, void *dev_id)
之所以说这个函数很关键,一是因为这个函数在input.c中,建立了与input.c的联系,二是因为在这个函数里调用了一个核心函数:
input_event(edev->input, type, code, value);
它通过调用input_handle_event对输入事件进行处理。然后将返回值传入中断申请函数。
在键盘驱动中,并没有建立键盘的设备结点。那么设备结点是在哪建立的呢?
其实,这个步骤是在input.c的初始化过程中完成的。
input_init(void)
err = input_proc_init(); //输入过程初始化
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
//设备结点的创建
在这里,主设备号为13,&input_fops 则传入生个file_operations结构的地址。
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
这三个驱动层中与用户态函数关系最密切的要属evdev.c了。从上面的分析我们知道,用户态调用了
fd = open(deviceName, O_RDWR);
if(ioctl(fd, EVIOCGVERSION, &version))
等函数对底层进行了操作,这个接口函数正是evdev.c提供的。、
这个函数主要负责对输入事件进行处理。所谓的输入事件是经input.c封装好的事件,如键盘事件、鼠标事件、触摸层事件等等。对于evdev.c来说,这些事件都是一样的,而这些事件的类型则有相应的变量进行标识(在结构体input_dev中的字段keybit)。如:
Ø EV_KEY 0x01 按键
Ø EV_REL 0x02 鼠标
Ø EV_ABS 0x03 触摸屏