天天看点

Linux- Input输入子系统

#Input输入子系统个人总结

Input输入子系统在驱动开发中的重要性,大家都知道, 就不多说了。突发奇想想写博客,来写写工作中常遇到的知识点,总结一下,为了更加清晰的理解。

我这里主要参考了几篇blog,如有侵权,还请谅解。

Linux input子系统分析之一:软件层次

http://blog.csdn.net/yueqian_scut/article/details/47903853

全网络对Linux input子系统最清晰、详尽的分析

http://blog.csdn.net/yueqian_scut/article/details/48026955

input子系统全面分析

http://www.cnblogs.com/lcw/p/3293302.html

一、完整的input子系统包括的层次和整个框架。

系统整体框图

Linux- Input输入子系统

input子系统框图

Linux- Input输入子系统

二、input 子系统 所涉及到的结构体和函数。

struct input_dev 设备结构体,记录了设备的信息和支持的事件

struct input_dev {
	const char *name;   //设备名
	const char *phys;   //设备节点名称
	const char *uniq;   // 唯一的ID号,
	struct input_id id; // 输入设备ID,用于和event handler 匹配

	unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; //bitmap of device properties and quirks

	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];  //设备支持的事件类型
	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //按键 ex:上下左右 home
	unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //相对坐标 ex 鼠标
	unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //绝对坐标 ex 触摸屏
	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; /* average number of events generated by the device in a packet (between EV_SYN/SYN_REPORT events). Used by event handlers to estimate size of the buffer needed to hold events.*/

	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_slot *mt; //pointer to multitouch state
	int mtsize;
	int slot;
	int trkid;

	struct input_absinfo *absinfo; /* array of struct input_absinfo elements holding information about absolute axes (current value, min, max, flat, fuzz, resolution) */

	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; //类似私有指针,可以访问到事件处理接口event

	spinlock_t event_lock; //自旋锁
	struct mutex mutex; //用于open close函数的连续访问互斥

	unsigned int users; //设备使用计数
	bool going_away; //不知

	bool sync; //是否上传同步ok

	struct device dev; //设备结构体

	struct list_head	h_list; //handle list
	struct list_head	node; // input dev list
};
           

分配和销毁input设备

struct input_dev *input_allocate_device(void);
void input_free_device(struct input_dev *dev)。
           

注册和注销input设备

int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)
           
struct input_dev *input_allocate_device(void)
{
	struct input_dev *dev;
 //动态申请内存,使用GFP_KERNEL方式,注意GFP_KERNEL可能导致睡眠,不能在中断中调用这个函数 
	dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
	//分配成功执行的代码,进行成员的默认填充
	if (dev) {
		dev->dev.type = &input_dev_type; 
		dev->dev.class = &input_class; //支持热插拔的结构体
		device_initialize(&dev->dev); //类设备初始化,添加进input类设备模型中  

		mutex_init(&dev->mutex);   //初始化互斥锁
		spin_lock_init(&dev->event_lock); //初始化自旋锁
		INIT_LIST_HEAD(&dev->h_list);//初始化handle链表
		INIT_LIST_HEAD(&dev->node); //初始化输入设备链表

		__module_get(THIS_MODULE);
	}

	return dev;
}

truct input_dev *input_dev = input_allocate_device();  
input_dev->name = "tiny4412 Touchscreen";  
input_dev->phys = "tiny4412/input0";  
input_dev->id.bustype = BUS_HOST;  
input_dev->id.vendor = 0x0001;  
input_dev->id.product = 0x0002;  
input_dev->id.version = 0x0100;  
input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_set_capability(barom->input_dev, EV_KEY, KEY_DOWN);
           
int input_register_device(struct input_dev *dev)
	void input_unregister_device(struct input_dev *dev)
           
int input_register_device(struct input_dev *dev)
{
	static atomic_t input_no = ATOMIC_INIT(0);
	struct input_handler *handler;
	const char *path;
	int error;

	/* Every input device generates EV_SYN/SYN_REPORT events. */
	__set_bit(EV_SYN, dev->evbit);

	/* KEY_RESERVED is not supposed to be transmitted to userspace. */
	__clear_bit(KEY_RESERVED, dev->keybit);

	/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
	input_cleanse_bitmasks(dev);

	if (!dev->hint_events_per_packet)
		dev->hint_events_per_packet =
				input_estimate_events_per_packet(dev);

	/*
	 * If delay and period are pre-set by the driver, then autorepeating
	 * is handled by the driver itself and we don't do it in input.c.
	 */
	init_timer(&dev->timer);
	if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
		dev->timer.data = (long) dev;
		dev->timer.function = input_repeat_key;
		dev->rep[REP_DELAY] = 250;
		dev->rep[REP_PERIOD] = 33;
	}

	if (!dev->getkeycode)
		dev->getkeycode = input_default_getkeycode;

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

	dev_set_name(&dev->dev, "input%ld",
		     (unsigned long) atomic_inc_return(&input_no) - 1);

	error = device_add(&dev->dev);
	if (error)
		return error;

	path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
	pr_info("%s as %s\n",
		dev->name ? dev->name : "Unspecified device",
		path ? path : "N/A");
	kfree(path);

	error = mutex_lock_interruptible(&input_mutex);
	if (error) {
		device_del(&dev->dev);
		return error;
	}

	list_add_tail(&dev->node, &input_dev_list);

	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler);

	input_wakeup_procfs_readers();

	mutex_unlock(&input_mutex);

	return 0;
}
           

