8.input子系统基础之按键

1.什么是input子系统

1.1、何为输入设备
1.2、linux中输入设备的编程模型
(1)命令行界面的输入类设备应用接口
(2)GUI界面带来的麻烦、不同的输入类设备也会带来麻烦
(3)struct input_event
1.3、input子系统简介
(1)linux的input子系统解决了什么问题
(2)input子系统分4个部分:应用层 + input event + input core + 硬件驱动
(3)input子系统如何工作
(4)事件驱动型GUI框架,如QT、VC等。


2.input设备应用层编程实践1

2.1、确定设备文件名
(1)应用层操作驱动有2条路:/dev目录下的设备文件,/sys目录下的属性文件
(2)input子系统用的/dev目录下的设备文件,具体一般都是在 /dev/input/eventn
(3)用cat命令来确认某个设备文件名对应哪个具体设备。我在自己的ubuntu中实测的键盘是event1,而鼠标是event3.

2.2、标准接口打开并读取文件
2.3、解析struct input_event

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <linux/input.h>
#include <time.h>
#include <pthread.h>

#define DEVICE_KEY "/dev/input/event1"
#define DEVICE_MOUSE "/dev/input/event3"

struct input_event env1;
struct input_event env2;

int fd1 = -1;
int fd2 = -1;
int ret = -1;
	
pthread_t tid1;
pthread_t tid2;
	
void *pth_fn1(void *args)
{
	for(;;)
	{
		ret = read(fd1, &env1, sizeof(struct input_event));
		if(ret > 0)
		{
			printf("ctime: %s -------------------------------", ctime((time_t *)&env1.time.tv_sec));
			printf("type = %hd code = %hd value = %d \n", env1.type, env1.code, env1.value);
			fflush(stdout);
			memset(&env1, 0, sizeof(struct input_event));
		}
	}
}

void *pth_fn2(void *args)
{
	for(;;)
	{	
		ret = read(fd2, &env2, sizeof(struct input_event));
		if(ret > 0)
		{
			printf("ctime: %s -------------------------------", ctime((time_t *)&env2.time.tv_sec));
			printf("type = %hd code = %hd value = %d \n", env2.type, env2.code, env2.value);
			fflush(stdout);
			memset(&env2, 0, sizeof(struct input_event));
		}	
	}	
}


int main(void)
{
	void *ret1 = NULL;
	void *ret2 = NULL;
	fd1 = open(DEVICE_KEY, O_RDONLY);
	if(fd1 < 0){
		perror("open1");
		goto err1;
	}
		
	
	fd2 = open(DEVICE_MOUSE, O_RDONLY);
	if(fd2 < 0){
		perror("open2");
		goto err2;	
	}
		
	memset(&env1, 0, sizeof(struct input_event));
	memset(&env2, 0, sizeof(struct input_event));
	
	pthread_create(&tid1, NULL, pth_fn1, NULL);
	pthread_create(&tid2, NULL, pth_fn2, NULL);	
	
	pthread_join(tid1, &ret1);  //wait thread return
	pthread_join(tid2, &ret2);  //wait thread return

return 0;
err3:
	close(fd2);
err2:
	close(fd1);
err1:
	return -1;
	
}

4.input子系统架构总览1

4.1、input子系统分为三层
(1)最上层:输入事件驱动层,用户通过该层得到数据
(2)中间层:输入核心层,管理和匹配
(3)最下层:输入设备驱动层,处理底层硬件的信息
 

我们自己编写的驱动就是处于最下层(驱动层),应用程序通过最上层得到事件


所有的输入设备的主设备号都是13,其通过次设备来将输入设备进行分类,如下图:

4.2、input类设备驱动开发方法
(1)输入事件驱动层和输入核心层不需要动,只需要编写设备驱动层
(2)设备驱动层编写的接口和调用模式已定义好,驱动工程师的核心工作量是对具体输入设备硬件的操作和性能调优。
(3)input子系统不算复杂,学习时要注意“标准模式”四个字。

#define EV_SYN			0x00  //表示支持所有事件
#define EV_KEY			0x01  //键盘或者按键,表示一个键码
#define EV_REL			0x02  //鼠标设备,表示一个相对的光标位置结果
#define EV_ABS			0x03  //手写板产生的值,其是一个绝对整数值
#define EV_MSC			0x04  //其他类型
#define EV_SW			0x05  //转换
#define EV_LED			0x11  //led
#define EV_SND			0x12  //声音设备
#define EV_REP			0x14  //允许重复输入按键类型
#define EV_FF			0x15
#define EV_PWR			0x16  //电源管理
#define EV_FF_STATUS		0x17
#define EV_MAX			0x1f
#define EV_CNT			(EV_MAX+1)


