文章目录
输入子系统学习
input 子系统层次结构图
驱动用于实时获取输入硬件的输入数据,事件处理层用于处理驱动报告的数据,并对上层提供标准的事件信息,输入核心层用来管理这两层并建立沟通的桥梁。
Input 子系统构建过程结构关系图
驱动模型
主要数据结构间的关系
输入子系统的作用和框架
input子系统概述
-
input子系统为一些常用的小规模数据传输的设备提供统一的数据上报系统,把数据以统一的格式上传到用户空间。
-
适用于input子系统的设备有摇杆、鼠标、键盘、触摸屏等。
设计成输入子系统:使得应用编程人员和驱动编程人员编程的时候变得简单统一
1, 兼容所有的输入设备
2, 统一的编程驱动方法(实现差异化硬件操作)
3, 统一的应用操作接口:/dev/input/event0,event1
open("/dev/input/event0"),
read(fd, struct input_event): struct input_event buff可以认为是一个统一的数据包
input子系统框架
框架:驱动分成三层
- input设备驱动层
input设备驱动层提供对硬件各寄存器的读写访问和将底层硬件的状态变化转换为标准的输入事件,再通过核心层提交给事件处理层。 - input核心层
input核心层对下提供了设备驱动层的编程接口,对上又提供了事件处理层的编程接口。 - input事件处理层
事件处理层就为我们用户空间的应用程序提供了统一访问设备的接口以及对驱动层提交来的事件进行处理。
input子系统相关结构体
input_handler这个结构体是事件驱动的主体,每一种处理方式对应一个handler结构体。注册input_handler,其实就是将
input_hangler加入到input_handler_list当中。使用input_register_handler注册。
在输入子系统的设备驱动中,最重要的数据结构是struct
input_dev,如下所示。需要完成的大部分工作都是围绕着它来的,它是驱动的主体。每个struct input_dev代表一个输入设备。
input_handle这个结构体用来连接input_dev和input_handler。
struct input_event是事件传送的载体,输入子系统的事件都是包装成struct input_event传给用户空间
input子系统框架
input子系统实例实现
input 核心层代码和input handler层需要在内核中必须有:
drivers/input/evdev.c // event handler
drivers/input/input.c // 核心层
make menuconfig 配置
Device Drivers --->
Input device support --->
-*- Generic input layer (needed for keyboard, mouse, ...) // input.c
<*> Event interface //input handler层--evdev.c
输入设备驱动重要的API
struct input_dev *input_allocate_device(void);
void input_free_device(struct input_dev *dev);
int input_register_device(struct input_dev *dev);
void input_unregister_device(struct input_dev *dev);
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);
static inline void input_sync(struct input_dev *dev);
输入设备驱动实现编写步骤一般如下(主要流程):
【1】分配一个input device对象,创建input_dev结构体变量
使用input_allocate_device分配一个输入设备的对象
struct input_dev *inputdev;
inputdev = input_allocate_device();
if(NULL == inputdev)
{
printk(KERN_ERR "input_allocate_device error \n");
return -ENOMEM;
}
【2】初始化input device对象
初始化输入设备对象,包括名字、路径、ID、能报告的事件类型和与编码表相关的内容
添加设备信息
在用户层被识别到/sys/class/input/目录下,主要是用来给用户层展示的信息
inputdev->name = "simple input key";
inputdev->phys = "key/input/input0";
inputdev->uniq = "simple key0 for 4412";
inputdev->id.bustype = BUS_HOST;
inputdev->id.vendor =0x1234 ;
inputdev->id.product = 0x8888;
inputdev->id.version = 0x0001;
设置位表(事件类型)
inputdev->evbit表示设备产生的数据类型,inputdev->keybit表示要设置按键的键值
//当前设备能够产生按键数据--将某个bit置1
__set_bit(EV_KEY, inputdev->evbit);
//表示当前设备能够产生power按键
子事件类型
//__set_bit(KEY_POWER, inputdev->keybit);
//另外一种设置bit的方式
inputdev->keybit[BIT_WORD(KEY_POWER)] |= BIT_MASK(KEY_POWER); // 116%32
【3】注册input_dev结构体变量
使用input_register_device注册设备输入设备
int input_register_device(struct input_dev *dev)
【4】上报事件:
在输入设备产生事件时使用input_event报考事件
事件类型:
EV_SYN 0x00 同步事件
EV_KEY 0x01 按键事件
EV_REL 0x02 相对坐标
EV_ABS 0x03 绝对坐标
EV_MSC 0x04 其它
EV_LED 0x11 LED
EV_SND 0x12 声音
EV_REP 0x14 Repeat
EV_FF 0x15 力反馈
将设备产生的数据上报给输入子系统,由系统把数据交给用户。
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
参数1:当前的input device上报数据
参数2:上报的是那种数据类型 EV_KEY,EV_ABS
参数3:具体数据是什么:KEY_POWER
参数4:值是是什么
【5】 使用input_sync同步事件
static inline void input_sync(struct input_dev *dev)
【6】注销input_dev结构体变量
在不需要输入设备时使用 input_unregister_device注销设备,
void input_unregister_device(struct input_dev *);
【7】释放input_dev结构体变量
用 input_free_device 释放其内存
void input_free_device(struct input_dev *dev);
【8】用户层读取
用户空间读到的数据:统一的数据包
用户调取read函数,以这种数据形式读取数据。
struct input_event {
struct timeval time; //时间戳
__u16 type; //数据类型
__u16 code;//具体数据是什么
__s32 value;//值是是什么
};
深入分析调用流程
input 子系统函数调用流程
input 子系统初始化:
input_handler 注册
当有设备注册时,会建立如下图
input_dev 注册
示例
simple_input.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <asm/io.h>
//设计一个全局设备对象--描述按键信息
typedef struct _key_desc{
int irqno;
void *reg_base;
}KEY_DESC_T;
KEY_DESC_T key_dev;
#define GPXCON_REG 0x11000C20
struct input_dev *inputdev;
int get_irqno_from_node(void)
{
// 获取到设备树中到节点
int irqno;
struct device_node *np = of_find_node_by_path("/key_int_node");
if(np)
{
printk("find node ok\n");
}else{
printk("find node failed\n");
}
// 通过节点去获取到中断号码
irqno = irq_of_parse_and_map(np, 0);
printk("irqno = %d\n", irqno);
return irqno;
}
irqreturn_t input_key_irq_handler(int irqno, void *devid)
{
//读取数据寄存器
int value = readl(key_dev.reg_base + 4) & (1<<2);
printk("-------%s-------------\n", __FUNCTION__);
if(value)
{//抬起
input_event(inputdev, EV_KEY, KEY_POWER, 0);
input_sync(inputdev);//上报数据结束
}
else
{
input_event(inputdev, EV_KEY, KEY_POWER, 1);
input_sync(inputdev);//上报数据结束
}
return IRQ_HANDLED;
}
/*
1,分配一个 input device 对象
2,初始化 input device 对象
3,注册 input device 对象
*/
static int __init simple_input_init(void)
{
int ret;
printk("-------%s-------------\n", __FUNCTION__);
//分配 input device 对象
inputdev = input_allocate_device();
if(NULL == inputdev)
{
printk(KERN_ERR "input_allocate_device error \n");
return -ENOMEM;
}
//初始化 input device 对象
//当前设备能够产生按键数据
__set_bit(EV_KEY, inputdev->evbit);
//表示当前设备能够产生power按键
__set_bit(KEY_POWER, inputdev->keybit);
//注册 input device 对象
ret = input_register_device(inputdev);
if(ret != 0)
{
printk(KERN_ERR "input_register_device error\n");
goto err_0;
}
//硬件的初始化--地址映射
key_dev.reg_base = ioremap(GPXCON_REG, 8);
if(NULL == key_dev.reg_base)
{
printk(KERN_ERR "ioremap error\n");
ret = -ENOMEM;
goto err_1;
}
else
{
printk("ioremap ok\n");
}
//驱动硬件
key_dev.irqno = get_irqno_from_node();
ret = request_irq(key_dev.irqno, input_key_irq_handler, IRQF_TRIGGER_FALLING,
"key3_eint10", NULL);
if(ret != 0)
{
printk("request_irq error\n");
goto err_2;
}
return 0;
err_2:
iounmap(key_dev.reg_base);
free_irq(key_dev.irqno, NULL);
err_1:
input_unregister_device(inputdev);
err_0:
input_free_device(inputdev);
return ret;
}
static void __exit simple_input_exit(void)
{
printk("-------%s-------------\n", __FUNCTION__);
iounmap(key_dev.reg_base);
free_irq(key_dev.irqno, NULL);
input_unregister_device(inputdev);
input_free_device(inputdev);
}
module_init(simple_input_init);
module_exit(simple_input_exit);
MODULE_LICENSE("GPL");
simple_input_test.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
int main(int argc, char* argv[])
{
int fd;
int ret;
struct input_event event;
fd = open("/dev/input/event1", O_RDWR);
if(fd < 0)
{
perror("open");
exit(1);
}
while(1)
{
ret = read(fd, &event, sizeof(struct input_event));
if(ret < 0)
{
perror("read");
exit(1);
}
if(event.type == EV_KEY)
{
if(event.code == KEY_POWER)
{
if(event.value)
{ //按下
printf("__APP_USER__ : power pressed\n");
}else{
printf("__APP_USER__ : power up\n");
}
}
}
}
close(fd);
return 0;
}
终端信息