嵌入式内核及驱动开发之学习笔记(十四) 输入子系统框架使用
简单回顾一下之前学习的驱动知识。基于之前的学习,首先实现了字符驱动框架的编写,实现模块化的驱动,简单的加载/卸载去动态执行驱动程序。对于应用层<——>驱动层,编写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");
文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/feit2417/article/details/84437276
- 点赞
- 收藏
- 关注作者
评论(0)