6.input设备注册

设备结构体:

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

	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 keycodemax;
	unsigned int keycodesize;
	void *keycode;
	int (*setkeycode)(struct input_dev *dev,
			  unsigned int scancode, unsigned int keycode);
	int (*getkeycode)(struct input_dev *dev,
			  unsigned int scancode, unsigned int *keycode);

	struct ff_device *ff;

	unsigned int repeat_key;
	struct timer_list timer;

	int sync;

	int abs[ABS_CNT];
	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_CNT];
	int absmin[ABS_CNT];
	int absfuzz[ABS_CNT];
	int absflat[ABS_CNT];
	int absres[ABS_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 *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;
}

设备id

struct input_device_id {

	kernel_ulong_t flags;//表示信息

	__u16 bustype;//总线类型
	__u16 vendor;  //制造商ID
	__u16 product;  //产品ID
	__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;  //额外信息
};

1.自动分配设备空间
 

struct input_dev *input_allocate_device(void)
{
	struct input_dev *dev;

	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);//初始化device结构
		mutex_init(&dev->mutex); //初始化互斥锁
		spin_lock_init(&dev->event_lock);//初始化自旋锁
		INIT_LIST_HEAD(&dev->h_list);//初始化链表
		INIT_LIST_HEAD(&dev->node);//初始化链表

		__module_get(THIS_MODULE);//模块引用计数加1
	}

	return dev;
}


(2)input_set_capability   //设置为的值

void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
	switch (type) {
	case EV_KEY://键盘或按键
		__set_bit(code, dev->keybit);
		break;
	case EV_REL://鼠标设备,表示一个相对光标位置结果
		__set_bit(code, dev->relbit);
		break;
	case EV_ABS://手写板产生的值,是一个绝对整数
		__set_bit(code, dev->absbit);
		break;
	case EV_MSC://其他类型
		__set_bit(code, dev->mscbit);
		break;
	case EV_SW:
		__set_bit(code, dev->swbit);
		break;
	case EV_LED:
		__set_bit(code, dev->ledbit);
		break;
	case EV_SND:
		__set_bit(code, dev->sndbit);
		break;
	case EV_FF:
		__set_bit(code, dev->ffbit);
		break;
	case EV_PWR:
		/* do nothing */
		break;
	default:
		printk(KERN_ERR
			"input_set_capability: unknown type %u (code %u)\n",
			type, code);
		dump_stack();
		return;
	}

	__set_bit(type, dev->evbit);
}

2.注册设备函数