input_register_device

进一步初始化输入设备,例如连击事件;

注册输入设备到input类中;

把输入设备挂到输入设备链表input_dev_list中;

查找并匹配输入设备对应的事件处理层,通过input_handler_list链表

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);
	if (!id)
		return -ENODEV;

	error = handler->connect(handler, dev, id);
	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;
}
           

在此之前我们还需要了解另一个结构体struct input_handler

struct input_handler {

	void *private; //driver-specific data

	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); //处理input dev 产生的数据,handler区处理和上报。
	bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//从"filter"中分离正常事件
	bool (*match)(struct input_handler *handler, struct input_dev *dev); //设备Id和handler的id_table 比较后,设备和处理器之间进行更细微的匹配
	
	int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); //handler和dev 匹配之后会被调用
	void (*disconnect)(struct input_handle *handle);//关闭handler从input dev
	void (*start)(struct input_handle *handle);//根据handle 启动handler

	const struct file_operations *fops;
	int minor;
	const char *name; //handler name,可以在/proc/bus/input/handlers 中看到

	const struct input_device_id *id_table;//a table for input_dev_ids this driver can handle 

	struct list_head	h_list;
	struct list_head	node;
};
           

数据包 和 上传数据包

struct input_event {
		struct timeval time;  //时间
		__u16 type;   // 数据类型:  按键数据/绝对数据/相对数据 
		__u16 code; // 类型中某个特定数据:  比如  上键,下键, 回车键,...
		__s32 value; // 状态: 按下会抬起
	};

上传数据event ,可以看出input_report_xxx() 是对input_event()进一步封装。
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);

static inline void input_report_key(struct input_dev *dev, 
unsigned int code, int value)
{
	input_event(dev, EV_KEY, code, !!value);
}

static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_ABS, code, value);
}
           

了解了上门的结构体和函数,现在我们开始整个input子系统,分三个层次,input device,input core和input handle。

input device

dev = input_allocate_device()
dev->name = "xxx"
...//填充dev。

//设置input_dev 能够产生的数据类型
dev->evbit[0] = BIT_MASK(EV_KEY);
set_bit(EV_ABS,dev->evbit);

input_register_device(dev)

	将当前input dev 加入到input dev list

	//遍历input handler list ,取出每个handler,和input dev比对,匹配,所有的input dev 都可以和 event handler匹配。然后执行handler 的connect 方法。
	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler);
	id = input_match_device(handler, dev);
	handler->connect(handler, dev, id);
	
           

input core

input_init()
	class_register()//创建类。 /sys/class/input/dev->name/
	register_chrdev() //创建设备。
           

input handler

evdev.c
static struct input_handler evdev_handler = {
	.event		= evdev_event,
	.connect	= evdev_connect, // 匹配后执行的函数
	.disconnect	= evdev_disconnect,
	.fops		= &evdev_fops,
	.minor		= EVDEV_MINOR_BASE,
	.name		= "evdev",
	.id_table	= evdev_ids,
};

input_register_handler(&evdev_handler)
	//将当前的handler加入到全局的数组
	//input_table[handler->minor >> 5] = handler;
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;

	for (minor = 0; minor < EVDEV_MINORS; minor++)
		if (!evdev_table[minor])
			break;

	if (minor == EVDEV_MINORS) {
		pr_err("no more free evdev devices\n");
		return -ENFILE;
	}

	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
	if (!evdev)
		return -ENOMEM;

	INIT_LIST_HEAD(&evdev->client_list);
	spin_lock_init(&evdev->client_lock);
	mutex_init(&evdev->mutex);
	init_waitqueue_head(&evdev->wait);

	dev_set_name(&evdev->dev, "event%d", minor);
	evdev->exist = true;
	evdev->minor = minor;

	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);

	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;

 err_cleanup_evdev:
	evdev_cleanup(evdev);
 err_unregister_handle:
	input_unregister_handle(&evdev->handle);
 err_free_evdev:
	put_device(&evdev->dev);
	return error;
}

           

继续阅读