input子系统相关

本文是我学习时所写,非百分之百原创,望指出错误之处。
参考资料:
input子系统按键处理
INPUT输入子系统

在系统中会出现很多的input设备,比如:键盘、屏幕等等,这些物理设备都会统一的抽象为input设备。
用来抽象这些设备的数据结构是struct input_dev结构,该结构如下:

input输入子系统的架构如下图:
在这里插入图片描述
从中我们可以得知,input输入子系统分为上中下三个部分。
1、下层设备驱动层。系统中可以注册多个input输入设备,每个设备对应不同的物理硬件设备,比如:键盘input设备、鼠标input设备等等。
2、上层为处理程序(handlers),事件驱动层。不同的input设备在这一层都会有不同的handlers所对应,不同的handlers在在=应用层中的接口命名方式又不一样,例如:Mouse下的输入设备在应用层的接口是 /dev/input/mousen (n代表0、1、2…),Joystick下的输入设备在应用层的接口是 /dev/input/jsn(n代表0、1、2…),这个是在input输入子系统中实现的。
3、中间的输入核心层。从图中我们可以看到,输入核心层起到上下层中进行数据传输的作用,当下层发生任意输入事件是,核心层便被激活,然后将该事件传输给上层的一个或多个handlers中去。

input核心层分别为设备驱动层和事件驱动层提供了相关的API接口。
提供给设备驱动层的API
设备驱动层用struct input_dev结构来抽象一个硬件设备。
以下为struct input_dev的结构:

struct input_dev {
	const char *name;
 	const char *phys;
	const char *uniq;
	struct input_id id;
	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];     //表示此input设备支持的事件
	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 keycodemax;
	unsigned int keycodesize;
	void *keycode;
	int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
	int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);
	struct ff_device *ff;
	unsigned int repeat_key;
	struct timer_list timer;
	int sync;
	int abs[ABS_MAX + 1];
	int rep[REP_MAX + 1];
	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 absmax[ABS_MAX + 1];
	int absmin[ABS_MAX + 1];
	int absfuzz[ABS_MAX + 1];
	int absflat[ABS_MAX + 1]; 
	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 *grab;	//当前占有该设备的handle
	spinlock_t event_lock;
	struct mutex mutex;
	unsigned int users;
	int going_away;
	struct device dev;
	struct list_head  h_list;//用来挂接dev上连接的所有handle的一个链表头
	struct list_head  node;//作为一个链表节点将自己挂接到 全局input_dev_list 链表上
};

其中,成员unsigned long evbit[BITS_TO_LONGS(EV_CNT)],表示此input设备支持的事件,比如:EV_SYN(同步事件)、EV_KEY(按键事件)、EV_SW(开关事件)、EV_ABS(绝对坐标事件)、EV_REL(相对坐标事件)、EV_LED(LED灯事件)、EV_SND(声音事件)、EV_REP(重复按键事件)、EV_FF(受力事件)、EV_PWR(电源相关事件)。
成员unsigned long keybit[BITS_TO_LONGS(KEY_CNT)],表示
成员unsigned long absbit[BITS_TO_LONGS(ABS_CNT)],设置相应的位以支持某一类绝对值坐标。

核心层提供给设备驱动层的API主要有三个,如下所示:

struct input_dev *input_allocate_device(void);//申请一个input设备
int input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code);
/*函数input_set_capability,该函数用来设备准备注册的input设备所支持的上报事件的类型type,如EV_KEY;以及需要上报的事件的code,
如KEY_POWER*/
int input_register_device(struct input_dev *dev);//注册一个已经设置好了的input设备dev

我们在编写某个输入设备的驱动程序时,一般会先使用函数input_allocate_device申请一个input设备。然后使用input_set_capability函数对该input设备进行设置,例如支持哪类事件,具体事件的code等等。然后调用input_register_device函数将input设备注册到内核中。

