我是靠谱客的博主 美满红酒,最近开发中收集的这篇文章主要介绍Linux驱动分析——input输入子系统1、朱有鹏老师的视频课程笔记和应用测试代码:2、input子系统架构分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

stm32mp157  盘古开发板  Linux内核版本4.19

目录

1、朱有鹏老师的视频课程笔记和应用测试代码:

2、input子系统架构分析

2.1、输入核心层源码分析

2.1.1、首先是核心模块注册input_init

2.1.2、提供给设备驱动层的接口函数input_allocate_device、input_set_capability、input_register_device

2.1.3、handler和device的匹配

2.1.4、事件驱动层的接口函数input_register_handler、input_register_handle

2.2、最上层:输入事件驱动层源码分析

2.3、最下层:输入设备驱动层


1、朱有鹏老师的视频课程笔记和应用测试代码:

***********《朱有鹏老师嵌入式linux核心课程》 ***********
《5.linux驱动开发-第5部分-5.8.input子系统基础之按键》

--------------------------------------------------------
本课程由朱老师物联网大讲堂推出并提供技术支持,课件可打包下载
网盘地址:http://yunpan.cn/cjVy3RAgfDufK 访问密码 4ad7
技术交流QQ群:朱老师物联网讲堂1群 397164505
朱老师个人QQ:1264671872
--------------------------------------------------------

第一部分、章节目录
5.8.1.什么是input子系统
5.8.2.input设备应用层编程实践1
5.8.3.input设备应用层编程实践2
5.8.4.input子系统架构总览1
5.8.5.input子系统架构总览2
5.8.6.输入核心层源码分析1
5.8.7.输入核心层源码分析2
5.8.8.输入事件驱动层源码分析
5.8.9.输入设备驱动层源码分析1
5.8.10.输入设备驱动层源码分析2
5.8.11.中断方式按键驱动实战1
5.8.12.中断方式按键驱动实战2



第二部分、章节介绍
5.8.1.什么是input子系统
	本节全面介绍input子系统的概念和来源、解决的主要问题,目的是让大家对linux中输入类设备有一个全面了解
5.8.2.input设备应用层编程实践1
	本节实践编写应用层程序,操作键盘和鼠标这些常见input类设备,目的是让大家先学会使用输入类设备,后面再来分析驱动。
5.8.3.input设备应用层编程实践2
	本节接着上节对读上来的数据进行解析,分析其规律并且和设备本身特性进行关联分析。
5.8.4.input子系统架构总览1
	本节详细介绍input子系统的三层结构以及各层的功能特点。
5.8.5.input子系统架构总览2
	本节介绍input子系统下编写驱动的路线和方法。
5.8.6.输入核心层源码分析1
	本节分析输入核心层,主要是模块装载和开放给其他层的接口的分析。
5.8.7.输入核心层源码分析2	
	本节接着分析输入核心层,主要是handler和device的匹配、安装部分的源码分析。
5.8.8.输入事件驱动层源码分析
	本节对输入事件层源码分析,主要以evdev.c为例分析了event handler的安装函数、数据上报函数的实现。
5.8.9.输入设备驱动层源码分析1
	本节分析输入设备驱动层,以x210自带的按键驱动为例进行分析。
5.8.10.输入设备驱动层源码分析2	
	本节接着分析按键驱动,主要是一些源码细节探究。
5.8.11.中断方式按键驱动实战1
	本节开始按键驱动实战,先找到内核提供的模版,并且对模版程序进行分析讲解。
5.8.12.中断方式按键驱动实战2	
	本节以模版驱动为基础,结合x210开发板的情况进行驱动移植、编译、测试、修改。
	
	

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


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

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


5.8.3.input设备应用层编程实践2
5.8.3.1、解析键盘事件数据
5.8.3.2、解析鼠标事件数据


5.8.4.input子系统架构总览1
5.8.4.1、input子系统分为三层
(1)最上层:输入事件驱动层,evdev.c和mousedev.c和joydev.c属于这一层
(2)中间层:输入核心层,input.c属于这一层
(3)最下层:输入设备驱动层,drivers/input/xxx 文件夹下
5.8.4.2、input类设备驱动开发方法
(1)输入事件驱动层和输入核心层不需要动,只需要编写设备驱动层
(2)设备驱动层编写的接口和调用模式已定义好,驱动工程师的核心工作量是对具体输入设备硬件的操作和性能调优。
(3)input子系统不算复杂,学习时要注意“标准模式”四个字。


