前言
与之前的简单的字符设备驱动不同的是,输入子系统采用分层分离模型,将一个驱动分为设备层,应用层和核心层,设备层只关心硬件的,应用层是纯软件的东西,而核心层则是建立起两者之间的联系。
应用层
应用层的主要功能如下图所示:
首先定义一个handler结构体,
初始化里面的信息,包括read、write、event等操作函数等,具体的内容当用到时再做介绍。
然后通过input_register_handler函数注册进内核。
函数主要作用如下:
int input_register_handler(struct input_handler *handler)
{
input_table[handler->minor >> 5] = handler; //#1
list_add_tail(&handler->node, &input_handler_list); //#2
list_for_each_entry(dev, &input_dev_list, node) //#3
input_attach_handler(dev, handler); //#4
}
- #1: 将
handler结构体告诉核心层,核心层就能通过这个数组直接找到相应的handler。 - #2: 将
handler加入input_handler_list链表中。 - #3: 这是一个宏定义,作用时等同于
for循环,意思是对于每一个handler都调用#4。 - #4: 通过
input_attach_handler函数建立两者的联系。
input_attach_handler函数内容如下:
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
id = input_match_device(handler->id_table, dev); //#1
error = handler->connect(handler, dev, id); //#2
}
- #1: 判断
handler的id是否与设备一致。 - #2: 若一致,则通过
connect函数将两者建立联系。
以evdev.c中的connect函数为例介绍函数内容:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //#1
init_waitqueue_head(&evdev->wait); //#2
devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); //#3
cdev = class_device_create(&input_class, &dev->cdev, devt,
dev->cdev.dev, evdev->name); //#4
error = input_register_handle(&evdev->handle); //#5
}
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
list_add_tail(&handle->d_node, &handle->dev->h_list); //#6
list_add_tail(&handle->h_node, &handler->h_list); //#7
}
- #1: 创建一个
handle结构体,该结构体功能就是连接设备层和应用层。 - #2: 初始化一个等待队列,这个对列在
read函数中唤醒。 - #3和#4: 这部分就是字符设备驱动中的创建结点,所以只有连接成功了之后才会真正的在系统中产生一个完整的驱动。
- #5: 这个函数将主要完成连接的工作。
- #6: 将
handle和设备层相连 - #7: 将
handle和应用层相连 - 连接成功后,就可以通过
handle来完成设备层向应用层的访问,应用层向设备层的访问。
read函数
当应用程序调用read函数时,就会调用应用层中相应的read函数。该read函数的主要功能就是使进程休眠。
event函数
该函数由设备层根据需求触发,在本例中,该函数主要就是唤醒进程。
设备层
设备层的主要功能如下图所示:
首先定义一个dev结构体,然后初始化
然后通过input_register_device函数注册进内核。
该函数主要功能如下:
int input_register_device(struct input_dev *dev)
{
list_add_tail(&dev->node, &input_dev_list); //#1
list_for_each_entry(handler, &input_handler_list, node) //#2
input_attach_handler(dev, handler); //#3
}
- #1: 将
dev结构体,加到input_dev_list链表中,用于和应用层进行匹配,用途在应用层中已经介绍过。 - #2和#3: 这个与应用层中的一样,这段代码出现两次的作用就是,这两个文件驱动可以不分先后的安装,当先安装设备层驱动的时候,执行该段代码,不会找到相应的应用层,所以就不会建立起连接,之后安装当应用层驱动,这时候就可以匹配到相应的设备层,然后创建出结点。
触发event函数
根据不同的需求,在相应的设备达到某种状态的这时候调用input_event()和input_sync()函数触发应用层中的event()函数。
核心层
- 核心层定义了相应的数据结构以及函数。
- 定义了
file_operations结构体。但该结构体里只有open()函数。 open()函数内可以根据打开的文件,调用相应应用层中的操作函数,如read()、write()函数等。