#input_register_device注册函数中重要的调用关系如下:
int input_register_device(struct input_dev *dev)  #input设备注册函数
	__set_bit(EV_SYN, dev->evbit); #首先将该设备将EV_SYN置位,表示支持所有事件
	init_timer(&dev->timer); #初始化一个定时器,该定时器在处理重复事件时发挥作用
	device_add(&dev->dev); #将input_dev内置的struct device dev结构注册到Linux设备模型中去
	list_add_tail(&dev->node, &input_dev_list);#将我们注册的dev加入到input_dev_list链表中
#input核心层维护两个全局链表,分别为input_dev_list和input_handler_list,用力啊存放所有的struct input_dev和struct input_handler
	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler);#这句和上一句是一个循环,遍历input_handler_list上的handler,用于和我们现在的dev匹配。
		
#input_attach_handler匹配函数的调用关系如下:
input_attach_handler(struct input_dev *dev, struct input_handler*handler)
	if (handler->blacklist&& input_match_device(handler->blacklist, dev)) 
                  return -ENODEV; 
    #以上连句表示先判断handler中的blacklist字段是否定义,定了则先对handler->blacklist和dev->id进行匹配
    #handler->blacklist是struct input_device_id结构的数据,dev->id是struct input_id结构的数据
    id = input_match_device(handler->id_table, dev);
    #这一句将handler中的id_table字段和dev->id进行匹配,匹配成功,返回struct input_device_id类型数据指针
    #handler->id_table也是struct input_device_id结构的数据
    handler->connect(handler, dev, id);#当该input设备和某个handler匹配成功后会调用对应handler中的connect()函数
	#connect()函数的作用就是将handler和input_dev使用struct input_handle结构连接起来
	#并通过struct input_handle结构组成的链表维护所有的已经匹配成功的handler和input_dev

提供给设备驱动层的API还有如下几个:

static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat);
input_set_abs_params(input_dev, ABS_RX, 0, 23040, 0, 0);
/*函数input_set_abs_params,参数dev表示input设备,支持绝对值x坐标,并设置它在坐标系中的最大值和最小值,以及干扰值和平焊位置等。
就是通过设置absbit成员实现的。*/

static void input_handle_event(structinput_dev *dev,unsigned int type,unsigned int code, int value);
/*函数input_handle_event,该函数向核心层上报事件,参数dev是经过注册了的input设备指针,参数type表示事件类型,如:EV_KEY、EV_SYN等*/

在input_handle_event函数中,会判断发生的事件应该是继续往上发送给handler还是发送给设备的,如让 LED 灯点亮事件、蜂鸣器鸣叫事件等,这些事件就要发送给设备,需要调用struct input_dev结构中的event()函数;如果是发送给handler层处理,则调用 input_pass_event()函数。

提供给事件驱动层的API
提供给事件驱动层的API主要由以下两个:

int input_register_handler(struct input_handler *handler);//向核心层注册handler
int input_register_handle(struct input_handle *handle);//注册

以下为struct input_handler结构:

struct input_handler {
    void *private;//  私有数据

    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    //handler用于向上层上报输入事件的函数
    bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
    bool (*match)(struct input_handler *handler, struct input_dev *dev);
    //match 函数用来匹配handler 与 input_dev 设备
    int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
    //当handler 与 input_dev 匹配成功之后调用该函数
    void (*disconnect)(struct input_handle *handle);
    //断开handler 与 input_dev 之间的连接
    void (*start)(struct input_handle *handle);

    const struct file_operations *fops;//  一个file_operations 指针,指向这个handler的操作函数
    int minor;
    //该handler 的编号 (在input_table 数组中用来计算数组下标) input_table数组就是input子系统用来管理注册的handler的一个数据结构
    const char *name;//handler的名字