5.8.5.input子系统架构总览2


5.8.6.输入核心层源码分析1
5.8.6.1、核心模块注册input_init
(1)class_register
(2)input_proc_init
(3)register_chrdev
5.8.6.2、设备驱动层的接口函数
(1)input_allocate_device
(2)input_set_capability
(3)input_register_device


5.8.7.输入核心层源码分析2
5.8.7.1、handler和device的匹配
(1)input_attach_handler
		input_match_device		匹配device和handler
		handler->connect(handler, dev, id)		连接device和handler
5.8.7.2、事件驱动层的接口函数
(1)input_register_handler
(2)input_register_handle


5.8.8.输入事件驱动层源码分析
5.8.8.1、input_handler
5.8.8.2、evdev_connect
5.8.8.3、evdev_event


5.8.9_10.输入设备驱动层源码分析1_2
5.8.9.1、先找到bsp中按键驱动源码
(1)锁定目标:板载按键驱动
(2)确认厂家提供的BSP是否已经有驱动
(3)找到bsp中的驱动源码
5.8.9.2、按键驱动源码初步分析
(1)模块装载分析
(2)平台总线相关分析
(3)确定重点:probe函数
5.8.9.3、源码细节实现分析
(1)gpio_request
(2)input_allocate_device
(3)input_register_device
(4)timer


5.8.11.中断方式按键驱动实战1
5.8.11.1、模板
(1)input类设备驱动模式非常固定,用参考模版修改即可
(2)新建驱动项目并粘贴模版内容
5.8.11.2、模板驱动的解析
5.8.11.3、着手移植驱动


5.8.12.中断方式按键驱动实战2
5.8.12.1、驱动移植细节
5.8.12.2、驱动实践

上图即为input子系统架构的三层:drivers(最下层,输入设备驱动层)、core(中间层,核心层)、handlers(最上层,输入事件驱动层)

应用程序测试代码:

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


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

#define X210_KEY			"/dev/input/event1"


int main(void)
{
	int fd = -1, ret = -1;
	struct input_event ev;
	
	// 第1步:打开设备文件
	fd = open(X210_KEY, O_RDONLY);
	if (fd < 0)
	{
		perror("open");
		return -1;
	}
	
	while (1)
	{
		// 第2步:读取一个event事件包
		memset(&ev, 0, sizeof(struct input_event));
		ret = read(fd, &ev, sizeof(struct input_event));
		if (ret != sizeof(struct input_event))
		{
			perror("read");
			close(fd);
			return -1;
		}
		
		// 第3步:解析event包,才知道发生了什么样的输入事件
		printf("-------------------------n");
		printf("type: %hdn", ev.type);
		printf("code: %hdn", ev.code);
		printf("value: %dn", ev.value);
		printf("n");
	}
	
	
	// 第4步:关闭设备
	close(fd);
	
	return 0;
}

2、input子系统架构分析

2.1、输入核心层源码分析

相关代码linux_kernellinux-stdriversinput目录下input.c

2.1.1、首先是核心模块注册input_init

static int __init input_init(void)
{
	int err;

	//在/sys/class下面注册一个input类
	err = class_register(&input_class);
	if (err) {
		pr_err("unable to register input_dev classn");
		return err;
	}
	//proc目录下面
	err = input_proc_init();
	if (err)
		goto fail1;

	//注册字符设备驱动,主设备号是13
	err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
				     INPUT_MAX_CHAR_DEVICES, "input");
	if (err) {
		pr_err("unable to register char major %d", INPUT_MAJOR);
		goto fail2;
	}

	return 0;

 fail2:	input_proc_exit();
 fail1:	class_unregister(&input_class);
	return err;
}

static void __exit input_exit(void)
{
	input_proc_exit();
	unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0),
				 INPUT_MAX_CHAR_DEVICES);
	class_unregister(&input_class);
}

subsys_initcall(input_init);
module_exit(input_exit);

2.1.2、提供给设备驱动层的接口函数input_allocate_device、input_set_capability、input_register_device

