Input子系统

前言

本文主要介绍在linux下input子系统框架,以及如何运作的,自己编写的驱动是怎样的方式嵌入到input子系统中去的。

背景

在linux中每出现一个子系统都是有原因的,为什么会有这个东西,以及这个东西有什么好处,针对一个复杂的系统,他一定会有各式各样的外设,比如鼠标键盘、遥感等等,这些都属于输入设备,这些设备的功能都很单一,获取位置数据,获取键盘键值等。当然可以给每个设备都写一个驱动,那么管理就会变得混乱,应用层使用起来就得一一对应比较麻烦,从这些设备反馈的也就是一个数值,可以进行抽象处理。
输入设备(键盘、鼠标等)的驱动都是采用字符设备、混杂设备处理的
可以对分散的、不同类别的输入设备进行统一的驱动,所以才出现了输入子系统
好处:

  1. 统一硬件差异但功能相近的设备管理
  2. 提供了用于分发输入报告给用户应用程序的简单的事件(event)接口
  3. 驱动不必创建、管理/dev节点以及相关的访问方法,不用在驱动中特定的创建设备节点及class
    简化驱动流程

重要的类型

//自己管理的链表
static LIST_HEAD(input_dev_list); //所有struct input_dev 的node都挂着
static LIST_HEAD(input_handler_list);//所有struct input_handler的node都挂这

struct input_dev {
	const char *name;
	struct input_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;

	struct list_head	h_list;  //input_handle的node挂这里
	struct list_head	node; //挂到全局的input_dev_list下
	}
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);//connect会去new一个struct evdev对象
	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;//input_handle的node挂这里
	struct list_head	node;//挂在全局的input_handler_list下
}
struct input_handle { //主要是记录input_dev和input_handler,作为两个的桥梁

	void *private;

	int open;
	const char *name;

	struct input_dev *dev;
	struct input_handler *handler;

	struct list_head	d_node;//挂到input_dev的内置list里面
	struct list_head	h_node;//挂到handler的内置list里面
};

struct evdev {//主要用户控件的open和read就是靠这个设备
 int open;
 struct input_handle handle;
 wait_queue_head_t wait;
 struct evdev_client __rcu *grab;
 struct list_head client_list; //将来evdev_client都挂在这
 spinlock_t client_lock; /* protects client_list */
 struct mutex mutex;
 struct device dev;
 struct cdev cdev;
 bool exist;
};
evdev_client 和evdev联系比较大 
struct evdev_client {//主要是存储,事件发生的时候键的类型,code以及value
 unsigned int head;
 unsigned int tail;
 ...
 struct evdev *evdev;
 struct list_head node;
 unsigned int clk_type;
...
 struct input_event buffer[];
};

在这里插入图片描述

驱动注册

handle有点像设备驱动模型里面的bus他左边的input_dev,右边是input_handler,
而eventdev 又是和handle关系比较密切。

框架init

z:\myworkspeace\kernel\myOrgPiPc2PRO\drivers\input\input.c

subsys_initcall(input_init);

intput初始化proc的内容以及注册了一个class下面的register_chardev
只是占用了一些设备号,并没有生成dev下面的设备。
在这里插入图片描述

一些注册的api

input_register_device

这个接口一般是用户写一个输入设备的驱动会用到这个接口,一般使用
gpstInputDev = input_allocate_device(); 申请一个设备
然后赋值指定这个输入设备有哪些能力等驱动相关的这个具体在OrangePIPC2—红外模块(一)这里详细分析。
在这里插入图片描述

整体流程大概是这样之,用户的驱动调用input_register_device,input子系统拿这个dev去全局的input_handler_list中去match(//匹配的方式:先由匹配dev的id和handler->id_table(这里还会根据id_table的flag进行是否有额外的匹配),然后在handler的match方法匹配),match成功后,执行这个handler的connect方法,在这里就会创建一个evdev 设备,

int input_register_device(struct input_dev *dev){
...
if (!dev->getkeycode)
		dev->getkeycode = input_default_getkeycode;

	if (!dev->setkeycode)
		dev->setkeycode = input_default_setkeycode;

	error = device_add(&dev->dev);
	if (error)
		goto err_free_vals;
	list_add_tail(&dev->node, &input_dev_list);

	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler);//拿这个dev去handler中去match
}
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
	const struct input_device_id *id;
	int error;

	id = input_match_device(handler, dev);//匹配的方式:先由匹配id和handler->id_table,然后在handler的match方法匹配
	if (!id)
		return -ENODEV;

	error = handler->connect(handler, dev, id);//匹配成功后执行connect函数
	if (error && error != -ENODEV)
		pr_err("failed to attach handler %s to device %s, error: %d\n",
		       handler->name, kobject_name(&dev->dev.kobj), error);

	return error;
}

input_register_handler

X:\myProjects\pipc2Pro\kernel
myOrgPiPc2PRO\drivers\input\evdev.c

这个有点像具体的处理方式比如有鼠标的handler,键盘的handler以及摇杆的handler
input_register_handler(&joydev_handler);
input_register_handler(&mousedev_handler);
input_register_handler(&input_leds_handler);
input_register_handler(&evdev_handler); //如果没有特别的匹配就选择这个匹配

connect:大致做了啥

  1. 创建一个struct evdev *evdev;
  2. 初始化client_list这个链表用来管理evdev_client
  3. 注册handle input_register_handle(&evdev->handle);
  4. 注册设备 节点 /dev/event%d
  5. 为设备节点注册fops evdev_fops, 设备节点的read就会调到这个fops的read
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,
};
int input_register_handler(struct input_handler *handler)
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id){
	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
	INIT_LIST_HEAD(&evdev->client_list);
	dev_set_name(&evdev->dev, "event%d", dev_no);

	evdev->handle.dev = input_get_device(dev);
	evdev->handle.name = dev_name(&evdev->dev);
	evdev->handle.handler = handler;
	evdev->handle.private = evdev;
	error = input_register_handle(&evdev->handle);
	cdev_init(&evdev->cdev, &evdev_fops);

	error = cdev_device_add(&evdev->cdev, &evdev->dev);
}

input_register_handle

int input_register_handle(struct input_handle *handle){
...
	if (handler->filter)
		list_add_rcu(&handle->d_node, &dev->h_list);
	else
		list_add_tail_rcu(&handle->d_node, &dev->h_list);
		...
	list_add_tail_rcu(&handle->h_node, &handler->h_list);

	if (handler->start)
		handler->start(handle);
}

事件的上报

上面的一些接口的描述基本是在设备的建立初期,一些成员变量的初始化以及匹配过程,
但还缺少一个主动触发的接口,才能将需要的数据上报出去。上面fops中有read接口获取内部数据,而时间上报的接口就是网这个buffer空间里写东西,一旦有东西了,read就不阻塞返回了。
input_event(gpstInputDev, EV_KEY, KEY_POWER, 0);

void input_event(struct input_dev *dev,
  unsigned int type, unsigned int code, int value)
{
主要将event type  code 和value装到buf里面,同样应用成拿到的也是一个大结构体,里面包含了type code 和value
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值