8.input子系统基础之按键
转自 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、驱动实践
上一篇: 10.块设备驱动介绍