输入子系统(一)

前面我们引入了分层的概念:我们要驱动一个设备我们只要编写差异化代码:5、硬件信息等等。。。
然后其它代码:内核会去帮你做:
那我们的内核是怎么帮你做的呢?它怎么知道怎么申请设备号?怎么知道怎么读写信息?等等那1234678这些的操作?
其实它在内核会有几个文件帮你做现在事情:
在kernel内核中会有drives这个文件,这个文件下就是帮我们内核去编写一个可复用性的代码(其实不是它帮我们编写,是它本来就有这些代码,只不过可以匹配)。
在drives下面你可以看到的是很多子系统,像i2c、input、等这些子系统,就是我们有很多设备,但是很多设备的功能都一样,比如说输入设备。输入设备的功能就是把我们底层硬件产生的数据交到给上层用户空间。事实上我们整个驱动要做的最终目的都是这个。
那我们输入子系统下面又会有一些文件:这些文件就是帮你实现复用性的代码的:
在这里插入图片描述
比如我们一个按键会被分到event里面去,所以evdev.c里面做的事情就是帮你做调用层的事情
还有input.c就是帮我们做核心层的事情
当然,里面还有很多文件mouse(鼠标)joystick(操纵杆)…怎么些也是属于输入子系统的。
所以说:我们只要在我们的硬件编写过程中告诉内核我这是一个输入子系统啊。。。。你帮我看一下我的其它代码(复用性代码)在哪里,你让它出来跟我匹配一个啊。然后就完成了驱动。
至于这个过程等下慢慢讲
那我们再个输入子系统做一个定义:
硬件可以实现输入功能的,最终目的是把硬件数据传递到上层空间的都属于输入子系统,可以调用匹配它内核提供好的可复用性代码。就属于输入子系统。
————————————————————————————————————————————————
不知道前面我说过,你这分层思想你每一层再做什么做了什么我也不知道啊,我这怎么理解你的分层。
看一下我的盲猜有没有猜中
在这里插入图片描述
这是在内核中驱动代码的三个层次:(我们要时刻记住我们的最终目的就是要把数据给到应用层)
1、设备层:
抽象出一个对象,描述输入设备信息
初始化输入设备硬件,获取到数据
知道具体的数据是什么,但是不知道数据如何给用户
所以这一层就是5的这件事。ioramp 申请中断。。。。。
2、内核层:
内核层是管理层,它会在这一层申请设备号,以及创建类
还会创建链表,用于匹配设备层和调用层。(比如我的硬件是鼠标,那么,他就会调用Mousdev去帮我实现复用性代码)
3、调用层:
这一层和应用层靠的最近,那么也最好更用户交互数据。
这一层会实现:创建文件(设备节点 /dev/xxx)还会实现各种文件操作接口。
————————————————————————————————————————————————
好的,我们现在知道了输入子系统很高级,很牛逼。。那具体怎么用我们来看一下。
步骤:
1):像以前一样搭建好框架
2)在模块装载的地方:
编写步骤:

1,分配一个input device对象实例化一个input device对象
		struct input_dev *inputdev;
2、给结构体对象分配空间给结构体对象分配空间,并初始化部分成员
				inputdev = input_allocate_device();
3、 初始化input  device对象
		_set_bit(EV_KEY,inputdev->evbit);  
		//这个函数就是把ev_key写到里面的evbit 表示我这个设备是产生按键数据的设备
	_set_bit(KEY_POWER, inputdev->keybit); //表示当前按键产生power按键
4,注册input device对象
	ret = input_register_device(inputdev);
//这一段代码下来,内核就知道,原来你是一个输入子系统的设备,我现在去给你找你的通用性代码啊
	

3)完成硬件信息
什么ioremap,中断申请中断处理。。。

4)完成这些我们就可以产生数据了,
那么最重要的是,怎么把数据发送给我用户呢?
我们知道调用层帮我们实现了文件操作接口,那这我们怎么给出数据啊?
我们用了一个函数,把数据给到接口。

上报数据:
	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:值是是什么
用户空间就可以读到的数据:统一的数据包
struct input_event {
	struct timeval time; //时间戳
	__u16 type; //数据类型
	__u16 code;//具体数据是什么
	__s32 value;//值是是什么

——-————————————————这样我们就完成了数据的传输
代码:

#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>


#define GPXCON_REG 0X11000C20

void *reg_base;
int irqno;
struct input_dev *inputdev;

//irqreturn_t (*irq_handler_t)(int, void *);

irqreturn_t inputdev_handler(int irqno,void *devid){

	printk("----%s-----\n",__FUNCTION__);
	//读取数据寄存器
	int value;
	
	value = readl(reg_base + 4) & (1<<2);
	
	//可以直接通过input_event直接把当前设备数据传给用户空间
	//可以理解成中断产生的时候当前设备给用户空间发了一个包,包里有时间戳、数据类型、键值...
	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);//上报数据结束
	}
	printk("handle end \n");

	return IRQ_HANDLED;

}




int get_irqno_form_node(void){
		// 获取到设备树中到节点
	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");
	}

	// 通过节点去获取到中断号码
	int irqno = irq_of_parse_and_map(np, 0);
	printk("irqno = %d\n", irqno);
	
	return irqno;

}


static int __init first_key_drv_init(void){
	
	int ret;
/*
	编写步骤:
		1,分配一个input device对象
		2, 初始化input  device对象
		3,注册input device对象
*/
	//1、给结构体对象分配空间,并初始化部分成员
	inputdev = input_allocate_device();
	if(inputdev == NULL){
		printk(KERN_ERR "input_allocate_device error\n");
		return -ENOMEM;
	}
	//2、初始化输入设备对象
		_set_bit(EV_KEY,inputdev->evbit);  
			//这个函数就是把ev_key写到里面的evbit 表示我这个设备是产生按键数据的设备
		_set_bit(KEY_POWER, inputdev->keybit); //表示当前按键产生power按键

	//3、把硬件设备注册到核心层中
		ret = input_register_device(inputdev);
	if(ret < 0){
		printk(KERN_ERR "input_register_device\n");
		goto err0;
	}
	//添加中断功能
	irqno = get_irqno_form_node();
	
	printk("get_irqno_form_node\n");	
	
	reg_base = ioremap(GPXCON_REG, 8);
	
	ret = request_irq(irqno,inputdev_handler,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"key_eint10", NULL);

	if(ret != 0){
		printk(KERN_ERR "input_register_device\n");
		goto err1;
	}

	return 0;
	
err1:
	input_unregister_device(inputdev);
err0:
	input_free_device(inputdev);
	return -EFAULT;

}


static void  __exit first_key_drv_exit(void){
	iounmap(reg_base);
	free_irq(irqno,NULL);
	input_unregister_device(inputdev);
	input_free_device(inputdev);
}


module_init(first_key_drv_init);
module_exit(first_key_drv_exit);
MODULE_LICENSE("GPL");

温馨提示:用数据之前特别是指针,请看清它是在你被调用之前还是之后那拿到的数据。
所以:ioremap一定要在request_irq之前。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值