int input_register_device(struct input_dev *dev)
{
	static atomic_t input_no = ATOMIC_INIT(0);//注意这里的static
	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);//确保dev->evbit中没有提到的位掩码是干净的

	/*
	 * 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)//如果处理KEY的函数没有定义就用默认的
		dev->getkeycode = input_default_getkeycode;

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

	dev_set_name(&dev->dev, "input%ld",//设置名字,input_no为stattic,因此会累加
		     (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);
	printk(KERN_INFO "input: %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)//从所有handler中找
		input_attach_handler(dev, handler);//挂接handler和设备

	input_wakeup_procfs_readers();

	mutex_unlock(&input_mutex);

	return 0;
}

3.匹配inpu_dev和handler

input-attach-handler()函数用来匹配input dev和handler,只有匹配成功,才能进行下,一步的关联操作。

input match device (函数用来与input dev和handler进行匹配。handler的id table表中定义了其支持的input dev设备。

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)
		printk(KERN_ERR
			"input: failed to attach handler %s to device %s, "
			"error: %d\n",
			handler->name, kobject_name(&dev->dev.kobj), error);

	return error;
}

static const struct input_device_id *input_match_device(struct input_handler *handler,
							struct input_dev *dev)
{
	const struct input_device_id *id;
	int i;

	for (id = handler->id_table; id->flags || id->driver_info; id++) {
		//重所有handler中匹配
		if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)//如果定义了,表示需要匹配flags
			if (id->bustype != dev->id.bustype)//表示不能和已经定义的handler匹配
				continue;

		if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
			if (id->vendor != dev->id.vendor)
				continue;

		if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
			if (id->product != dev->id.product)
				continue;

		if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
			if (id->version != dev->id.version)
				continue;

		MATCH_BIT(evbit,  EV_MAX);//匹配
		MATCH_BIT(keybit, KEY_MAX);
		MATCH_BIT(relbit, REL_MAX);
		MATCH_BIT(absbit, ABS_MAX);
		MATCH_BIT(mscbit, MSC_MAX);
		MATCH_BIT(ledbit, LED_MAX);
		MATCH_BIT(sndbit, SND_MAX);
		MATCH_BIT(ffbit,  FF_MAX);
		MATCH_BIT(swbit,  SW_MAX);

		if (!handler->match || handler->match(handler, dev))
			return id;
	}

	return NULL;
}

7.handler 和  handle 注册

1.   注册handler

input_handler结构体:

struct input_handler {

	void *private;//表示特定驱动的数据
            //处理发送给设备的事件
	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
	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);
                          //连接handler和input_dev
	void (*disconnect)(struct input_handle *handle);//断开handler和input_dev
	void (*start)(struct input_handle *handle);

	const struct file_operations *fops;//handler实现的操作文件集
	int minor;//次设备号
	const char *name;//handler的名字

	const struct input_device_id *id_table;//表示驱动能够处理的表

	struct list_head	h_list;//连接到于这个handler相联系的下一个handler
	struct list_head	node;//链接到全局的handler链表中,所有的handler都在上面
};

函数:

int input_register_handler(struct input_handler *handler)
{
	struct input_dev *dev;
	int retval;


	retval = mutex_lock_interruptible(&input_mutex);
	if (retval)
		return retval;

	INIT_LIST_HEAD(&handler->h_list);

	if (handler->fops != NULL) {
		if (input_table[handler->minor >> 5]) { //除以32
			retval = -EBUSY;
			goto out;
		}
		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);


	input_wakeup_procfs_readers();

 out:
	mutex_unlock(&input_mutex);
	return retval;
}

2.   注册handle

  1. input_handle是用来连接input_dev和input_handler的一个中间结构体。

  2. 事件通过, input_handle从input_dev发送到input handler,

  3. 或者从input_handler发送到input_dev进行处理。

  4. 在使用input handle之前,需要对其进行注册,注册函数是input register-handle().

 

结构体:

struct input_handle {

	void *private;//handler的私有数据

	int open;//表示handle是否正在被使用,当使用时,会将事件分发给设备处理
	const char *name;//handle名字

	struct input_dev *dev;//handle依附的设备
	struct input_handler *handler;//handle依附的handler

	struct list_head	d_node;//放到 设备     相关联的handle链表中
	struct list_head	h_node;//放到 handler  相关联的handle链表中
};

注册函数:由handler的evdev_connect来调用这个挂接函数

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{  //...........
	error = input_register_handle(&evdev->handle);
}
int input_register_handle(struct input_handle *handle)
{
	struct input_handler *handler = handle->handler;  //取出handler
	struct input_dev *dev = handle->dev;  //取出设备
	int error;

	/*
	 * We take dev->mutex here to prevent race with
	 * input_release_device().
	 */
	error = mutex_lock_interruptible(&dev->mutex);
	if (error)
		return error;

	/*
	 * Filters go to the head of the list, normal handlers
	 * to the tail.
	 */
	if (handler->filter)
		list_add_rcu(&handle->d_node, &dev->h_list);//挂接设备
	else
		list_add_tail_rcu(&handle->d_node, &dev->h_list);

	mutex_unlock(&dev->mutex);

	/*
	 * Since we are supposed to be called from ->connect()
	 * which is mutually exclusive with ->disconnect()
	 * we can't be racing with input_unregister_handle()
	 * and so separate lock is not needed here.
	 */
	list_add_tail_rcu(&handle->h_node, &handler->h_list);//挂接handler

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

	return 0;
}

handle,handler和device的关系————————————

  1. 结点1,2,3表示input_dev设备,其通过input_dev->node变量连接到全局输入设备链表input_dev list中。
  2. 结点4、5、6表示input_handler处理器,其通过input_handler->noce连接到全局handler处理器链表input_handler list中。
  3. 结点7是一个input_handle的结构体,其用来连接input_dev和input_handler, input_handle的dev成员指向了对应的input_dev设备, input_handle的handler成员指向了对应的input_handler。
  4. 另外,结点7的input_handle通过d_node连接到了结点2的input_dev上的h_list链表上。
  5. 另一方面,结点7的input_handle通过h_node连接到了结点5的input_handler的h_list链表上。

通过这种关系,将input_dev和input_handler联系了起来。

从本质上讲,input_dev与handler是多对多的关系,从上图可以看出来,一个input_dev可以对应多个handler,一个handler也可以对应多个input_dev。因为在匹配的时候,

一个input_dev会与所有的handler都进行匹配的,并不是匹配成功一次就退出。

从图中可以看出来,一个handle就是用来记录系统中一对匹配成功的handler和device,我们可以从这个handle出发得到handler的信息,还可以得到device的信息。所以正因为有这样的