/**
 * input_allocate_device - allocate memory for new input device
 *
 * Returns prepared struct input_dev or %NULL.
 *
 * NOTE: Use input_free_device() to free devices that have not been
 * registered; input_unregister_device() should be used for already
 * registered devices.
 */
 //相当于动态申请一个input dev设备变量,并对其进行初步必要的初始化
struct input_dev *input_allocate_device(void)
{
	static atomic_t input_no = ATOMIC_INIT(-1);
	struct input_dev *dev;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (dev) {
		dev->dev.type = &input_dev_type;
		dev->dev.class = &input_class;
		device_initialize(&dev->dev);
		mutex_init(&dev->mutex);
		spin_lock_init(&dev->event_lock);
		timer_setup(&dev->timer, NULL, 0);
		INIT_LIST_HEAD(&dev->h_list);
		INIT_LIST_HEAD(&dev->node);

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

		__module_get(THIS_MODULE);
	}

	return dev;
}
EXPORT_SYMBOL(input_allocate_device);
/**
 * input_register_device - register device with input core
 * @dev: device to be registered
 *
 * This function registers device with input core. The device must be
 * allocated with input_allocate_device() and all it's capabilities
 * set up before registering.
 * If function fails the device must be freed with input_free_device().
 * Once device has been successfully registered it can be unregistered
 * with input_unregister_device(); input_free_device() should not be
 * called in this case.
 *
 * Note that this function is also used to register managed input devices
 * (ones allocated with devm_input_allocate_device()). Such managed input
 * devices need not be explicitly unregistered or freed, their tear down
 * is controlled by the devres infrastructure. It is also worth noting
 * that tear down of managed input devices is internally a 2-step process:
 * registered managed input device is first unregistered, but stays in
 * memory and can still handle input_event() calls (although events will
 * not be delivered anywhere). The freeing of managed input device will
 * happen later, when devres stack is unwound to the point where device
 * allocation was made.
 */
 //将input_dev类型的变量注册进系统
int input_register_device(struct input_dev *dev)
{
	struct input_devres *devres = NULL;
	struct input_handler *handler;
	unsigned int packet_size;
	const char *path;
	int error;

	if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) {
		dev_err(&dev->dev,
			"Absolute device without dev->absinfo, refusing to registern");
		return -EINVAL;
	}

	if (dev->devres_managed) {
		devres = devres_alloc(devm_input_device_unregister,
				      sizeof(*devres), GFP_KERNEL);
		if (!devres)
			return -ENOMEM;

		devres->input = dev;
	}

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

	packet_size = input_estimate_events_per_packet(dev);
	if (dev->hint_events_per_packet < packet_size)
		dev->hint_events_per_packet = packet_size;

	dev->max_vals = dev->hint_events_per_packet + 2;
	dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
	if (!dev->vals) {
		error = -ENOMEM;
		goto err_devres_free;
	}

	/*
	 * 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.
	 */
	if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD])
		input_enable_softrepeat(dev, 250, 33);

	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;

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

	error = mutex_lock_interruptible(&input_mutex);
	if (error)
		goto err_device_del;

	//添加到输入设备列表里面
	list_add_tail(&dev->node, &input_dev_list);

	//遍历所有的handler,看哪个和刚注册进来的设备驱动能够匹配,
	//如果匹配就会将其绑定,并且生成应用层可以操作的文件接口
	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler);//负责匹配和链接的
	//handler和device的挂接是handle结构体,即在handle结构体
	//中记录配对的device和handler结构体指针

	input_wakeup_procfs_readers();

	mutex_unlock(&input_mutex);

	if (dev->devres_managed) {
		dev_dbg(dev->dev.parent, "%s: registering %s with devres.n",
			__func__, dev_name(&dev->dev));
		devres_add(dev->dev.parent, devres);
	}
	return 0;

err_device_del:
	device_del(&dev->dev);
err_free_vals:
	kfree(dev->vals);
	dev->vals = NULL;
