linux输入子系统(一) —— 架构分析

  • 初识linux输入子系统

  linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。

  1. 设备驱动层:主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层。
  2. 核心层:为设备驱动层提供了规范和接口。设备驱动层只要关心如何驱动硬件并获得硬件数据(例如按下的按键数据),然后调用核心层提供的接口,核心层会自动把数据提交给事件处理层。
  3. 事件处理层:则是用户编程的接口(设备节点),并处理驱动层提交的数据处理。

 

  • input输入子系统框架分析

  输入子系统由输入子系统核心层( Input Core ),驱动层和事件处理层(Event Handler)三部份组成。

  一个输入事件,从具体设备的device driver---> input driver -> Input core -> Event handler -> userspace 到达用户空间传给应用程序。

input

1.系统核心层(对应图中的input core 层)

  主要功能

  1. 注册主设备号
  2. 对于系统调用经过虚拟文件系统进入的open函数进行第一层处理,并通过次设备号选择handler进入第二层open,也就是真正的open所在的file_operation,并返回该file_opration的fd
  3. 提供input_register_device跟input_register_handler函数分别用于注册device跟handler

 

2.handler层(对应图中的Event handler 层)

  Event handler对应一个名为input_handler的结构体,该结构体内含的主要成员如下

    .id_table   一个存放该handler所支持的设备id的表(其实内部存放的是EV_xxx事件,用于判断device是否支持该事件)

    .fops     该handler的file_operation

    .connect   连接该handler跟所支持device的函数

    .disconnect  断开该连接

    .event    事件处理函数,让device调用

      h_list    也是一个链表,该链表保存着该handler到所支持的所有device的中间站:handle结构体的指针

 

3.device层(驱动层:sensor  ,tp等设备的具体驱动)

  device是纯硬件操作层,包含不同的硬件接口处理,如gpio等

  对于每种不同的具体硬件操作,都对应着不同的input_dev结构体

  该结构体内部也包含着一个h_list

 

4.input_handler_list和input_device_list

  对于handler和device,分别用链表input_handler_list和input_device_list进行维护,

  当handler或者device增加或减少的时候,分别往这两链表增加或删除节点。

5.input子系统框架图

 

input

 

 

  • Input输入子系统数据结构分析

 

input_dev

  input_dev 这是input设备基本的设备结构,每个input驱动程序中都必须分配初始化这样一个结构,成员比较多 

    struct input_dev {
    const char *name;
    const char *phys;
    const char *uniq;
    struct input_id id;

    unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];

    unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
    unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
    unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
    unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
    unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
    unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
    unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
    unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
    unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

    unsigned int hint_events_per_packet;

    unsigned int keycodemax;
    unsigned int keycodesize;
    void *keycode;

    int (*setkeycode)(struct input_dev *dev,
              const struct input_keymap_entry *ke,
              unsigned int *old_keycode);
    int (*getkeycode)(struct input_dev *dev,
              struct input_keymap_entry *ke);

    struct ff_device *ff;

    unsigned int repeat_key;
    struct timer_list timer;

    int rep[REP_CNT];

    struct input_mt *mt;

    struct input_absinfo *absinfo;

    unsigned long key[BITS_TO_LONGS(KEY_CNT)];
    unsigned long led[BITS_TO_LONGS(LED_CNT)];
    unsigned long snd[BITS_TO_LONGS(SND_CNT)];
    unsigned long sw[BITS_TO_LONGS(SW_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;

    spinlock_t event_lock;
    struct mutex mutex;

    unsigned int users;
    bool going_away;

    struct device dev;

    struct list_head    h_list;
    struct list_head    node;

    unsigned int num_vals;
    unsigned int max_vals;
    struct input_value *vals;

    bool devres_managed;
};

 

 

(1)多个位图数组

     unsigned long evbit[BITS_TO_LONGS(EV_CNT)];   //事件支持的类型  
    
 // 下面是每种类型支持的编码  
    unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];   //按键    
    unsigned long relbit[BITS_TO_LONGS(REL_CNT)];     
    unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];   //绝对坐标,其中触摸屏驱动使用的就是这个  
    unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];  
    unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];  
    unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];  
    unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];  
    unsigned long swbit[BITS_TO_LONGS(SW_CNT)];  

(2) struct input_id id 成员

struct input_id {  
    __u16 bustype;   //总线类型  
    __u16 vendor;    //生产厂商  
    __u16 product;   //产品类型  
    __u16 version;   //版本  
 }; 

  如果需要特定的事件处理器来处理这个设备的话,这几个就非常重要,因为子系统核心是通过他们,将设备驱动与事件处理层联系起来的。但是因为安卓系统大部分的输入设备驱动匹配的事件处理器为evdev,所有这个初始化也无关紧要。

 

input_handler

  input_handler 这是事件处理器的数据结构,代表一个事件处理器


struct input_handler {

    void *private;

    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    void (*events)(struct input_handle *handle,
               const struct input_value *vals, unsigned int count);
    bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    bool (*match)(struct input_handler *handler, struct input_dev *dev);
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
    void (*disconnect)(struct input_handle *handle);
    void (*start)(struct input_handle *handle);

    bool legacy_minors;
    int minor;
    const char *name;

    const struct input_device_id *id_table;

    struct list_head    h_list;
    struct list_head    node;
};

 

(1)几个操作函数

void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);

int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);

