Linux驱动——input子系统

一、input子系统基本框架

Linux内核为了两个目的:

  1. 简化纯输入类外设(如:键盘、鼠标、游戏杆、轨迹球、触摸屏。。。等等)的驱动开发

  2. 统一输入类外设产生的数据格式(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_event
    input_report_abs
    input_sync
*/
​

相关接口:

/*_init*/
struct input_dev *input_allocate_device(void)//创建对象
​
void set_bit(struct input_dev *dev,unsigned long whichbits)//设置事件类型
​
void input_set_abs_params(struct input_dev *dev,unsigned int axis,int min,int max,int fuzz,int flat)//设置上报值的取值范围
​
int input_register_device(struct input_dev *dev)//注册input设备到内核
​
/*_exit*/
void input_unregister_device(struct input_dev *dev)
void input_free_device(struct input_dev *dev)
​
/*上报事件*/
void input_event(struct input_dev *,unsigned int t,unsigned int c,int v)
​
void input_report_key(struct input_dev *,unsigned int c,int v) //上报按键事件
void input_report_abs(struct input_dev *,unsigned int c,int v)//上报绝对坐标事件
    
void input_sync(struct input_dev *)//上报完成后需要调用这些函数来通知系统处理完整事件
​
/*应用层数据类型*/
struct input_event {
    struct timeval time;       // 时间戳
    __u16 type;             // 事件类型
    __u16 code;             // 哪个分值
    __s32 value;            // 具体值      
};

三、fs4412key2-input版代码解析

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/platform_device.h>
#include<linux/device.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<asm/uaccess.h>
#include<linux/mm.h>
#include<linux/sched.h>
#include<linux/io.h>
#include<linux/slab.h>
#include<asm/atomic.h>
#include<linux/wait.h>
#include<linux/poll.h>
#include<linux/i2c.h>
#include<linux/input.h>
#include<linux/gpio.h>
#include<linux/interrupt.h>
#include<linux/of_gpio.h>
#include<linux/of_irq.h>
#include<linux/delay.h>
#include<linux/of.h>

//函数声明,不声明会报错不知道是什么原因
struct device_node *of_find_node_by_path(const char *path);

struct fs4412key2_dev{

	struct input_dev *pinput;
	 int gpio;
	 int irqno;

	
};//自定义驱动结构体

//定义全局指针
struct fs4412key2_dev *pdev = NULL;


//中断处理函数,也就是当开发板摁下key2后执行的函数
irqreturn_t myfs4412key2_handle(int no,void *arg){
	
	struct fs4412key2_dev *pdev = (struct fs4412key2_dev *)arg;
	int status1 = 0;
	int status2 = 0;

//读取引脚状态判断按键是否摁下,status2用来防抖
	status1 = gpio_get_value(pdev->gpio);
	mdelay(1);
	status2 = gpio_get_value(pdev->gpio);

	if(status1 != status2)
		return IRQ_NONE;
	if(status1){
//上报按键时间并通知系统来进行处理
		input_event(pdev->pinput,EV_KEY,KEY_2,0);
		input_sync(pdev->pinput);
	}else{
		input_event(pdev->pinput,EV_KEY,KEY_2,1);
		input_sync(pdev->pinput);
	
	}

	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;
		
	}

	pdev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);
	if(NULL == pdev){
		printk("kmalloc failed\n");
		return -1;
	}

//提取gpio口
	pdev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);
//获取中断号并进行映射
	pdev->irqno = irq_of_parse_and_map(pnode,0);

	pdev->pinput = input_allocate_device();

	set_bit(EV_KEY,pdev->pinput->evbit);
	set_bit(KEY_2,pdev->pinput->keybit);

	ret = input_register_device(pdev->pinput);
//申请中断
	ret = request_irq(pdev->irqno,myfs4412key2_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pdev);

	if(ret){
	
		printk("request_irq failed\n");
		input_unregister_device(pdev->pinput);
		input_free_device(pdev->pinput);
		kfree(pdev);
		pdev = NULL;
		return -1;
	
	}
	return 0;

}
void __exit fs4412key2_exit(void){

	free_irq(pdev->irqno,pdev); 
	input_unregister_device(pdev->pinput);
	input_free_device(pdev->pinput);
	pdev->pinput = NULL;
	kfree(pdev);
	pdev = NULL;
}
#if 0
struct i2c_device_id fs4412key2_idt[] = {

	{"fs4412key2",0},
	{}
};
struct i2c_driver fs4412key2_dev = {

	.driver = {
		.name = "fs4412key2",
		.owner = THIS_MODULE,
	},
	.probe = fs4412key2_init,
	.remove = fs4412key2_exit,
	.id_table = fs4412key2_idt,

};

#endif

//向内核注册一个i2c_driver对象
#if 0
int __init myfs4412key2_init(void){
	i2c_add_driver(&fs4412key2_dev);
	return 0;
}
//从内核注销一个i2c_driver对象
void __exit myfs4412key2_exit(void){
	i2c_del_driver(&fs4412key2_dev);
}
module_init(myfs4412key2_init);
module_exit(myfs4412key2_exit);
#else
//一个宏,同时完成注册和注销
//module_i2c_driver(fs4412key2_dev);
#endif
module_init(fs4412key2_init);
module_exit(fs4412key2_exit);

MODULE_LICENSE("GPL");

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值