输入子系统框架
新人报道,试着发个帖子试试。
字符设备驱动程序框架:
1. major
2. file-operation
3. register
4. 入口函数
5. 出口函数
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
如果写的程序想很通用,要引入输入子系统框架
输入子系统由 输入子系统核心层( Input Core ),驱动层和事件处理层(Event Handler)三部份组成。一个输入事件,如鼠标移动,通过 Driver -> InputCore -> Eventhandler -> userspace 的顺序到达用户空间传给应用程序。
其中Input Core 即 Input Layer 由 driver/input/input.c及相关头文件实现。对下提供了设备驱动的接口,对上提供了Event Handler层的编程接口。
具体过程需要仔细体会代码。
这部分可以仔细看韦东山老师的视频讲解,理解会更快些,百度下有很多。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
目的:我们写驱动的目的是为了构造一些函数,使上层可以调用,也就是构造file-operation。
先看下别人写的驱动程序是什么样子
drivers/input/input.c:
static int __init input_init(void)中有注册驱动的过程,调用了
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
找一下input_fops,发现只有一个结构体,那再看看input_open_file都做了什么事情。
struct input_handler *handler = input_table[iminor(inode) >> 5];
定义一个input_handler指针,根据传入文件的次设备号从input_table中提取出一个input_handler
new_fops = fops_get(handler->fops);//从input_handler中提取出new_fops
file->f_op = new_fops;//将new_fops赋值给当前文件的file_operations
其实目的是将打开的新的文件的fop 指向所找到的 handler的里面的fop,将两者对应起来,这样以后操作这个文件的fop 实际上是操作handler的里面的fop。所以这里的驱动程序最重要的是怎么来构造这个handler里面的fop,也就是怎么来构造input_table里面的东西。这样看来input.c只是起到了一个中转的作用。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
找到input_table数组由谁构造?在input_register_handler这个函数里面来构造
搜索内核可知:
evdev.c,tsdev.c,joydev.c,keyboard.c,mousedev.c等文件调用了 input_register_handler,在每种设备里面,都构造了 input_handler 这种结构,我们以evdev.c为例。这些通用的文件已经将常规的操作都写好了,也就是fop结构。
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
evdev_fops 可以看到很多读写函数,相当于帮我们去写了那些驱动函数。看下EVDEV_MINOR_BASE = 64,那么input_table[iminor(inode) >> 5];相当于
input_table[2].
id_table 表示这个evdev_handler里面有个列表,记录了可以支持的硬件设备。因为这里的handler是个软件的概念,写的是通用的函数,最后要转化的还是具体各种不同的硬件,比如有很多的鼠标,很多的键盘,这里提供一个通用的接口,开发人员要用一种新的鼠标,只要自己把硬件相关的模块写好就行。所以这个evdev_handler可能会支持很多硬件,要找到他跟硬件之间的联系。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
反过来看input.c中有注册input_handler跟注册输入设备的两个过程。
**********************************************************************************************
注册input_handler:
input_register_handler
// 放入数组
input_table[handler->minor >> 5] = handler;
// 放入链表
list_add_tail(&handler->node, &input_handler_list);
// 对于每个input_dev,调用input_attach_handler
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev
**********************************************************************************************
注册输入设备:
input_register_device
// 放入链表
list_add_tail(&dev->node, &input_dev_list);
// 对于每一个input_handler,都调用input_attach_handler
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev
**********************************************************************************************
两边是个对称的过程。
注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,
根据input_handler的id_table判断这个input_handler能否支持这个input_dev,
如果能支持,则调用input_handler的connect函数建立"连接
注意这里的每一种类型的设备只有一个input_handler的结构,而device的结构有很多钟,那我们的目标就要去建立这些device.
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
看下dev 跟 handler两者怎么样建立连接:
struct input_handle handle;
input_handle结构是个核心的结构体,在每个设备中都建立了这个结构体,这个结构体是在input.h中定义的,这样被很多文件调用比如evdev,里面建立了自己的input_handle结构。这个结构体里面又定义了两种结构
struct input_dev *dev;
struct input_handler *handler;
这样就把dev 跟handler进行了关联(通过h_list 进行关联)。
这样搞清楚了之间的联系,evdev.c,tsdev.c,joydev.c,keyboard.c,mousedev.c等文件,已经写好了,里面是软件的操作代码,具体操作会用到dev设备的代码,我们写驱动,主要就是写这些设备的驱动,然后将这些设备驱动进行注册跟evdev等进行关联就行。最后上层调用读写等函数,实际上是调用evdev等里面的读写函数,进而通过h_list 找到对应的dev函数进行操作 |