err_devres_free:
	devres_free(devres);
	return error;
}
EXPORT_SYMBOL(input_register_device);
/**
 * input_set_capability - mark device as capable of a certain event
 * @dev: device that is capable of emitting or accepting event
 * @type: type of the event (EV_KEY, EV_REL, etc...)
 * @code: event code
 *
 * In addition to setting up corresponding bit in appropriate capability
 * bitmap the function also adjusts dev->evbit.
 */
 //声明驱动向上层应用上报哪些信息,在设置驱动向上上报事件的能力
 //专用的能力方面设置
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:
		input_alloc_absinfo(dev);
		if (!dev->absinfo)
			return;

		__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:
		pr_err("%s: unknown type %u (code %u)n", __func__, type, code);
		dump_stack();
		return;
	}

	__set_bit(type, dev->evbit);
}
EXPORT_SYMBOL(input_set_capability);
static void input_handle_event(struct input_dev *dev,
			       unsigned int type, unsigned int code, int value)
{
	int disposition = input_get_disposition(dev, type, code, &value);

	if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
		add_input_randomness(type, code, value);

	if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
		dev->event(dev, type, code, value);

	if (!dev->vals)
		return;

	if (disposition & INPUT_PASS_TO_HANDLERS) {
		struct input_value *v;

		if (disposition & INPUT_SLOT) {
			v = &dev->vals[dev->num_vals++];
			v->type = EV_ABS;
			v->code = ABS_MT_SLOT;
			v->value = dev->mt->slot;
		}

		v = &dev->vals[dev->num_vals++];
		v->type = type;
		v->code = code;
		v->value = value;
	}

	if (disposition & INPUT_FLUSH) {
		if (dev->num_vals >= 2)
			input_pass_values(dev, dev->vals, dev->num_vals);
			// 最终执行了handle->handler里面的event函数
		dev->num_vals = 0;
	} else if (dev->num_vals >= dev->max_vals - 2) {
		dev->vals[dev->num_vals++] = input_value_sync;
		input_pass_values(dev, dev->vals, dev->num_vals);
		dev->num_vals = 0;
	}

}

/**
 * input_event() - report new input event
 * @dev: device that generated the event
 * @type: type of the event
 * @code: event code
 * @value: value of the event
 *
 * This function should be used by drivers implementing various input
 * devices to report input events. See also input_inject_event().
 *
 * NOTE: input_event() may be safely used right after input device was
 * allocated with input_allocate_device(), even before it is registered
 * with input_register_device(), but the event will not reach any of the
 * input handlers. Such early invocation of input_event() may be used
 * to 'seed' initial state of a switch or initial position of absolute
 * axis, etc.
 */
void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)
{
	unsigned long flags;

	if (is_event_supported(type, dev->evbit, EV_MAX)) {

		spin_lock_irqsave(&dev->event_lock, flags);
		input_handle_event(dev, type, code, value); // 上报事件
		spin_unlock_irqrestore(&dev->event_lock, flags);
	}
}
EXPORT_SYMBOL(input_event);

2.1.3、handler和device的匹配

input_attach_handler
        input_match_device        匹配device和handler
        handler->connect(handler, dev, id)        连接device和handler

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

	//匹配device和handler
	id = input_match_device(handler, dev);
	if (!id)
		return -ENODEV;

	//链接device和handler
	error = handler->connect(handler, dev, id);
	if (error && error != -ENODEV)
		pr_err("failed to attach handler %s to device %s, error: %dn",
		       handler->name, kobject_name(&dev->dev.kobj), error);

	return error;
}
/**
 * struct input_handler - implements one of interfaces for input devices
 * @private: driver-specific data
 * @event: event handler. This method is being called by input core with
 *	interrupts disabled and dev->event_lock spinlock held and so
 *	it may not sleep
 * @events: event sequence handler. This method is being called by
 *	input core with interrupts disabled and dev->event_lock
 *	spinlock held and so it may not sleep
 * @filter: similar to @event; separates normal event handlers from
 *	"filters".
 * @match: called after comparing device's id with handler's id_table
 *	to perform fine-grained matching between device and handler
 * @connect: called when attaching a handler to an input device
 * @disconnect: disconnects a handler from input device
 * @start: starts handler for given handle. This function is called by
 *	input core right after connect() method and also when a process
 *	that "grabbed" a device releases it
 * @legacy_minors: set to %true by drivers using legacy minor ranges
 * @minor: beginning of range of 32 legacy minors for devices this driver
 *	can provide
 * @name: name of the handler, to be shown in /proc/bus/input/handlers
 * @id_table: pointer to a table of input_device_ids this driver can
 *	handle
 * @h_list: list of input handles associated with the handler
 * @node: for placing the driver onto input_handler_list
 *
 * Input handlers attach to input devices and create input handles. There
 * are likely several handlers attached to any given input device at the
 * same time. All of them will get their copy of input event generated by
 * the device.
 *
 * The very same structure is used to implement input filters. Input core
 * allows filters to run first and will not pass event to regular handlers
 * if any of the filters indicate that the event should be filtered (by
 * returning %true from their filter() method).
 *
 * Note that input core serializes calls to connect() and disconnect()
 * methods.
 */
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);
	// dev和handler匹配
	bool (*match)(struct input_handler *handler, struct input_dev *dev);
	// 将dev和handler链接
	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;
};
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)];

	// BITS_TO_LONGS的作用是计算EV_CNT这么多需要多少个32位的bit来存放记录
	// 用来标注自己的驱动有哪些没有哪些,比如只有3个按键...但是系统支持的有很多
	// 比如鼠标、触摸屏、键盘等,想鼠标的左右击也需要标注,这就是用其中的一个bit
	// 来标注。     去标注自己支持的事件
	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;
};

