温州互联网前十名公司,北京seo推广服务,广西建设科技协会网站,中标公告在哪里查询一、input子系统基本框架
Linux内核为了两个目的#xff1a;
简化纯输入类外设#xff08;如#xff1a;键盘、鼠标、游戏杆、轨迹球、触摸屏。。。等等#xff09;的驱动开发统一输入类外设产生的数据格式#xff08;struct input_event#xff09;#xff0c;更加方…一、input子系统基本框架
Linux内核为了两个目的
简化纯输入类外设如键盘、鼠标、游戏杆、轨迹球、触摸屏。。。等等的驱动开发统一输入类外设产生的数据格式struct input_event更加方便应用层编程
设计了输入子系统 事件处理层接收来自核心层上报的事件并选择对应的handler事件处理器 struct input_handler去处理。内核维护着多个事件处理器对象每个input_handler对象专门处理一类事件所有产生同类事件的设备驱动共用同一个handler。
设备驱动层主要实现获取硬件设备的数据信息包括触摸屏被按下、按下位置、鼠标移动、键盘按下等等并转换为核心层定义的规范事件后提交给核心层该层每个设备对应一个struct input_dev对象
核心层负责连接设备驱动层和事件处理层为设备驱动层提供输入设备驱动的接口struct input_dev以及输入设备驱动的注册函数input_register_device,为事件处理层提供输入事件驱动的接口通知事件处理层对事件进行处理。
二、驱动开发步骤
/*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_eventinput_report_absinput_sync
*/
相关接口
/*_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
#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
#include sys/types.h
#include sys/stat.h
#include linux/input.h
#include fcntl.h
#include unistd.h#include stdio.hint 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
#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(不自检2G5Hz)
#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); // 指定模块许可证