input子系统驱动学习之中的一个
刚一開始我就陷入了一个困境,理不清究竟input子系统驱动哪些是我须要做的哪些是系统已经为我们完毕的。为此花费了我一个星期的时间才真正的从这个困惑里走出来。当然后边还有非常多其它的困难这里我就不多说了,以下我就来总结一下input子系统学习的过程。
首先我们要清楚input子系统分为几层,各层都有些什么,以及各自的功能
这一层中包括一个input_dev结构体,它是用来描写叙述一个设备的信息的。
包括了对设备的各项设置及操作函数。
这一层包括一个input_handle结构体。它做为input_dev和input_handler之间的桥梁 ,将handle的list_headd_node加入到handle关联的dev的h_list中, 将handle的list_headh_node加入到handle关联的handler的h_list中。
使设备驱动部分不再关心对设备文件的操作而仅仅需关心对各硬件寄存器的操作和提交的输入事件就可以 。
这一层包括了input_handler结构体,它是作为事件处理器相应用程序的接口。
二、input子系统驱动的实现过程
2.1 分配一个输入设备
struct input_dev *dev;//声明一个输入设备结构体
Struct input_dev
{
const char *name;//输入设备的名称
...
struct input_id id;//输入设备的ID号
...
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//输入设备所支持的事件类型
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//保存设备所支持的事件码
...
//输入设备文件接口操作函数指针
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 __rcu *grab;
struct device dev;// 表示SYS设备模型中的一个设备
struct list_head h_list;//
struct list_head node;
}
dev=input_allocate_device(void);//为声明的结构体分配空间
2.2 驱动支持什么事件
这一步我们能够设置输入设备支持哪些事件类型。以鼠标为例我们能够设置设备支持按键事件和相对坐标事件,在设置完支持的事件类型后还须要设置事件类型的码表,即该事件所包括的子事件。
input_dev->evbit[0] = BIT(EV_KEY); //赋值使输入设备支持按键类型
input_dev->keybit[BITS_TO_LONGS(KEY_CNT)=BIT(BTN_LEFT)| BIT(BTN_RIGHT)|BIT(BTN_MIDDLE); //按键类型的键码,左键、右键、中键
2.3 注冊一个输入设备
这一步调用的是input_register_device()函数。该函数是输入子系统核心层提供的函数。该函数将 input_dev结构体注冊到输入子系统核心中。input_register_device()函数假设注冊失败,必须调用 input_free_device()函数释放分配的input_dev的空间。
假设该函数注冊成功,在卸载函数中应该调用 input_unregister_device()函数来注销输入设备结构体。注冊输入设备的过程就是为输入设备设置默认值,并将其挂在input_dev_list链上,与挂载在 input_handler_list 中的 handler 相匹配。假设匹配成功,就会调用 handler 的 connect函数。
int input_register_device(struct input_dev *dev)。//向input子系统注冊一个新的输入设备
2.4 驱动事件报告
这一步是有设备驱动层向核心层报告发生的事件,比如按键事件。相对坐标事件等。这些事件全都汇总到核心层由核心层统一分配到事件处理层。
input_report_key(dev,BTN_LEFT,value);
实质是函数:input_event(dev,type,value);
2.5 释放和注销设备
这一步用来注销输入设备并清除为该设备分配的空间。
void input_unregister_device(struct input_dev *dev);
void input_free_device(struct input_dev *dev);
通过以上的介绍我们了解了整个input子系统驱动程序的编写流程,以下让我们依据usb鼠标驱动程序来看一看input子系统驱动程序的实现过程。
以下就来说一说三个结构体的连接过程
在我们编写的设备驱动中调用input_register_device()来注冊一个设备。 input_register_device()完毕的主要功能就是初始化一些默认的值,将自己的device结构加入到linux设备模型其中。将input_dev加入到input_dev_list链表中。然后寻找合适的handler与input_handler配对,配对的核心函数是input_attach_handler。
input_attach_handler的主要功能则是调用了两个函数。一个是input_match_device进行配对,一个connect处理配对成功兴许工作。这些函数我们能够在linux内核源代码中进行查看。
对于connect函数,每种事件处理器的实现都有差异,但原理都同样,以事件处理器evdev为例,其connect函数为evdev_connect()。evdev_connect函数做配对后的善后工作,分配一个evdev结构体,并初始化相关成员。evdev结构体中有input_handle结构,在函数evdev_connect()中实现该结构体的初始化并调用 input_register_handle实现注冊。
input_register_handle()就是把一个handle结构体通过d_node链表项。分别链接到input_dev的h_list,input_handler的h_list上。
从而实现了3个结构体的连接。
总结
Input子系统驱动的编写须要我们理清哪些操作是须要我们做的,哪些操作是子系统已经为我们完毕了的。这一点非常重要,唯独我们明确了自己须要做些什么才干让我们更快的学会input子系统程序的编写。在这一个过程中我就走了非常多弯路。起先对input子系统不是太多了解,通过网上查找相关介绍和浏览上课的PPT,我看到了非常多关于input子系统驱动的介绍。但这些介绍不只包括了我们须要做的并且还包括了系统已经为我们完毕了的。
这给我的学习带来了非常大的困扰,我不明确到底我该从何下手编写一个完整的input子系统设备驱动了。通过一遍又一遍的浏览体会我最终明确了哪些是我须要做的。在这个过程中我也学会了怎样查看linux内核源代码。