2.1.4、事件驱动层的接口函数input_register_handler、input_register_handle

/**
 * input_register_handler - register a new input handler
 * @handler: handler to be registered
 *
 * This function registers a new input handler (interface) for input
 * devices in the system and attaches it to all input devices that
 * are compatible with the handler.
 */
int input_register_handler(struct input_handler *handler)
{
	struct input_dev *dev;
	int error;

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

	INIT_LIST_HEAD(&handler->h_list);

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

	mutex_unlock(&input_mutex);
	return 0;
}
EXPORT_SYMBOL(input_register_handler);
/**
 * input_register_handle - register a new input handle
 * @handle: handle to register
 *
 * This function puts a new input handle onto device's
 * and handler's lists so that events can flow through
 * it once it is opened using input_open_device().
 *
 * This function is supposed to be called from handler's
 * connect() method.
 */
int input_register_handle(struct input_handle *handle)
{
	struct input_handler *handler = handle->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);

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

	return 0;
}
EXPORT_SYMBOL(input_register_handle);

即在input_handle中记录input_handler和dev的配对情况,民政局。

总结,核心层主要的功能是为设备驱动层(drivers层)和输入事件驱动层(handler层)提供注册接口,同时将这两层能够配对的设备做好配对绑定,然后提供从drivers到handlers数据传输通道,即将dev的数据直接传输给已经配对的handle。

2.2、最上层:输入事件驱动层源码分析

这一层的相关代码linux_kernellinux-stdriversinput目录下evdev.c和joydev.c

evdev.c文件里面,首先是模块的注册:

static struct input_handler evdev_handler = {
	.event		= evdev_event,
	.events		= evdev_events,//将数据事件数据放进buffer以及发信号通知应用层,都是这里面做的
	.connect	= evdev_connect,
	.disconnect	= evdev_disconnect,
	.legacy_minors	= true,
	//次设备号的基地址,有8个,均是32的整数倍,一个handler对应
	//多个dev的时候,第一个dev的次设备号是32*n,第二个是32*n+1...
	.minor		= EVDEV_MINOR_BASE,
	.name		= "evdev",
	.id_table	= evdev_ids,
};

static int __init evdev_init(void)
{
	//将上面声明的handler注册到核心层
	return input_register_handler(&evdev_handler);
}

static void __exit evdev_exit(void)
{
	input_unregister_handler(&evdev_handler);
}

module_init(evdev_init);
module_exit(evdev_exit);

MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Input driver event char devices");
MODULE_LICENSE("GPL");
/*
 * Create new evdev device. Note that input core serializes calls
 * to connect and disconnect.
 */
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{
	struct evdev *evdev;
	int minor;
	int dev_no;
	int error;

	minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
	if (minor < 0) {
		error = minor;
		pr_err("failed to reserve new minor: %dn", error);
		return error;
	}

	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
	if (!evdev) {
		error = -ENOMEM;
		goto err_free_minor;
	}

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

	dev_no = minor;
	/* Normalize device number if it falls into legacy range */
	if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
		dev_no -= EVDEV_MINOR_BASE;
	// 设备文件名的产生,event0,event1...次设备号是32*n+event后面的数字
	dev_set_name(&evdev->dev, "event%d", dev_no);

	// dev和handler配对时,需要先到handle这里登记,相当于民政局
	evdev->handle.dev = input_get_device(dev);
	evdev->handle.name = dev_name(&evdev->dev);
	evdev->handle.handler = handler;
	evdev->handle.private = evdev;

	// 这里的主设备是13,次设备号是32*n+minor,moinor在上面有赋值计算
	evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
	evdev->dev.class = &input_class;
	evdev->dev.parent = &dev->dev;
	evdev->dev.release = evdev_free;
	device_initialize(&evdev->dev);// 这个函数和下面的cdev_device_add函数一起构成device_register函数
	// device_register就是注册设备,在input.c中class_register是注册class下面的input类
	// 这里是注册input类里面的设备,如event1 , event2 , ...

	error = input_register_handle(&evdev->handle);	// 注册handle,民政局的登记册
	if (error)
		goto err_free_evdev;

	cdev_init(&evdev->cdev, &evdev_fops);

	error = cdev_device_add(&evdev->cdev, &evdev->dev);
	if (error)
		goto err_cleanup_evdev;

	return 0;

 err_cleanup_evdev:
	evdev_cleanup(evdev);
	input_unregister_handle(&evdev->handle);
 err_free_evdev:
	put_device(&evdev->dev);
 err_free_minor:
	input_free_minor(minor);
	return error;
}
static const struct file_operations evdev_fops = {
	.owner		= THIS_MODULE,
	.read		= evdev_read,	// 应用层调用的read,其实就是这个read
								// 这个evdev_read里面调用了capy_to_user
								// 传到用户空间
	.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,
	.llseek		= no_llseek,
};
static ssize_t evdev_read(struct file *file, char __user *buffer,
			  size_t count, loff_t *ppos)
{
	struct evdev_client *client = file->private_data;
	struct evdev *evdev = client->evdev;
	struct input_event event;
	size_t read = 0;
	int error;

	if (count != 0 && count < input_event_size())
		return -EINVAL;

	for (;;) {
		if (!evdev->exist || client->revoked)
			return -ENODEV;

		if (client->packet_head == client->tail &&
		    (file->f_flags & O_NONBLOCK))
			return -EAGAIN;

		/*
		 * count == 0 is special - no IO is done but we check
		 * for error conditions (see above).
		 */
		if (count == 0)
			break;

		while (read + input_event_size() <= count &&
		       evdev_fetch_next_event(client, &event)) {
			//写到用户空间
			if (input_event_to_user(buffer + read, &event))
				return -EFAULT;

			read += input_event_size();
		}

		if (read)
			break;

		if (!(file->f_flags & O_NONBLOCK)) {
			error = wait_event_interruptible(evdev->wait,	//阻塞等待有数据产生
					client->packet_head != client->tail ||
					!evdev->exist || client->revoked);
			if (error)
				return error;
		}
	}

	return read;
}

总结,这一层主要向应用层提供操作接口。

2.3、最下层:输入设备驱动层

代码目录linux-stdriversinputkeyboard    gpio_keys.c

static struct platform_driver gpio_keys_device_driver = {
	.probe		= gpio_keys_probe,
	.driver		= {
		.name	= "gpio-keys",
		.pm	= &gpio_keys_pm_ops,
		.of_match_table = gpio_keys_of_match,
	}
};

static int __init gpio_keys_init(void)
{
	return platform_driver_register(&gpio_keys_device_driver);
}

static void __exit gpio_keys_exit(void)
{
	platform_driver_unregister(&gpio_keys_device_driver);
}

