一 输入子系统分层
1、输入子系统事件处理层(EventHandler)
.id_table 一个存放该handler所支持的设备id的表(其实内部存放的是EV_xxx事件 ,用于判断device是否支持该事件)
.fops 该handler的file_operation
.connect 连接该handler跟所支持device的函数
.disconnect 断开该连接
.event 事件处理函数,让device调用
.h_list 也是一个链表,该链表保存着该handler到所支持的所有device的中间站:hand le结构体的指针
2、输入子系统核心层(InputCore)
主要功能: 注册主设备号
对于swi进入的open函数进行第一层处理,并通过次设备号选择handler进入第二层open,也就是真正的open所在的file_operation,并返回该file_opration的fd
提供input_register_device跟input_register_handler函数分别用于注册device跟handler
3、输入子系统设备驱动层(Input Device)
device是纯硬件操作层,包含不同的硬件接口处理,如gpio等
对于每种不同的具体硬件操作,都对应着不同的input_dev结构体
该结构体内部也包含着一个h_list
二 设备注册过程
注册过程:
1、两个重要链表
对于handler和device,分别用链表input_handler_list和input_device_list进行维护,
当handler或者device增加或减少的时候,分别往这两链表增加或删除节点。
2、从register_input_device开始
input_register_device(button_dev)
list_add_tail(&dev->node, &input_dev_list) /* 增加一个设备 */
/* 遍历input_handler_list,为input_dev寻找合适它的另一半input_hander */
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
/* 通过handler 的.id_table查看该handler是否支持该device */
input_match_device(handler, dev)
for (id = handler->id_table; id->flags || id->driver_info; id++) {
}
/* 如果支持,则建立连接 */
error = handler->connect(handler, dev, id){
}
3、红娘input_handle的注册
int input_register_handle(struct input_handle *handle)
list_add_tail_rcu(&handle->d_node, &dev->h_list); //将handle的d_node,链接到其相关的input_dev的h_list链表中
list_add_tail(&handle->h_node, &handler->h_list); //将handle的h_node,链接到其相关的input_handler的h_list链表中
input_handle在上一步中connect成功后创建,如evdev_connect:
evdev->handle.dev = input_get_device(dev); //
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler; //
evdev->handle.private = evdev;
4、 input_handler的注册
input_register_handler
//input_table,每个注册的handler都会将自己保存到这里,索引值为handler->minor右移5为,也就是除以32
//为什么会这样呢,因为每个handler都会处理最大32个input_dev,所以要以minor的32为倍数对齐,这个minor是传进来的handler的MINOR_BASE
//每一个handler都有一个这一个MINOR_BASE,以evdev为例,EVDEV_MINOR_BASE = 64,可以看出系统总共可以注册8个handler
input_table[handler->minor >> 5] = handler;
list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
补充两结构:
evdev:
evdev结构体在配对成功的时候生成,由handler->connect生成,对应设备文件为/class/input/event(n)
struct evdev {
int exist;
int open; //打开标志
int minor; //次设备号
struct input_handle handle; //关联的input_handle
wait_queue_head_t wait; //等待队列,当进程读取设备,而没有事件产生的时候,进程就会睡在其上面
struct evdev_client *grab; //强制绑定的evdev_client结构,这个结构后面再分析
struct list_head client_list; //evdev_client 链表,这说明一个evdev设备可以处理多个evdev_client,可以有多个进程访问evdev设备
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev; //device结构,说明这是一个设备结构
};
evdev_client:
这个结构在进程打开event设备的时候调用evdev的open方法,在open中创建这个结构,并初始化。在关闭设备文件的时候释放这个结构。
struct evdev_client {
struct input_event buffer[EVDEV_BUFFER_SIZE];
//这个是一个input_event数据结构的数组,input_event代表一个事件,基本成员:类型(type),编 码(code),值(value)
int head; //针对buffer数组的索引
int tail; //针对buffer数组的索引,当head与tail相等的时候,说明没有事件
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync; //异步通知函数
struct evdev *evdev; //evdev设备
struct list_head node; // evdev_client 链表项
};
关于event号的来历:
分析evdev_connect可知:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;
/* 所有分配的evdev都会放入evdev_table数组,这里按序查找一个空闲位置 */
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
/* EVDEV_MINORS=32,这里最多可支持32个evdev */
if (minor == EVDEV_MINORS) {
pr_err("no more free evdev devices\n");
return -ENFILE;
}
/* 如果数组没满,就分配一个evdev结构 */
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
...
dev_set_name(&evdev->dev, "event%d", minor); /* 将此次设备号作为event号 */
evdev->exist = true;
evdev->minor = minor;
/* 初始化evdev里面的handle,建立input_dev和input_handler的连接 */
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
/* 注册handle结构 */
error = input_register_handle(&evdev->handle);
if (error)
goto err_free_evdev;
error = evdev_install_chrdev(evdev);
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev);
if (error)
goto err_cleanup_evdev;
return 0;
...
}
输入事件结构体:
struct input_event {
struct timeval time; //按键时间
__u16 type; //事件的类型
__u16 code; //要模拟成什么按键
__s32 value; //是按下1还是释放0
};
type:
事件的类型:
EV_KEY, 按键事件,如键盘的按键(按下哪个键),鼠标的左键右键(是非击下)等;
EV_REL, 相对坐标,主要是指鼠标的移动事件(相对位移);
EV_ABS, 绝对坐标,主要指触摸屏的移动事件,但好像这个不能用在鼠标上面,也就是说无法通过这个来获取鼠标的绝对坐标(鼠标是一个相对位移的设备)。
code:
事件的代码:
如果事件的类型代码是EV_KEY,该代码code为设备键盘代码。代码植0~127为键盘上的按键代码,0x110~0x116为鼠标上按键代码,其中0x110(BTN_ LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标右键,0x112(BTN_ MIDDLE)为鼠标中键。其它代码含义请参看include/linux /input.h文件。该文件中会定义相应的宏来代表不同的按键。
如果事件的类型代码是EV_REL,code值表示轨迹的类型。如指示鼠标的X轴方向 REL_X(代码为0x00),指示鼠标的Y轴方向REL_Y,指示鼠标中轮子方向REL_WHEEL。
value:
事件的值:
如果事件的类型代码是EV_KEY,当按键按下时值为1,松开时值为0;
如果事件的类型代码是EV_ REL,value的正数值和负数值分别代表两个不同方向的值。例如:如果code是REL_X,value是10的话,就表示鼠标相对于上一次的坐标,往x轴向右移动10个像素点。