    const struct input_device_id *id_table;//指向一个 input_device_id类型的数组,用来进行与input设备匹配时用到的信息
	/*id_table会和struct input_dev结构中的id字段进行比较*/
	const struct input_device_id *blacklist; //指向一个 input_device_id类型的数组,这个数组包含 handler 应该忽略的设备
    struct list_head    h_list;
    //用来挂接handler上连接的所有handle的一个链表头
    struct list_head    node;
    /*作为一个链表节点将自己挂接到 input_handler_list 链表上
    (input_handler_list 链表是一个由上层handler参维护的一个用来挂接所有注册的handler的链表头)*/
};

想要向核心层注册handler时,就需要将以上结构体中,必要的字段进行填充,然后调用input_register_handler函数。

#input_register_handler函数的调用关系如下:
int input_register_handler(struct input_handler *handler)
	list_add_tail(&handler->node,&input_handler_list);
	#以上这句将 handler 加入全局的 input_handler_list 链表中,该链表包含了系统中所有的 input_handler
	 list_for_each_entry(dev,&input_dev_list, node)
     	input_attach_handler(dev, handler);
     #以上两句是一个循环,之前介绍input_register_device时介绍过,遍历全局input_dev_list链表,与之和handler对比

input_register_handle函数在什么地方调用呢,在struct input_handler结构中,有一个函数connect,这个函数作用就是当handler和input_device匹配成功之后就调用该函数,将通过struct input_handle结构建立起关系,并向核心层注册这个struct input_handle结构。
struct input_handle的结构如下:

struct input_handle {
    void *private;//handle  的私有数据

    int open;//  这个用来做打开计数的
    const char *name;//   该handle 的名字

    struct input_dev *dev;//  用来指向该handle 绑定的input_dev 结构体
    struct input_handler *handler;//  用来指向该handle 绑定的 handler 结构体

    struct list_head    d_node;//  作为一个链表节点挂接到与他绑定的input_dev->hlist 链表上
    struct list_head    h_node;//  作为一个链表节点挂接到与他绑定的handler->hlist 链表上
};

在input_register_handle函数中,将handle挂到对应input_dev的h_list链表中,使用d_node挂载;也会将handle挂到对应handler的h_list中,使用h_node挂载。

#input_register_handle函数调用关系如下
int input_register_handle(struct input_handle *handle);
	  list_add_tail_rcu(&handle->d_node,&dev->h_list);#将d_node挂载到h_list下
	  list_add_tail(&handle->h_node,&handler->h_list);#将h_ndde挂载到h_list下
	  if (handler->start)
      	handler->start(handle);
      	#以上两句表示,如果handler定义了start()函数,则执行该函数

通过以上步骤,便将handler和input_device关联起来了。
事件上报
之前我们说过input_handle_event函数是核心层提供的事件上报函数,那接下来就跟一下这个函数,如下:

#input_handle_event函数调用关系如下
input_handle_event(struct input_dev *dev,unsigned int type,unsigned int code, int value);
	 if ((disposition &INPUT_PASS_TO_DEVICE) && dev->event)
     	dev->event(dev,type, code, value);
     #以上两句表示,如果该事件需要传递给设备,则调用设备的event函数
     if(disposition & INPUT_PASS_TO_HANDLERS)
     	input_pass_event(dev, type, code, value);
     #以上两句表示,如果事件需要传递给handler处理,则调用input_pass_event函数处理
#input_pass_event函数调用关系如下
input_pass_event(struct input_dev *dev,unsigned int type, unsigned int code, int value);
	 handle= rcu_dereference(dev->grab);
	 #以上一句表示,通过struct input_dev的grab字段获得与该input_dev占有的handle
	  if(handle)
      		handle->handler->event(handle,type, code, value);
        else
        	list_for_each_entry_rcu(handle,&dev->h_list, d_node)  #一般情况下走这里
	 			if (handle->open) 
     				handle->handler->event(handle,type,code, value);
     #以上三局句组成循环,第一句遍历dev中的h_list,该链表是和该input_dev相关联的所有handle的集合
     #如果引用计数非0,则调用handler中的event函数继续想上层传递事件
     #如果引用计数为0,表示该handler没有被打开,用户进程没有使用该handler,所以不必向上层传递事件
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值