功能,所以可以由handler经过handle最终获取到device的信息,同理也可以从device从发经过handle最终获取到handler的信息。这种运用方法将会在后面的分析中看到。

总结:#####################################

核心层(其实就是驱动框架)提供的服务有哪些:

(1)创建设备类、注册字符设备

(2)向设备驱动层提供注册接口

(3)提供上层handler和下层device之间的匹配函数

(4)上层和下层通过handle来匹配

 

8.evdev输入事件驱动分析

evdev输入事件驱动,为输入子系统提供了一个默认的事件处理方法。其接收来自底,层驱动的大多数事件,并使用相应的逻辑对其进行处理。evdev输入事件驱动从底层接收事件信息,将其反映到sys文件系统中,用户程序通过对svs文件系统的操作,就能够达到处理事件的能力。

1.evdev的初始化-----------------------------------------------

static int __init evdev_init(void)
{
	return input_register_handler(&evdev_handler);
}

2.evdev成员解析-------------------------------------------------------------------------------

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,
};

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

第06行,定义了minor为EVDEV MINOR BASE (64) 。因为一个handler可以处理32个设备,所以evdev handler所能处理的设备文件范围为(13.64)~(13,64+32) ,其中13是所有输入设备的主设备号。

evdev. ids没有定义flags,也没有定义匹配属性值。

这个evdev ids的意思就是: evdev handler可以匹配所有input dev设备,也就是所有的input dev发出的事件,都可以由evdev handler来处理。另外,从前面的分析可以知道,匹配成功之后会调用, handler->connect()函数,对该函数的介绍如下:

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

	for (minor = 0; minor < EVDEV_MINORS; minor++)
		if (!evdev_table[minor])//找到可以用的次设备号
			break;

	if (minor == EVDEV_MINORS) {
		printk(KERN_ERR "evdev: 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);//定义在/dev/input/evevt(minor)文件名
	evdev->exist = 1;
	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);//主设备号 = 13
	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);//添加到iput设备数组中
	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;
}

第04~06行,声明了一些必要的局部变量。
第07~13行, for循环中的EVDEV MINORS定义为32,表示evdev handler所表示的32个设备文件。evdev talbe是一个struct evdev类型的数组, struct evdev是模块使用的封装结构,与具体的输入设备有关。
第08行,这一段代码的在 evdev talbe找到为空的那一项,当找到为空的一项,便结束for循环。这时, minor就是数组中第一项为空的序号。

第10到13行,如果没有空闲的表项,则退出。
第14~16行,分配一个struct evdev的空间,如果分配失败,则退出。
第17~20行,对分配的evdev结构进行初始化,主要对链表、互斥锁和等待队列做必要的初始化。在evdev中,封装了一个handle结构,这个结构与handler是不同的。可以把handle看成是handler和input_device的信息集合体,这个结构用来联系匹配成功的handler和input_device.

第21行,对evdev命一个名字,这个设备的名字形如eventx,例如eventl. event2和event3等。最大有32个设备,这个设备将在/dev/input/目录下显示。
第23-27行,对evdev进行必要的初始化。其中,主要对handle进行初始化,这此初始化的目的是使input dev和input handler联系起来。
第28-33行,在设备驱动模型中注册一个evdev->dev的设备,并初始化一个 evdev->dev的设备。这里,使evdev->dev所属的类指向input class。这样在/sysfs中创建的设备目录就会在/sys/class/input/下显示。

第34行,调用input register-handle()函数注册一个input handle结构体。
第37行,注册handle,如果成功,那么调用evdev install chrdev将evdev table的minor项指向evdev。
第40行,将evdev->device注册到sysfs文件系统中。
第41~50行,进行一些必要的错误处理。

2.2   evdev_event函数***********将传入事件传递给所有连接的客户端。

static void evdev_event(struct input_handle *handle,
			unsigned int type, unsigned int code, int value)
{
	struct evdev *evdev = handle->private;
	struct evdev_client *client;
	struct input_event event;
	struct timespec ts;

	ktime_get_ts(&ts);//获得当前时间
	event.time.tv_sec = ts.tv_sec;//秒
	event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;//纳秒
	event.type = type;//获得重驱动传来的类型
	event.code = code;
	event.value = value;

	rcu_read_lock();//加读锁

	client = rcu_dereference(evdev->grab);
	if (client)
		evdev_pass_event(client, &event);
	else
		list_for_each_entry_rcu(client, &evdev->client_list, node)
			evdev_pass_event(client, &event);

	rcu_read_unlock();

	wake_up_interruptible(&evdev->wait);//等待直到中断
}

