欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

8.input子系统基础之按键

程序员文章站 2022-06-03 22:49:40
...

转自 https://edu.csdn.net/lecturer/505 朱老师物联网大讲堂
《5.linux驱动开发-第5部分-5.8.input子系统基础之按键》

第一部分、章节目录
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.

#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"


int main(void)
{
	int fd = -1, ret = -1;
	struct input_event ev;
	
	// 第1步:打开设备文件
	fd = open(DEVICE_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("%s.\n", (unsigned char *)&ev);	
	}
	
	
	// 第4步:关闭设备
	close(fd);
	
	return 0;
}

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

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

#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: %hd\n", ev.type);
		printf("code: %hd\n", ev.code);
		printf("value: %d\n", ev.value);
		printf("\n");
	}
	
	
	// 第4步:关闭设备
	close(fd);
	
	return 0;
}

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文件夹class那一块,还有udev那一块
(2)input_proc_init文件系统proc那一块
(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这个是dev的爸爸,一个handler匹配或者被匹配多个dev
(2)input_register_handle它负责连接dev和handlr,让父子相认

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、着手移植驱动

#include <linux/input.h> 
#include <linux/module.h> 
#include <linux/init.h>
#include <asm/irq.h> 
#include <asm/io.h>

#include <mach/irqs.h>			// arch/arm/mach-s5pv210/include/mach/irqs.h
#include <linux/interrupt.h>
#include <linux/gpio.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 BUTTON_IRQ	IRQ_EINT2

static struct input_dev *button_dev;

static irqreturn_t button_interrupt(int irq, void *dummy) 
{ 
	int flag;

	s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0));		// input模式
	flag = gpio_get_value(S5PV210_GPH0(2));
	s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));		// eint2模式

	input_report_key(button_dev, KEY_LEFT, !flag);
	input_sync(button_dev);
	return IRQ_HANDLED; 
}

static int __init button_init(void) 
{ 
	int error;
	
	error = gpio_request(S5PV210_GPH0(2), "GPH0_2");
	if(error)
		printk("key-s5pv210: request gpio GPH0(2) fail");
	s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));		// eint2模式
	
	if (request_irq(BUTTON_IRQ, button_interrupt, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "button-x210", NULL)) 
	{ 
		printk(KERN_ERR "key-s5pv210.c: Can't allocate irq %d\n", BUTTON_IRQ);
		return -EBUSY; 
	}
	button_dev = input_allocate_device();
	if (!button_dev) 
	{ 
		printk(KERN_ERR "key-s5pv210.c: Not enough memory\n");
		error = -ENOMEM;
		goto err_free_irq; 
	}
	
	button_dev->evbit[0] = BIT_MASK(EV_KEY);
	button_dev->keybit[BIT_WORD(KEY_LEFT)] = BIT_MASK(KEY_LEFT);
	
	error = input_register_device(button_dev);
	if (error) 
	{ 
		printk(KERN_ERR "key-s5pv210.c: Failed to register device\n");
		goto err_free_dev; 
	}
	return 0;
	
err_free_dev:
	input_free_device(button_dev);
err_free_irq:
	free_irq(BUTTON_IRQ, button_interrupt);
	return error; 
}

static void __exit button_exit(void) 
{ 
	input_unregister_device(button_dev); 
	free_irq(BUTTON_IRQ, button_interrupt); 
}

module_init(button_init); 
module_exit(button_exit); 

MODULE_LICENSE("GPL");
MODULE_AUTHOR("aston <[email protected]>");
MODULE_DESCRIPTION("key driver for x210 button.");





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