Android系统 linux内核按键驱动开发
Posted 打码?我带你们打
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android系统 linux内核按键驱动开发相关的知识,希望对你有一定的参考价值。
android系统 linux内核按键驱动开发
前言
刚入门的小白,在csdn的帮助下完成了第一个按键驱动,特写此文记录学习并分享给有需要的人。
1.修改设备树.dts
我是用的开发板是rp-rk3288,Android8.1的源码。路径是rk3288-android8.1/kernel/arch/arm/boot/dts/rp-rk3288.dts。
在dts文件里面配置要使用的gpio。具体如何配置不同开发板是不一样的,可以参考同节点下其他gpio的配置,大多数都是大同小异的。
gpio_num = <&gpio5 15 GPIO_ACTIVE_LOW> : 代 表 设 置 gpio5_B7 为 低 电 平 ,.
将GPIO_ACTIVE_LOW 改成 GPIO_ACTIVE_HIGH 就是设置为高电平
gpio_function = <0>:0 代表设置为输出模式,1代表输入模式
2.创建驱动文件
在rk3288-android8.1/kernel/drivers/目录下创建目录hby,在hby下创建驱动文件hby.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include<linux/kdev_t.h>//获取一个设备的设备编号,应当使用<linux/kdev_t.h>中定义的宏
//定义输入设备指针
struct input_dev *inputdev ;
struct rk3288_key_struct {
int key_gpio;// GPIO编号
int key_code; // 按键能产生的键值
struct work_struct work; // 按键的工作队列
};
struct rk3288_key_struct keys_list[] ={//按键列表,有多个按键就可以在这里加上
{.key_code = KEY_BACK, .key_gpio = 167},
//{.key_code = KEY_VOLUMEUP, .key_gpio = 166},
//{.key_code = KEY_VOLUMEDOWN, .key_gpio = 223}
};
static irqreturn_t rk3288_key_intnerrupt(int irq, void *dev_id, struct pt_regs *regs){ //中断上半部分
int i= (int)dev_id;
int gpio = keys_list[i].key_gpio; //获取按键的 GPIO
int code = keys_list[i].key_code; // 获取按键的键值
// 延迟 20uS,看按键是不是按下,如果不是,就是抖动
udelay(20);
if (gpio_get_value(gpio)) {
return IRQ_HANDLED;
}
input_report_key(inputdev, code, 1);// 先报告键按下事件
input_sync(inputdev);
schedule_work(&(keys_list[i].work));
// 提交工作队列,实现中断的下半部处理
return IRQ_HANDLED;
}
static void rk3288_scankeypad(struct work_struct *_work){//中断处理下班部分
// 通过工作队列指针而获得它所属的 rk3288_key_struct类型的对象
struct rk3288_key_struct *key_tmp = container_of(_work, struct rk3288_key_struct, work);
int gpio = key_tmp -> key_gpio;
int code = key_tmp->key_code;
// 每隔 10mS 检查按键是否已经提起,如果没有提起就一直等待
while(!gpio_get_value(gpio)){
mdelay(10);
}
input_report_key(inputdev, code, 0);
// 报告按键提起事件
input_sync(inputdev);
}
static int __init hello_init(void){
int i = 0, ret = 0;
int irq_no = 0;
int code, gpio;
printk("!!!!!!!!!!HELLO 3288!!!!!!!!!!!!");
inputdev = input_allocate_device();;// 1. 分配一个input_dev结构体
if (!inputdev) {
return -ENOMEM;
}
inputdev->name = "hby_rk3288_key_ko";
set_bit(EV_KEY, inputdev->evbit); // 设置输入设备支持按键事件
for (i = 0; i < sizeof(keys_list)/sizeof(keys_list[0]); i++) {
code = keys_list[i].key_code;
gpio = keys_list[i].key_gpio;
INIT_WORK(&(keys_list[i].work), rk3288_scankeypad);// 为每个按键都初始化工作队列
set_bit(code, inputdev->keybit);//设置输入设备支持的键值
gpio_free(gpio);//为每个按键都初始化 GPIO
ret = gpio_request(gpio, "gpio5b7");
if (ret) {
printk("request gpio failed %d \\n", gpio);
return -EBUSY;
}
gpio_direction_input(gpio); //当 GPIO 被设置为输入工作状态后,就可以检测中断信号
int irq_no= gpio_to_irq(gpio);//获取中断号
int setirqret = irq_set_irq_type(irq_no, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING);// 把每个 GPIO 中断响应方式都设置为下降沿响应
//为每个按键的中断都安装中断处理函数,其私有数据为按键信息在 keys_list 数组下的索引
ret = request_irq(irq_no, rk3288_key_intnerrupt, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "hby_rk3288_key",(void *)i);///(void *)i &keys_list[i]
if (ret) {
printk("request irq faile%d!\\n", irq_no);
return -EBUSY;
}
}
//3. 注册
int registerdevice=input_register_device(inputdev);
printk("hby key driver up \\n");
return 0;
}
static void __exit hello_exit(void)
{
int i = 0;
int irq_no;
for (i = 0; i < sizeof(keys_list)/sizeof(keys_list[0]); i++) {
irq_no = gpio_to_irq(keys_list[i].key_gpio); // 为每个按键释放 GPIO
free_irq(irq_no, (void *)i); // 为每个按键卸载中断处理函数
}
printk("!!!!!!!!!!!!!BYE!!!!!!!!!!!! \\n");
input_unregister_device(inputdev);
// 注销输入设备驱动
}
subsys_initcall(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
驱动的代码就不解释了,注释写的很清楚了。
3.新建makefile和kconfig文件
这里需要在hby目录下新建makefile文件和kconfig文件
#makefile
// makefile文件只有一行代码
obj-$(CONFIG_HBY) +=hby.o
#kconfig
config HBY
tristate "register driver hby"
default n
help
This is key driver for hby
至于makefile 和kconfig的具体语法这里就不详细介绍(因为我也不会【狗头】)想了解的可以参考这里
4.修改上级的makefile和kconfig文件
linux内核源码每一层都有一个makefile文件和kconfig文件,一个makefile只负责处理本目录中的编译关系,整个linux内核的makefile组成一个树状结构,对于上层makefile的子目录而言,只需要让kbuild知道它应该怎样进行递归地进入目录即可。所以要把我们新建的makefile和kconfig添加到这颗树上。也就是告诉上层我们这里还有一个makefile和config
具体做法
#1.修改hby目录的上层makefile,也就是rk3288-android8.1/kernel/drivers/makefile。添加
obj-$(CONFIG_HBY) += hby/
#2.修改hby目录的上层kconfig,也就是rk3288-android8.1/kernel/drivers/kconfig。
在menu "Device Drivers"和endmenu之间添加这一句。
source "drivers/hby/Kconfig
5.配置编译菜单
添加完之后进入内核根目录(/kernel),执行make menuconfig命令,menuconfig: 由scripts工具和Kconfig构成的图形配置界面, 通过它生成.config文件,在这个配置菜单中,将我们编写的驱动添加进去,则编译内核的时候我们的驱动就会编译。
进入Device Drivers —> 找到我们刚刚编写的驱动,把他设置为M。
在menuconfig中选择n、m和 y的区别:
n:不编译
y: 模块驱动编译到内核中,启动时自动加载
m:模块会被编译,但是不会被编译到内核中,可以使用insmod命令动态加载驱动。
6.编译
有了上面的设置,编译内核的时候就会在hby目录下生成一个hby.ko文件
7.加载驱动
将这个.ko文件拷贝到设备里,使用insmod 命令加载驱动
dmesg -c命令查看printk方法打印的日志。
使用getevent 指令获取按键上报信息。其中event3就是我们新加入的驱动,设备名就是我们在驱动代码中设置的。当按下按键的时候,就能获取按键上报的信息。
在我们上述的这个例子里,我把该按键设置成back,也就是按下按键的时候上层就会收到一个back事件,我们的安卓设备就会 “返回”
以上是关于Android系统 linux内核按键驱动开发的主要内容,如果未能解决你的问题,请参考以下文章
Linux——Linux驱动之使用内核定时器进行按键消抖的开发实战(内核定时器的基本概念使用要点修改定时周期运行)