2.3  fops

用于

static const struct file_operations evdev_fops = {
	.owner		= THIS_MODULE,
	.read		= evdev_read,
	.write		= evdev_write,
	.poll		= evdev_poll,
	.open		= evdev_open,
	.release	= evdev_release,
	.unlocked_ioctl	= evdev_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= evdev_ioctl_compat,
#endif
	.fasync		= evdev_fasync,
	.flush		= evdev_flush
};

对主设备号为INPUT_MAJOR的设备结点进行操作,会将操作集转换成handler的操作集。

在evdev handler中定义了一个fops集合,被赋值为evdev_fops的指针。


evdev_fops结构体是一个file_operations的类型。当用户层调用类似代码 "open("/dev/input/event1", O RDONLY)函数打开设备结点时,会调用evdev_fops中的 evdev_read()函数。

 

3.evdev设备的打开----------------------------------------------------------------

3.1evdev_open函数

evdev_fops结构体是一个file_operations的类型。当用户层调用类似代码 "open("/dev/input/event1", O RDONLY)函数打开设备结点时,会调用evdev_fops中的 evdev_read()函数,该函数的代码如下:

client会用来装载设备传过来的信息,然后在等待应用层读取

static int evdev_open(struct inode *inode, struct file *file)
{
	struct evdev *evdev;
	struct evdev_client *client;
	int i = iminor(inode) - EVDEV_MINOR_BASE;//得到在evdev_table[]中的序号
	int error;

	if (i >= EVDEV_MINORS)
		return -ENODEV;

	error = mutex_lock_interruptible(&evdev_table_mutex);
	if (error)
		return error;
	evdev = evdev_table[i];//得到evdev
	if (evdev)
		get_device(&evdev->dev);
	mutex_unlock(&evdev_table_mutex);

	if (!evdev)
		return -ENODEV;
	//初始化一个client,用来关联evdev
	client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
	if (!client) {
		error = -ENOMEM;
		goto err_put_evdev;
	}

	spin_lock_init(&client->buffer_lock);
	snprintf(client->name, sizeof(client->name), "%s-%d",
			dev_name(&evdev->dev), task_tgid_vnr(current));
	wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
	client->evdev = evdev;
	evdev_attach_client(evdev, client);//挂接到client_list上

	error = evdev_open_device(evdev);//打开输入设备
	if (error)
		goto err_free_client;

	file->private_data = client;//挂接到文件的私有数据上
	nonseekable_open(inode, file);

	return 0;
}

3.2evdev_open_device函数

evdev_open_device()函数用来打开相应的输入设备,使设备准备好接收或者发送数据。evdev_open_device()函数先获得互斥锁,然后检查设备是否存在,并判断设备是否已经被打开。如果没有打开,则调用input_open_device()函数打开设备。evdev_open_device()函数的代码如下:

static int evdev_open_device(struct evdev *evdev)
{
	int retval;

	retval = mutex_lock_interruptible(&evdev->mutex);
	if (retval)
		return retval;

	if (!evdev->exist)//判断是否存在
		retval = -ENODEV;
	else if (!evdev->open++) {//如果是第一次打开,就进行下一步
		retval = input_open_device(&evdev->handle);//执行核心的打开函数
		if (retval)
			evdev->open--;
	}

	mutex_unlock(&evdev->mutex);
	return retval;
}

3.2input_open_device函数

在这个函数中,递增handle的打开计数。如果是第一次打开,则调用input dev的open()函数。

int input_open_device(struct input_handle *handle)
{
	struct input_dev *dev = handle->dev;
	int retval;

	retval = mutex_lock_interruptible(&dev->mutex);
	if (retval)
		return retval;

	if (dev->going_away) {
		retval = -ENODEV;
		goto out;
	}

	handle->open++;//handle的打开加一

	if (!dev->users++ && dev->open)
		retval = dev->open(dev);//执行驱动的打开函数

	if (retval) {
		dev->users--;
		if (!--handle->open) {//减少打开次数
			/*
			 * Make sure we are not delivering any more events
			 * through this handle
			 */
			synchronize_rcu();
		}
	}

 out:
	mutex_unlock(&dev->mutex);
	return retval;
}

事件如何从驱动传递到应用层---------------------------------------------------

参考了Linux典藏大典驱动开发  P323

注意:只有设备文件被打开时,才会传送事件

通过在应用的   中断函数  中调用   input_report_key()  来开始传递

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

input子系统下层通过调用input_event函数项核心层上报数据

/*************** input .c  ****************/

