一、input子系统基本框架
Linux内核为了两个目的:
- 简化纯输入类外设(如:键盘、鼠标、游戏杆、轨迹球、触摸屏。。。等等)的驱动开发
- 统一输入类外设产生的数据格式(struct input_event),更加方便应用层编程
设计了输入子系统
事件处理层:接收来自核心层上报的事件,并选择对应的handler(事件处理器 struct input_handler)去处理。内核维护着多个事件处理器对象,每个input_handler对象专门处理一类事件,所有产生同类事件的设备驱动共用同一个handler。
设备驱动层:主要实现获取硬件设备的数据信息(包括触摸屏被按下、按下位置、鼠标移动、键盘按下等等),并转换为核心层定义的规范事件后提交给核心层,该层每个设备对应一个struct input_dev对象,
核心层:负责连接设备驱动层和事件处理层,为设备驱动层提供输入设备驱动的接口(struct input_dev)以及输入设备驱动的注册函数(input_register_device),为事件处理层提供输入事件驱动的接口;通知事件处理层对事件进行处理。
二、驱动开发步骤
c
/*init或probe函数中:
1. 创建struct input_dev对象input_allocate_device
2. 设置事件类型以及相关参数set_bit
3. 注册struct input_dev对象input_register_device
*/
/*exit或remove函数中:
1. 注销struct input_dev对象input_unregister_device
2. 销毁struct input_dev对象input_free_device
*/
/*上报事件
两种事件上报方式:
1. 对有中断支持的输入设备:在其中断处理函数(上半部或下半部)中上报事件
2. 对无中断支持的输入设备:使用workqueue循环定时上报(struct delayed_work)
主要函数:
input_event
input_report_abs
input_sync
*/
相关接口:
c
/*_init*/
struct input_dev *input_allocate_device(void):
/*
创建一个输入设备对象。
返回一个指向 input_dev 结构的指针,该结构用于表示输入设备。
*/
void set_bit(struct input_dev *dev, unsigned long whichbits):
/*
设置输入设备的事件类型。dev 是输入设备对象的指针,whichbits 是一个位掩码,用于指定事件类型。
例如,使用 set_bit(dev, EV_KEY) 可以设置输入设备支持按键事件。
whichbits:
EV_KEY:按键事件。这个事件类型用于处理键盘、鼠标等输入设备的按键事件
EV_ABS:绝对坐标事件。这个事件类型用于处理绝对坐标的事件,例如触摸屏的触摸位置。
*/
void input_set_abs_params(struct input_dev *dev, unsigned int axis, int min, int max, int fuzz, int flat)
/*
配置输入设备的绝对坐标参数。
dev 是输入设备对象的指针,
axis 是要配置的坐标轴(如ABS_X、ABS_Y等),
min 和 max 分别是坐标轴的最小值和最大值,
fuzz 和 flat 是用于指定坐标轴的模糊度和平坦度参数。
*/
int input_register_device(struct input_dev *dev)
/*
注册输入设备到内核。将输入设备对象注册到内核,以便它可以开始上报事件。
*/
/*_exit*/
void input_unregister_device(struct input_dev *dev)//注销输入设备。从内核中注销输入设备,停止事件的上报。
void input_free_device(struct input_dev *dev)//释放输入设备。释放输入设备对象的内存。
/*上报事件*/
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
/*
上报一个通用输入事件。通常,这个函数用于上报不是绝对坐标或按键事件的事件。
dev 是输入设备对象的指针,
type 表示事件类型,
code 表示事件代码(例如,按键代码),
value 表示事件的值。
*/
void input_report_key(struct input_dev *dev, unsigned int code, int value)
/*
上报按键事件。
dev 是输入设备对象的指针,
code 表示按键事件的代码,
value 表示按键事件的值(0表示松开,1表示按下)。
*/
void input_report_abs(struct input_dev *dev, unsigned int code, int value)
/*
上报绝对坐标事件。
dev 是输入设备对象的指针,
code 表示绝对坐标事件的代码,
value 表示绝对坐标的值。
*/
void input_sync(struct input_dev *dev)//上报完成后需要调用这些函数来通知系统处理完整事件, 这个函数告诉内核事件已经完整,可以处理了。
/*应用层数据类型*/
struct input_event { //这是一个用于表示输入事件的结构体。它包含以下字段:
struct timeval time; //事件的时间戳。
__u16 type; //事件类型,如 EV_KEY(按键事件)、EV_ABS(绝对坐标事件)等。
__u16 code; //事件代码,具体表示事件的含义,例如按下哪个键或是哪个绝对坐标轴。
__s32 value; //事件的值,通常表示按键的状态(按下或松开)或绝对坐标的值。
}
三、key2-input版代码解析
key2.c
cpp
#include <linux/module.h> // Linux内核模块头文件
#include <linux/kernel.h> // 内核相关功能的头文件
#include <linux/fs.h> // 文件系统相关功能的头文件
#include <linux/gpio.h> // GPIO库的头文件
#include <linux/interrupt.h> // 中断处理相关功能的头文件
#include <linux/of_gpio.h> // Open Firmware GPIO相关功能的头文件
#include <linux/of_irq.h> // Open Firmware中断相关功能的头文件
#include <linux/cdev.h> // 字符设备相关功能的头文件
#include <linux/wait.h> // 等待队列相关功能的头文件
#include <linux/sched.h> // 调度相关功能的头文件
#include <linux/poll.h> // poll相关功能的头文件
#include <linux/mm.h> // 内存管理相关功能的头文件
#include <linux/input.h> // 输入子系统相关功能的头文件
#include <linux/delay.h> // 延时相关功能的头文件
#include <linux/slab.h> // 内存分配相关功能的头文件
#include <asm/uaccess.h> // 用户态内核态数据传输相关功能的头文件
struct fs4412key2_dev
{
struct input_dev *pdev; // 输入设备结构体指针,用于注册输入事件
int gpio; // GPIO引脚的编号
int irqno; // 中断编号
};
struct fs4412key2_dev *pgmydev = NULL; // 指向驱动程序数据结构的指针
// 中断处理函数,处理按键中断
irqreturn_t key2_irq_handle(int no, void *arg)
{
struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
int status1 = 0;
int status2 = 0;
// 读取GPIO引脚状态两次,用于防抖
status1 = gpio_get_value(pmydev->gpio);
mdelay(1);
status2 = gpio_get_value(pmydev->gpio);
// 如果两次状态不一致,认为是抖动,不处理
if (status1 != status2)
{
return IRQ_NONE;
}
// 根据按键状态生成输入事件
if (status1)
{
input_event(pmydev->pdev, EV_KEY, KEY_2, 0); // 按键释放事件
input_sync(pmydev->pdev); // 同步输入事件
}
else
{
input_event(pmydev->pdev, EV_KEY, KEY_2, 1); // 按键按下事件
input_sync(pmydev->pdev); // 同步输入事件
}
return IRQ_HANDLED;
}
// 模块初始化函数
int __init fs4412key2_init(void)
{
int ret = 0;
struct device_node *pnode = NULL;
// 查找设备树节点
pnode = of_find_node_by_path("/mykey2_node");
if (NULL == pnode)
{
printk("find node failed\n");
return -1;
}
// 分配驱动程序数据结构内存
pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev), GFP_KERNEL);
if (NULL == pgmydev)
{
printk("kmalloc for struct fs4412key2_dev failed\n");
return -1;
}
// 从设备树中获取GPIO引脚编号
pgmydev->gpio = of_get_named_gpio(pnode, "key2-gpio", 0);
// 从设备树中获取中断编号
pgmydev->irqno = irq_of_parse_and_map(pnode, 0);
// 分配并注册输入设备
pgmydev->pdev = input_allocate_device();
set_bit(EV_KEY, pgmydev->pdev->evbit);
set_bit(KEY_2, pgmydev->pdev->keybit);
ret = input_register_device(pgmydev->pdev);
// 请求中断处理函数
ret = request_irq(pgmydev->irqno, key2_irq_handle, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "fs4412key2", pgmydev);
if (ret)
{
printk("request_irq failed\n");
input_unregister_device(pgmydev->pdev);
input_free_device(pgmydev->pdev);
kfree(pgmydev);
pgmydev = NULL;
return -1;
}
return 0;
}
// 模块卸载函数
void __exit fs4412key2_exit(void)
{
// 释放中断
free_irq(pgmydev->irqno, pgmydev);
// 注销输入设备
input_unregister_device(pgmydev->pdev);
input_free_device(pgmydev->pdev);
// 释放驱动程序数据结构内存
kfree(pgmydev);
pgmydev = NULL;
}
MODULE_LICENSE("GPL"); // 指定模块许可证
module_init(fs4412key2_init); // 指定模块初始化函数
module_exit(fs4412key2_exit); // 指定模块卸载函数
testkey2.c
cpp
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
int fd = -1;
struct input_event evt;
if(argc < 2)
{
printf("Argument is too few\n");
return 1;
}
/*open*/
fd = open(argv[1],O_RDONLY);
if(fd < 0)
{
printf("open %s failed\n",argv[1]);
return 2;
}
/*init mpu6050*/
while(1)
{
read(fd,&evt,sizeof(evt));
if(evt.type == EV_KEY && evt.code == KEY_2)
{
if(evt.value)
{
printf("KEY2 DOWN\n");
}
else
{
printf("KEY2 UP\n");
}
}
}
/*close*/
close(fd);
fd = -1;
return 0;
}
四、mpu6050-input版代码解析
mpu6050drv.c
cpp
#include <linux/module.h> // Linux内核模块头文件
#include <linux/kernel.h> // 内核相关功能的头文件
#include <linux/fs.h> // 文件系统相关功能的头文件
#include <linux/i2c.h> // I2C总线相关功能的头文件
#include <linux/cdev.h> // 字符设备相关功能的头文件
#include <linux/wait.h> // 等待队列相关功能的头文件
#include <linux/sched.h> // 调度相关功能的头文件
#include <linux/poll.h> // poll相关功能的头文件
#include <linux/slab.h> // 内存分配相关功能的头文件
#include <linux/mm.h> // 内存管理相关功能的头文件
#include <linux/input.h> // 输入子系统相关功能的头文件
#include <linux/io.h> // I/O内存操作相关功能的头文件
#include <asm/uaccess.h> // 用户态内核态数据传输相关功能的头文件
#include <asm/atomic.h> // 原子操作相关功能的头文件
/****************MPU6050内部寄存器地址****************/
#define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
#define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x18(不自检,2G,5Hz)
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用)
#define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读)
#define SlaveAddress 0x68 //MPU6050-I2C地址
// 定义MPU6050设备结构体
struct mpu6050_dev
{
struct input_dev * pinput; // 输入设备结构体指针
struct i2c_client *pclient; // I2C客户端结构体指针
struct delayed_work work; // 延迟工作结构体
};
struct mpu6050_dev *pgmydev = NULL; // 指向MPU6050设备数据结构的指针
// 读取MPU6050寄存器的函数
int mpu6050_read_byte(struct i2c_client *pclt, unsigned char reg)
{
int ret = 0;
char txbuf[1] = {reg};
char rxbuf[1] = {0};
struct i2c_msg msg[2] =
{
{pclt->addr, 0, 1, txbuf},
{pclt->addr, I2C_M_RD, 1, rxbuf}
};
ret = i2c_transfer(pclt->adapter, msg, ARRAY_SIZE(msg));
if (ret < 0)
{
printk("ret = %d, in mpu6050_read_byte\n", ret);
return ret;
}
return rxbuf[0];
}
// 写入MPU6050寄存器的函数
int mpu6050_write_byte(struct i2c_client *pclt, unsigned char reg, unsigned char val)
{
int ret = 0;
char txbuf[2] = {reg, val};
struct i2c_msg msg[1] =
{
{pclt->addr, 0, 2, txbuf},
};
ret = i2c_transfer(pclt->adapter, msg, ARRAY_SIZE(msg));
if (ret < 0)
{
printk("ret = %d, in mpu6050_write_byte\n", ret);
return ret;
}
return 0;
}
// 延迟工作函数,用于读取MPU6050传感器数据
void mpu6050_work_func(struct work_struct *pwk)
{
struct mpu6050_dev *pmydev = container_of((struct delayed_work *)pwk, struct mpu6050_dev, work);
unsigned short ax = 0;
unsigned short ay = 0;
unsigned short az = 0;
unsigned short gx = 0;
unsigned short gy = 0;
unsigned short gz = 0;
unsigned short temp = 0;
// 读取加速度和陀螺仪数据
ax = mpu6050_read_byte(pmydev->pclient, ACCEL_XOUT_L);
ax |= (mpu6050_read_byte(pmydev->pclient, ACCEL_XOUT_H) << 8);
input_report_abs(pmydev->pinput, ABS_X, ax);
ay = mpu6050_read_byte(pmydev->pclient, ACCEL_YOUT_L);
ay |= (mpu6050_read_byte(pmydev->pclient, ACCEL_YOUT_H) << 8);
input_report_abs(pmydev->pinput, ABS_Y, ay);
az = mpu6050_read_byte(pmydev->pclient, ACCEL_ZOUT_L);
az |= (mpu6050_read_byte(pmydev->pclient, ACCEL_ZOUT_H) << 8);
input_report_abs(pmydev->pinput, ABS_Z, az);
gx = mpu6050_read_byte(pmydev->pclient, GYRO_XOUT_L);
gx |= (mpu6050_read_byte(pmydev->pclient, GYRO_XOUT_H) << 8);
input_report_abs(pmydev->pinput, ABS_RX, gx);
gy = mpu6050_read_byte(pmydev->pclient, GYRO_YOUT_L);
gy |= (mpu6050_read_byte(pmydev->pclient, GYRO_YOUT_H) << 8);
input_report_abs(pmydev->pinput, ABS_RY, gy);
gz = mpu6050_read_byte(pmydev->pclient, GYRO_ZOUT_L);
gz |= (mpu6050_read_byte(pmydev->pclient, GYRO_ZOUT_H) << 8);
input_report_abs(pmydev->pinput, ABS_RZ, gz);
temp = mpu6050_read_byte(pmydev->pclient, TEMP_OUT_L);
temp |= (mpu6050_read_byte(pmydev->pclient, TEMP_OUT_H) << 8);
input_report_abs(pmydev->pinput, ABS_MISC, temp);
input_sync(pmydev->pinput);
schedule_delayed_work(&pgmydev->work, msecs_to_jiffies(1000)); // 延迟1秒后再次读取数据
}
// 初始化MPU6050传感器
void init_mpu6050(struct i2c_client *pclt)
{
mpu6050_write_byte(pclt, PWR_MGMT_1, 0x00);
mpu6050_write_byte(pclt, SMPLRT_DIV, 0x07);
mpu6050_write_byte(pclt, CONFIG, 0x06);
mpu6050_write_byte(pclt, GYRO_CONFIG, 0xF8);
mpu6050_write_byte(pclt, ACCEL_CONFIG, 0x19);
}
// I2C设备驱动的探测函数
static int mpu6050_probe(struct i2c_client *pclt, const struct i2c_device_id *pid)
{
int ret = 0;
pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev), GFP_KERNEL);
if (NULL == pgmydev)
{
printk("kmalloc failed\n");
return -1;
}
memset(pgmydev, 0, sizeof(struct mpu6050_dev));
pgmydev->pclient = pclt;
init_mpu6050(pgmydev->pclient);
pgmydev->pinput = input_allocate_device();
set_bit(EV_ABS, pgmydev->pinput->evbit);
input_set_abs_params(pgmydev->pinput, ABS_X, -32768, 32767, 0, 0);
input_set_abs_params(pgmydev->pinput, ABS_Y, -32768, 32767, 0, 0);
input_set_abs_params(pgmydev->pinput, ABS_Z, -32768, 32767, 0, 0);
input_set_abs_params(pgmydev->pinput, ABS_RX, -32768, 32767, 0, 0);
input_set_abs_params(pgmydev->pinput, ABS_RY, -32768, 32767, 0, 0);
input_set_abs_params(pgmydev->pinput, ABS_RZ, -32768, 32767, 0, 0);
input_set_abs_params(pgmydev->pinput, ABS_MISC, -32768, 32767, 0, 0);
ret = input_register_device(pgmydev->pinput);
if (ret)
{
printk("input_register_device failed\n");
input_free_device(pgmydev->pinput);
pgmydev->pinput = NULL;
kfree(pgmydev);
pgmydev = NULL;
return -1;
}
INIT_DELAYED_WORK(&pgmydev->work, mpu6050_work_func);
schedule_delayed_work(&pgmydev->work, msecs_to_jiffies(1000)); // 初始化后立即开始读取数据
return 0;
}
// I2C设备驱动的卸载函数
static int mpu6050_remove(struct i2c_client *pclt)
{
cancel_delayed_work(&pgmydev->work);
input_unregister_device(pgmydev->pinput);
input_free_device(pgmydev->pinput);
pgmydev->pinput = NULL;
kfree(pgmydev);
pgmydev = NULL;
return 0;
}
// 匹配设备树中的MPU6050节点
struct of_device_id mpu6050_dt[] =
{
{.compatible = "invensense,mpu6050"},
{}
};
// 定义MPU6050设备驱动的ID
struct i2c_device_id mpu6050_ids[] =
{
{"mpu6050", 0},
{}
};
// 定义MPU6050设备驱动结构体
struct i2c_driver mpu6050_driver =
{
.driver = {
.name = "mpu6050",
.owner = THIS_MODULE,
.of_match_table = mpu6050_dt,
},
.probe = mpu6050_probe,
.remove = mpu6050_remove,
.id_table = mpu6050_ids,
};
// 注册MPU6050设备驱动
module_i2c_driver(mpu6050_driver);
MODULE_LICENSE("GPL"); // 指定模块许可证