late_initcall(gpio_keys_init);
module_exit(gpio_keys_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
MODULE_DESCRIPTION("Keyboard driver for GPIOs");
MODULE_ALIAS("platform:gpio-keys");
static const struct of_device_id gpio_keys_of_match[] = {
	{ .compatible = "gpio-keys", },
	{ },
};
MODULE_DEVICE_TABLE(of, gpio_keys_of_match);
static int gpio_keys_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
	struct fwnode_handle *child = NULL;
	struct gpio_keys_drvdata *ddata;
	struct input_dev *input;
	size_t size;
	int i, error;
	int wakeup = 0;

	if (!pdata) {
		pdata = gpio_keys_get_devtree_pdata(dev);
		if (IS_ERR(pdata))
			return PTR_ERR(pdata);
	}

	size = sizeof(struct gpio_keys_drvdata) +
			pdata->nbuttons * sizeof(struct gpio_button_data);
	ddata = devm_kzalloc(dev, size, GFP_KERNEL);
	if (!ddata) {
		dev_err(dev, "failed to allocate staten");
		return -ENOMEM;
	}

	ddata->keymap = devm_kcalloc(dev,
				     pdata->nbuttons, sizeof(ddata->keymap[0]),
				     GFP_KERNEL);
	if (!ddata->keymap)
		return -ENOMEM;

	// input.c中给input_dev分配内存的函数
	input = devm_input_allocate_device(dev);
	if (!input) {
		dev_err(dev, "failed to allocate input devicen");
		return -ENOMEM;
	}

	ddata->pdata = pdata;
	ddata->input = input;
	mutex_init(&ddata->disable_lock);

	platform_set_drvdata(pdev, ddata);
	input_set_drvdata(input, ddata);

	input->name = pdata->name ? : pdev->name;
	input->phys = "gpio-keys/input0";
	input->dev.parent = dev;
	input->open = gpio_keys_open;
	input->close = gpio_keys_close;

	input->id.bustype = BUS_HOST;
	input->id.vendor = 0x0001;
	input->id.product = 0x0001;
	input->id.version = 0x0100;

	input->keycode = ddata->keymap;
	input->keycodesize = sizeof(ddata->keymap[0]);
	input->keycodemax = pdata->nbuttons;

	/* Enable auto repeat feature of Linux input subsystem */
	if (pdata->rep)
		__set_bit(EV_REP, input->evbit);
	// 设置input里面的evbit支持EV_REP事件

	for (i = 0; i < pdata->nbuttons; i++) {
		const struct gpio_keys_button *button = &pdata->buttons[i];

		if (!dev_get_platdata(dev)) {
			// 从设备树取出对应的节点
			child = device_get_next_child_node(dev, child);
			if (!child) {
				dev_err(dev,
					"missing child device node for entry %dn",
					i);
				return -EINVAL;
			}
		}

		// 设置相关的按键引脚,在这里面有中断设置
		error = gpio_keys_setup_key(pdev, input, ddata,
					    button, i, child);
		if (error) {
			fwnode_handle_put(child);
			return error;
		}

		if (button->wakeup)
			wakeup = 1;
	}

	fwnode_handle_put(child);

	error = devm_device_add_group(dev, &gpio_keys_attr_group);
	if (error) {
		dev_err(dev, "Unable to export keys/switches, error: %dn",
			error);
		return error;
	}

	error = input_register_device(input);	// 注册设备
	if (error) {
		dev_err(dev, "Unable to register input device, error: %dn",
			error);
		return error;
	}

	device_init_wakeup(dev, wakeup);

	return 0;
}
// 上报事件
static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
{
	const struct gpio_keys_button *button = bdata->button;
	struct input_dev *input = bdata->input;
	unsigned int type = button->type ?: EV_KEY;
	int state;

	state = gpiod_get_value_cansleep(bdata->gpiod);
	if (state < 0) {
		dev_err(input->dev.parent,
			"failed to get gpio state: %dn", state);
		return;
	}

	if (type == EV_ABS) {
		if (state)
			input_event(input, type, button->code, button->value);// 上报事件
	} else {
		input_event(input, type, *bdata->code, state);
	}
	input_sync(input);
}

 

最后

以上就是美满红酒为你收集整理的Linux驱动分析——input输入子系统1、朱有鹏老师的视频课程笔记和应用测试代码:2、input子系统架构分析的全部内容,希望文章能够帮你解决Linux驱动分析——input输入子系统1、朱有鹏老师的视频课程笔记和应用测试代码:2、input子系统架构分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

评论列表共有 0 条评论

立即
投稿
返回
顶部