input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value) //327

     input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)

        input_pass_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)

            handle = rcu_dereference(dev->grab);//得到handle,grab 就是handler

            handle->handler->event(handle, type, code, value);            //  最终会调用到handler 中的event函数

/*********************  evdev.c  ************/

                evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value

                         //再这里面会把驱动的事件赋值给event

                  evdev_pass_event(struct evdev_client *client,struct input_event *event)  //挂接到client上

                      client->buffer[client->head++] = *event;     //  会将input输入事件数据存放在evdev_client结构体中的缓冲去中

 

如果没有为input device强制指定handler,即为grab赋值,就会遍历input_device->hlist上的handle成员。如果该handle被打开,表示该设备 ,已经被一个用户进程使用。就会调用与输入设备对应的handler的event)函数。注意,只有在handle被打开的情况下才会接收到事件,这就是说,只有设备被用户要向用户空间导出信息。事

当我们的应用层通过open打开event0这个设备节点时最终会调用到input_init函数中注册的字符设备input时注册的file_operations->open() 函数

/*************** input .c  ****************/

input_open_file(struct inode *inode, struct file *file)

    handler = input_table[iminor(inode) >> 5]  //查找对应的handler

    handler->fops->open()    //执行对应的open函数  

/*********************  evdev.c  ************/

       evdev_open(struct inode *inode, struct file *file)

          evdev = evdev_table[i];

          evdev_open_device

               input_open_device

                    input_dev->open()         //  最终就是执行input设备中的open函数

          file->private_data = evdev_client;   //把事件挂接到文件的私有数据上

 

所以当我们在应用层调用read函数时,最终会调用到handler->fops->read函数

evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)

   struct evdev_client *client = file->private_data;  //取事件

    evdev_fetch_next_event(struct evdev_client *client,struct input_event *event)

        *event = client->buffer[client->tail++]      //  将evdev_client->buffer中的数据取走

    input_event_to_user

        copy_to_user(buffer, &compat_event,sizeof(struct input_event_compat))                  //  拷贝到用户空间

/********************************************************************************************/

参考:https://www.cnblogs.com/deng-tao/p/6094049.html

 

驱动发送事件的函数:

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_KEY, code, !!value);  //key类型
}
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_REL, code, value);
}
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_ABS, code, value);
}
static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_FF_STATUS, code, value);
}
static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_SW, code, !!value);
}
static inline void input_sync(struct input_dev *dev)
{
	input_event(dev, EV_SYN, SYN_REPORT, 0);
}
static inline void input_mt_sync(struct input_dev *dev)
{
	input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
}


9.按键驱动层源码分析

1 如何找到bsp中按键驱动源码

(1)锁定目标:板载按键驱动
(2)确认厂家提供的BSP是否已经有驱动

通过对input中的文件进行读写来判断是否存在驱动
(3)找到bsp中的驱动源码

  1. 同过对input下设备文件name 的读,来获得驱动的名字
  2. 通过查找哪些被编译来判断

2. 源码细节实现分析

结构体:

static struct platform_driver s3c_button_device_driver = {
	.probe		= s3c_button_probe,
	.remove		= s3c_button_remove,
	.suspend	= s3c_button_suspend,
	.resume		= s3c_button_resume,
	.driver		= {
		.name	= "s3c-button",
		.owner	= THIS_MODULE,
	}
};

//平台设备
static struct platform_device s3c_device_button = {
	.name	= "s3c-button",
	.id		= -1,
};

2.1 注册和卸载函数:

static int __init s3c_button_init(void)
{   
	platform_device_register(&s3c_device_button);       

	return platform_driver_register(&s3c_button_device_driver);
}

static void __exit s3c_button_exit(void)
{
	platform_driver_unregister(&s3c_button_device_driver);
	platform_device_unregister(&s3c_device_button);   
}

2.2 设备识别时挂接的函数:

