Linux Input子系统

一 输入子系统分层

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个像素点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值