前言:
这篇写的是一次性给5个按键写好驱动~用输入子系统实现。
代码:
设备树节点:
myinput-keys {
compatible = "myinput-keys";
home {
code = <KEY_HOME>;
gpios = <&gpx1 1 GPIO_ACTIVE_LOW>;
interrupt-parent = <&gpx1>;
interrupts = <1 IRQ_TYPE_EDGE_BOTH>;
};
back {
code = <KEY_BACK>;
interrupt-parent = <&gpx1>;
interrupts = <2 IRQ_TYPE_EDGE_BOTH>;
gpios = <&gpx1 2 GPIO_ACTIVE_LOW>;
};
sleep {
interrupt-parent = <&gpx2>;
interrupts = <6 IRQ_TYPE_EDGE_BOTH>;
code = <KEY_POWER>;
gpios = <&gpx2 6 GPIO_ACTIVE_LOW>;
};
vol-up {
interrupt-parent = <&gpx2>;
code = <KEY_UP>;
interrupts = <1 IRQ_TYPE_EDGE_BOTH>;
gpios = <&gpx2 1 GPIO_ACTIVE_HIGH>;
};
vol-down {
interrupt-parent = <&gpx2>;
interrupts = <0 IRQ_TYPE_EDGE_BOTH>;
code = <KEY_DOWN>;
gpios = <&gpx2 0 GPIO_ACTIVE_HIGH>;
};
};
14th_input_sys_mulkey.c
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("LCH");
#include <linux/input.h>
#include <asm-generic/bitops/non-atomic.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
#include <uapi/linux/input-event-codes.h>
#include <linux/string.h>
#define MYKEY_NUM 5
struct input_dev *idev;
struct device_node *f_node; //设备树节点
struct hw_describe{
struct device_node *mynode; //设备树节点
int mygpio; //gpio号
int irqno; //中断号
unsigned int key_code; //是什么键值/内涵
char irqname[20];
};
struct hw_describe allkey[MYKEY_NUM];
int i;
static irqreturn_t keyirq_handler(int irqno,void *devid){
int value;
struct hw_describe *imykey = (struct hw_describe *)devid;
printk("receive a interrupt %s!\n",imykey->irqname);
// 4.1非设备树方式
// value = *(key_dev->virreg_base+1)&(0x1<<1);
// 4.2设备树方式
value = gpio_get_value(imykey->mygpio);
if(value==1){ //没按下 == key_event.value = 0
input_event(idev,EV_KEY,imykey->key_code,0);//tpye,code,value
input_sync(idev);
printk("key up!\n");
}
else if(value==0){ //按键按下
input_event(idev,EV_KEY,imykey->key_code,1);//tpye,code,value
input_sync(idev);
printk("key down!\n");
}
return IRQ_HANDLED;
}
static int __init input_sys_init(void)
{
int ret,tmp;
char *irqname;
struct device_node *now_node; //设备树节点
struct device_node *prev_node; //设备树节点
printk(KERN_EMERG "input_sys_init\n");
//0.给input_dev分配空间
idev = input_allocate_device();
if(idev==NULL){
printk(KERN_EMERG "input_allocate_device error\n");
return -ENOMEM;
}
//1.初始化input_dev
// 1.1设备enbit即类型
__set_bit(EV_KEY,idev->evbit);
//得到父节点先
f_node = of_find_compatible_node(NULL,NULL,"myinput-keys");
if(f_node==NULL){
printk(KERN_EMERG "of_find_compatible_node error\n");
ret = -ENOMEM;
goto err_0;
}
prev_node = NULL;//表示从第一个开始找
i=0;
irqname=NULL;
do
{
//找到子节点
now_node = of_get_next_child(f_node,prev_node);
if(now_node==NULL){
printk(KERN_EMERG "%d of_get_next_child error\n",i);
ret = -ENOMEM;
goto err_0;
}
allkey[i].mynode = now_node;
prev_node = now_node;
//得到KEY键值/内涵:
ret = of_property_read_u32(now_node, "code", &allkey[i].key_code);
if (ret) {
printk(KERN_EMERG "%d of_property_read_u32 error\n",i);
goto err_0;
}else
printk(KERN_EMERG "%d the keycode is %d\n",i,allkey[i].key_code);
//设置注册中断的名字
sprintf(allkey[i].irqname, "KEYIRQ_%d",allkey[i].key_code);
printk(KERN_EMERG "%d the irqname is %s\n",i,allkey[i].irqname);
// 1.2设置keybit即键值/内涵
__set_bit(allkey[i].key_code,idev->keybit);
//得到gpio
allkey[i].mygpio = of_get_named_gpio_flags(now_node,"gpios",0,NULL);
if (!gpio_is_valid(allkey[i].mygpio))
printk("gpio%d isn't valid\n",i);
else printk("gpio num=%d",allkey[i].mygpio);
ret= gpio_request(allkey[i].mygpio, "gpios");
if(ret!=0){
printk(KERN_EMERG "gpio_request%d error\n",i);
ret = -ENOMEM;
goto err_1;
}
gpio_direction_input(allkey[i].mygpio);//初始化为输入模式
//获取中断号--申请中断
allkey[i].irqno = of_irq_get(now_node,0);
ret = request_irq(allkey[i].irqno,keyirq_handler,IRQ_TYPE_EDGE_BOTH,allkey[i].irqname,&allkey[i]);
if(ret!=0){ //返回0说明正确
printk(KERN_EMERG "irqno error\n");
goto err_2;
}
printk(KERN_EMERG "irqno is %d\n",allkey[i].irqno);
i++;
}while(of_get_next_child(f_node,prev_node)!=NULL);
// 1.3设备idev的信息 /sys/class/input/devices
idev->name = "lch-idev";
idev->phys = "/lch-idev-666";//设备路径
idev->uniq = "/lch-idev-999";//好像是命令
idev->id.bustype = BUS_HOST;//GPIO一般用这个,其他还有IIC等BUS
idev->id.vendor = 0x1111;//cat时显示的是0x后面的东西
idev->id.version=0x2222;//cat时显示的是0x后面的东西
//2.注册到输入子系统中,这样就注册了输入设备、生成了设备节点/dev/input/enventx
ret = input_register_device(idev);
if(ret!=0){//正常是返回0的
printk(KERN_EMERG "input_register_device error\n");
goto err_0;
}
return 0;
err_2:
for(tmp =0;tmp<i;tmp++){
gpio_free(allkey[tmp].mygpio);
}
err_1:
input_unregister_device(idev);
err_0:
input_free_device(idev);
return ret;
}
static void __exit input_sys_exit(void)
{
int tmp=0;
printk(KERN_EMERG "input_sys_exit\n");
for(tmp =0;tmp<i;tmp++){
free_irq(allkey[tmp].irqno,NULL);
gpio_free(allkey[tmp].mygpio);
}
input_unregister_device(idev);
input_free_device(idev);
}
//
module_init(input_sys_init);
module_exit(input_sys_exit);
app_input_sys_mulkey.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/input.h>
struct input_event myevent;
//打开设备节点 传入cmd ,
int main(int argc ,char **argv){
int fd,res;
//打开节点
fd = open("/dev/input/event1",O_RDWR);
if(fd>0)
printf("app open success\n");
else {
printf("app open error\n");
return -1;
}
while(1){
res = read(fd, (void *)(&myevent),sizeof(struct input_event));
if(res<0){
perror("read");
exit(-1);
}
if(myevent.type==EV_KEY){
switch (myevent.code){
case KEY_HOME:
if(myevent.value==1){ //按下1,抬起0
printf("app KEY_HOME down\n");
}
else if(myevent.value==0){
printf("app KEY_HOME up\n");
}
break;
case KEY_BACK:
if(myevent.value==1){ //按下1,抬起0
printf("app KEY_BACK down\n");
}
else if(myevent.value==0){
printf("app KEY_BACK up\n");
}
break;
case KEY_POWER:
if(myevent.value==1){ //按下1,抬起0
printf("app KEY_POWER down\n");
}
else if(myevent.value==0){
printf("app KEY_POWER up\n");
}
break;
case KEY_UP:
if(myevent.value==1){ //按下1,抬起0
printf("app KEY_UP down\n");
}
else if(myevent.value==0){
printf("app KEY_UP up\n");
}
break;
case KEY_DOWN:
if(myevent.value==1){ //按下1,抬起0
printf("app KEY_DOWN down\n");
}
else if(myevent.value==0){
printf("app KEY_DOWN up\n");
}
break;
}
}
}
close(fd);
return ;
}
运行效果:
查看注册的驱动:cat /proc/interrupts
insmod驱动:
运行app:
遇到问题:
遇到一个问题,也蛮古怪的,调试了好久,因为要注册中断嘛,想用不同的名,就使用sprintf去整,我一开始是定义了一个字符指针
char *irqname;
然后再
sprintf(irqname, "KEYIRQ_%d",allkey[i].key_code);
allkey[i].irqname = irqname ;
结果不知道怎么回事一直报错说有空指针。
相关数据结构定义如下:
struct hw_describe{
struct device_node *mynode; //设备树节点
int mygpio; //gpio号
int irqno; //中断号
unsigned int key_code; //是什么键值/内涵
char irqname[20];
};
struct hw_describe allkey[MYKEY_NUM];
后来改成了:
//设置注册中断的名字
sprintf(allkey[i].irqname, "KEYIRQ_%d",allkey[i].key_code);
就正常了。
不是很懂,看起来没有问题呀原来的写法,知道的朋友麻烦指教一下。