static int s3c_button_probe(struct platform_device *pdev)
{
	int i;
	int ret;

	/* gph0_1 (power) */
	ret = gpio_request(S5PV210_GPH0(1), "GPH0");
	if(ret)
		printk("button-x210: request gpio GPH0(1) fail");
	s3c_gpio_setpull(S5PV210_GPH0(1), S3C_GPIO_PULL_UP);
	s3c_gpio_cfgpin(S5PV210_GPH0(1), S3C_GPIO_SFN(0));
	s3c_button_history[0] = gpio_get_value(S5PV210_GPH0(1));
	input = input_allocate_device();//分配空间
	if(!input) 
		return -ENOMEM;

	set_bit(EV_KEY, input->evbit);//设置位图

	for(i = 0; i < MAX_BUTTON_CNT; i++)
		set_bit(s3c_Keycode[i], input->keybit);//设置驱动支持哪些键的值

	input->name = "s3c-button";
	input->phys = "s3c-button/input0";

	input->id.bustype = BUS_HOST;//总线类型
	input->id.vendor = 0x0001;//制造商
	input->id.product = 0x0001;//产品id
	input->id.version = 0x0100;//版本号

	input->keycode = s3c_Keycode;//挂接key值

	if(input_register_device(input) != 0)//注册设备
	{
		printk("s3c-button input register device fail!!\n");

		input_free_device(input);
		return -ENODEV;
	}

	/* Scan timer init */
	init_timer(&timer);//初始化时钟
	timer.function = s3cbutton_timer_handler;//时间到了要执行的函数

	timer.expires = jiffies + (HZ/100);//设置为10毫秒检测一次按键的状态
	add_timer(&timer);//添加到系统中

	printk("s3c button Initialized!!\n");

	return 0;
}



2.3 时间到了要执行的函数

static void s3cbutton_timer_handler(unsigned long data)
{
	int flag;

	/* power */
	flag = gpio_get_value(S5PV210_GPH0(1));//判断历史的值,如果没变就不执行
	if(flag != s3c_button_history[0])
	{
		if(flag)
		{
			input_report_key(input, s3c_Keycode[0], 0);
		}
		else
		{
			input_report_key(input, s3c_Keycode[0], 1);
		}
		s3c_button_history[0] = flag;//设置值
	}

	/* Kernel Timer restart */
	mod_timer(&timer,jiffies + HZ/100);//更新时间
}

 

11.中断方式按键驱动实战

实例代码:

由于有的按键没有中断,就没有添加中断,但是有两个可以正常执行。

#include <linux/input.h> 
#include <linux/module.h> 
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>

#include <asm/irq.h> 
#include <asm/io.h>
#include <mach/irqs.h>


/*
 * X210:
 *
 * POWER  -> EINT1   -> GPH0_1
 * LEFT   -> EINT2   -> GPH0_2
 * DOWN   -> EINT3   -> GPH0_3
 * UP     -> KP_COL0 -> GPH2_0
 * RIGHT  -> KP_COL1 -> GPH2_1
 * MENU   -> KP_COL3 -> GPH2_3 (KEY_A)
 * BACK   -> KP_COL2 -> GPH2_2 (KEY_B)
 */
 
#define KEY_NUM (3)

struct int_type{
	unsigned int num;
	unsigned char name[8];
	unsigned int  gpio;
	unsigned char gpio_name[8];
};

struct work_struct work;
struct workqueue_struct *wq;

static struct input_dev *button_dev;
static int x210_Keycode[KEY_NUM] = {KEY_POWER, KEY_UP, KEY_DOWN};
/*, KEY_LEFT, KEY_RIGHT, KEY_A, KEY_B};*/

static unsigned int x210_button_history[KEY_NUM] = {0};

static struct int_type int_arry[KEY_NUM] = {
	{
		.num  = IRQ_EINT1,
		.name = {"POWER"},
		.gpio = S5PV210_GPH0(1),
		.gpio_name = "GPH0_1",
	},{
		.num = IRQ_EINT2,
		.name = {"LEFT"},
		.gpio = S5PV210_GPH0(2),
		.gpio_name = "GPH0_2",
	},{
		.num = IRQ_EINT3,
		.name = {"DOWN"},
		.gpio = S5PV210_GPH0(3),
		.gpio_name = "GPH0_3",
	},
};
/*{
		.num = KP_COL0,
		.name = {"UP"},
		.gpio = S5PV210_GPH2(0),
		.gpio_name = "GPH2_0",
	},{
		.num = KP_COL1,
		.name = {"RIGHT"},
		.gpio = S5PV210_GPH2(0),
		.gpio_name = "GPH2_1",
	},{
		.num = KP_COL2,
		.name = {"MENU"},
		.gpio = S5PV210_GPH2(1),
		.gpio_name = "GPH2_2",
	},{
		.num = KP_COL3,
		.name = {"BACK"},
		.gpio = S5PV210_GPH2(2),
		.gpio_name = "GPH2_3",
	},
}*/

//bottom
static void ket_bottom(struct work_struct *work)
{	
	printk("This is key_bottom function\n");
}