void (*disconnect)(struct input_handle *handle); void (*start)(struct input_handle *handle);

  event 函数是当事件处理器接收到了来自input设备传来的事件时调用的处理函数,负责处理事件,非常重要。

  connect 函数是当一个input设备模块注册到内核的时候调用的,将事件处理器与输入设备联系起来的函数,也就是将input_dev和input_handler配对的函数。

  disconnect 函数实现connect相反的功能。

(2) 一个id

  const struct input_device_id *id_table;      //这个是事件处理器所支持的input设备   

  这个数组都会用在connect函数中,input_device_id结构与input_id结构类似,但是input_device_id有一个flag,用来让程序选择比较哪项,如:busytype,vendor还是其他。

struct input_device_id {

    kernel_ulong_t flags;

    __u16 bustype;
    __u16 vendor;
    __u16 product;
    __u16 version;

    kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
    kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];

    kernel_ulong_t driver_info;
};

input_handle

  input_handle 结构体代表一个成功配对的input_dev和input_handler

 

struct input_handle {  
     /*每个配对的事件处理器都会分配一个对应的设备结构,如evdev事件处理器的evdev结构,
    注意这个结构与设备驱动层的input_dev不同,初始化handle时,保存到这里。*/
    void *private;    
    int open;        //打开标志,每个input_handle 打开后才能操作,这个一般通过事件处理器的open方法间接设置  
    const char *name;   
    struct input_dev *dev;  //关联的input_dev结构  
    struct input_handler *handler; //关联的input_handler结构  
    struct list_head    d_node;  //input_handle通过d_node连接到了input_dev上的h_list链表上  
    struct list_head    h_node;  //input_handle通过h_node连接到了input_handler的h_list链表上  
};

三个数据结构之间的关系

  input_dev 是硬件驱动层,代表一个input设备

  input_handler 是事件处理层,代表一个事件处理器

  input_handle 属于核心层,代表一个配对的input设备与input事件处理器

  input_dev 通过全局的input_dev_list链接在一起。设备注册的时候实现这个操作。

  input_handler 通过全局的input_handler_list链接在一起。事件处理器注册的时候实现这个操作(事件处理器一般内核自带,一般不需要我们来写)

  input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_dev 和 input_handler 的h_list上了。

  通过input_dev 和input_handler就可以找到input_handle 在设备注册和事件处理器, 注册的时候都要进行配对工作,配对后就会实现链接。

  通过input_handle也可以找到input_dev和input_handler。

注:三者之间的关系可以参考前面介绍的input子系统框架图

 

补充两个结构体

 (1) evdev设备结构

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结构体在配对成功的时候生成,由handler->connect生成,对应设备文件为sys/class/input/event(n)。

  如触摸屏驱动的event0,这个设备是用户空间要访问的设备,可以理解它是一个虚拟设备,因为没有对应的硬件,但是通过handle->dev 就可以找到input_dev结构。这个设备结构生成之后保存在evdev_table中,索引值是minor。

注:需要注意evdev结构体------->sys/class/input/event(n)        input_dev结构-------->sys/class/input/input(n)

 

sdm660_64:/sys/class/input # ls
event0  event16 event23 event30 input1  input17 input24 input4
event1  event17 event24 event4  input10 input18 input25 input5
event10 event18 event25 event5  input11 input19 input26 input6
event11 event19 event26 event6  input12 input2  input27 input7
event12 event2  event27 event7  input13 input20 input28 input8
event13 event20 event28 event8  input14 input21 input29 input9
event14 event21 event29 event9  input15 input22 input3  mice
event15 event22 event3  input0  input16 input23 input30 mouse0
sdm660_64:/sys/class/input #

 

(2) evdev用户端结构

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 链表项  
};  

 

 这个结构在进程打开event0设备的时候调用evdev的open方法,在open中创建这个结构,并初始化。在关闭设备文件的时候释放这个结构。

Input输入子系统数据结构关系图

input

 static const struct input_device_id evdev_ids[] = {
	{ .driver_info = 1 },	/* Matches all devices */
	{ },			/* Terminating zero entry */
};

MODULE_DEVICE_TABLE(input, evdev_ids);

static struct input_handler evdev_handler = {
	.event		= evdev_event,
	.events		= evdev_events,
	.connect	= evdev_connect,
	.disconnect	= evdev_disconnect,
	.legacy_minors	= true,
	.minor		= EVDEV_MINOR_BASE,
	.name		= "evdev",
	.id_table	= evdev_ids,
};

struct evdev {   //每次与evdev_handler 配对成功都会创建这个机构体  而且对应一个eventx
	int open;
	struct input_handle handle;
	wait_queue_head_t wait;
	struct evdev_client __rcu *grab;
	struct list_head client_list;
	spinlock_t client_lock; /* protects client_list */
	struct mutex mutex;
	struct device dev;
	struct cdev cdev;
	bool exist;
};

struct evdev_client {
	unsigned int head;
	unsigned int tail;
	unsigned int packet_head; /* [future] position of the first element of next packet */
	spinlock_t buffer_lock; /* protects access to buffer, head and tail */
	struct wake_lock wake_lock;
	bool use_wake_lock;
	char name[28];
	struct fasync_struct *fasync;
	struct evdev *evdev;
	struct list_head node;
	int clkid;
	bool revoked;
	unsigned int bufsize;
	struct input_event buffer[];
};

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值