简单回顾一下之前学习的驱动知识。基于之前的学习,首先实现了字符驱动框架的编写,实现模块化的驱动,简单的加载/卸载去动态执行驱动程序。对于应用层<——>驱动层,编写file_operations 使驱动对上层应用层提供接口,实现功能机制让用户可以通过阻塞、非阻塞、多路复用或者异步通信的方式从驱动读设备;对于驱动层<——>硬件层,通过地址映射机制读写硬件的寄存器,实现对硬件的控制。利用中断实现驱动程序对中断事件的异步处理,引入“中断下半部”的概念来解决中断处理耗时大的问题;最后,通过设备驱动模型,实现了设备、驱动分离的思想,而平台总线模型就是这样的一个机制。

 

言归正传

 

设备驱动模型,将对硬件的描述从原来的驱动代码中抽离出去,构造出一个设备对象。可以让驱动代码去兼容更多的硬件,这样提高了代码的重用率。

目的是用少量的代码,去兼容大量的硬件。

 

模型开发

虽然抛去设备对象,但回顾字符驱动开发流程,驱动代码中仍然有很多相似的代码。精明的程序员前辈们总结出了一种模型框架,这种模型屏蔽了硬件的差异化(差异部分的代码我们自己来实现),对上提供统一的接口,以兼容更多不同的硬件。输入子系统这种模型之一。

 

输入子系统

输入设备

像按键、鼠标、触摸屏、游戏杆这类都是输入设备。如果为多个输入设备设计驱动,为每一个设备定制一个,就意味着你要写很多份驱动代码;如果设计成输入子系统,我们只要实现有部分差异性的代码即可

驱动分层

按照框架模型,再将驱动代码上中下分成三层:


<上>input handler层:数据处理者
<中>input 核心层:管理层         
<下>input device设备层:       

input handler层和input核心层有系统来实现,而编程主要在input device层。

(有待补充!!!!!!!!!!!!!!!!!!!!!!)

 

 

 

主要流程

分配一个input device对象

struct input_dev *input_allocate_device(void)

 

初始化input  device对象

进入input_dev对象,可以查看到该对象的很多属性。表示的是一个具体的输入设备,描述设备能够产生什么数据。对输入设备对象的初始化

添加设备信息

在用户层被识别到/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

 

注册input device对象

int input_register_device(struct input_dev *dev)

 

上报数据

将设备产生的数据上报给输入子系统,由系统把数据交给用户。

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
static inline void input_sync(struct input_dev *dev)

 

用户层读取

用户空间读到的数据:统一的数据包

用户调取read函数,以这种数据形式读取数据。

struct input_event {
    struct timeval time; //时间戳
    __u16 type; //数据类型
    __u16 code;//具体数据是什么
    __s32 value;//值是是什么
};

 

 

驱动代码实现

当模块加载时,分配一个输入设备对象,然后添加一个按键类型并指定键值为'KEY_POWER'。同时申请一个中断资源,当有按键key2按下时,会产生一个中断信号。在驱动-中断处理中,进行上报数据的动作。

//simple_input_drv.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>


#define GPXCON_REG 0x11000C20

irqreturn_t input_key_irq_handler(int irqno, void *devid);	//中断入口
int get_irqno_from_node(void);


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

static int __init input_drv_init(void)
{
	printk("-------%s-------------\n", __FUNCTION__);

	int ret;
	//分配 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);
	__set_bit(KEY_POWER, inputdev->keybit);

	//注册 input device 对象
	ret = input_register_device(inputdev);
	if(0 != ret)
	{
		printk(KERN_ERR "input_allocate_device error\n");
		goto err_0;
	}

	//从设备树种拿中断号 -> 申请中断资源
	irqno = get_irqno_from_node();
	ret = request_irq(irqno, input_key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
					"key3_eint10", NULL);
	if(ret != 0)
	{
		printk("request_irq error\n");
		goto err_1;
	}

	//寄存器地址映射
	reg_base  = ioremap(GPXCON_REG, 8);
	
	return 0;


	err_1:
		input_unregister_device(inputdev);

	err_0:
		input_free_device(inputdev);
		return ret;
}

static void __exit input_drv_exit(void)
{
	printk("-------%s-------------\n", __FUNCTION__);

	iounmap(reg_base);		//取消地址映射
	free_irq(irqno, NULL);	//释放中断资源
	input_unregister_device(inputdev);	//将input device 对象注销
	input_free_device(inputdev);		//释放input device 对象

}

irqreturn_t  input_key_irq_handler(int irqno, void *devid)
{
	int value = 0;
	printk("-------%s-------------\n", __FUNCTION__);
	//读取数据寄存器
	value = readl(reg_base + 4) & (1 << 2);
	if(value) {//抬起
		//input_event(inputdev, EV_KEY, KEY_POWER, 0);
		input_report_key(inputdev, KEY_POWER, 1);
		input_sync(inputdev);//上报数据结束
	} else {
		input_event(inputdev, EV_KEY, KEY_POWER, 1);
		input_sync(inputdev);//上报数据结束
	}

	return IRQ_HANDLED;
}



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


module_init(input_drv_init);
module_exit(input_drv_exit);

MODULE_LICENSE("GPL");