//top
static irqreturn_t button_interrupt(int irq, void *dummy) 
{
	int flag;
	int i;
	
	
	for(i = 0; i < KEY_NUM; i++)
	{
		if(irq == int_arry[i].num){
			s3c_gpio_cfgpin(int_arry[i].gpio, S3C_GPIO_SFN(0x0));// input模式
			flag = gpio_get_value(int_arry[i].gpio);
			s3c_gpio_cfgpin(int_arry[i].gpio, S3C_GPIO_SFN(0x0f));// eint2模式

			input_report_key(button_dev, x210_Keycode[i], !flag);
			input_sync(button_dev);
			break;
		}
		//printk("interrupt function\n");
	}

	if (!work_pending(&work)) 
	{
		queue_work(wq, &work);
	}
	
	return IRQ_HANDLED; 
}

static int x210_button_probe(struct platform_device *pdev) 
{ 
	int error;
	int ret = -1;
	int i;

	button_dev = input_allocate_device();  //分配空间
	if (!button_dev) { 
		printk(KERN_ERR "button.c: Not enough memory\n");
		error = -ENOMEM;
		goto err_free_irq; 
	}
	
	set_bit(EV_KEY, button_dev->evbit);//设置位图
	for(i = 0; i < KEY_NUM; i++)
	{
		set_bit(x210_Keycode[i], button_dev->keybit);//设置驱动支持哪些键的值		
	}

	button_dev->name = "x210-button";
	button_dev->phys = "x210-button/input0";

	button_dev->id.bustype = BUS_HOST;//总线类型
	button_dev->id.vendor = 0x0001;//制造商
	button_dev->id.product = 0x0001;//产品id
	button_dev->id.version = 0x0100;//版本号

	button_dev->keycode = x210_Keycode;//挂接key值
	
	error = input_register_device(button_dev);
	if (error) { 
		printk(KERN_ERR "button.c: Failed to register device\n");
		goto err_free_dev; 
	}
	
/********* interrupt ***************/	
	for(i = 0; i < KEY_NUM; i++)
	{
		ret = gpio_request(int_arry[i].gpio, int_arry[i].gpio_name);
		if (ret) { 
			printk(KERN_ERR "button.c: Can't requset gpio  %s\n", int_arry[i].gpio_name);
			//return -EBUSY; 
		}
		s3c_gpio_cfgpin(int_arry[i].gpio, S3C_GPIO_SFN(0x0f));
		x210_button_history[0] = gpio_get_value(int_arry[i].gpio);
	}
	

	wq = create_singlethread_workqueue("work_x210");
	if (!wq) {
		printk(KERN_ERR"Could not create workqueue\n");
		goto err_free_dev;
	}
	flush_workqueue(wq);

	INIT_WORK(&work, ket_bottom);	

	for(i = 0; i < KEY_NUM; i++)
	{
		ret = request_irq((unsigned int)int_arry[i].num, button_interrupt, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, int_arry[i].name, NULL);
		if (ret) { 
			printk(KERN_ERR "button.c: Can't allocate irq %s\n", int_arry[i].name);
			return -EBUSY; 
		}		
	}

	
	printk("x210 button initialized!\n");
	return 0;
	
err_free_dev:
	input_free_device(button_dev);
err_free_irq:
	for(i = 0; i < KEY_NUM; i++)
	{
		free_irq((unsigned int)int_arry[i].num, button_interrupt);		
	}
	return error;
}

static int x210_button_remove(struct platform_device *pdev)
{
	int i;
	for(i = 0; i < KEY_NUM; i++)
	{
		free_irq((unsigned int)int_arry[i].num, NULL);		
	}	
	input_unregister_device(button_dev); 
	for(i = 0; i < KEY_NUM; i++)
	{
		free_irq((unsigned int)int_arry[i].num, button_interrupt);		
	}

	destroy_workqueue(wq);	
	return  0;
}

#define x210_button_suspend	NULL
#define x210_button_resume	NULL

//platform_driver
static struct platform_driver x210_button_device_driver = {
	.probe		= x210_button_probe,
	.remove		= x210_button_remove,
	.suspend	= x210_button_suspend,
	.resume		= x210_button_resume,
	.driver		= {
		.name	= "x210-button",
		.owner	= THIS_MODULE,
	}
};

//platform_device
static struct platform_device x210_device_button = {
	.name	= "x210-button",
	.id		= -1,
};

static int __init button_init(void) 
{ 
	platform_device_register(&x210_device_button);       
	return platform_driver_register(&x210_button_device_driver);
}

static void __exit button_exit(void) 
{
	platform_driver_unregister(&x210_button_device_driver);
	platform_device_unregister(&x210_device_button);   
}

module_init(button_init); 
module_exit(button_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ljj <123456@qq.com>");
MODULE_DESCRIPTION("Key x210 driver for x210 button.");
MODULE_ALIAS("platform:x210-button");

 

 

 

 

 

 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值