前言:
输入子系统,就是 入号注节硬操 中 申请设备号,注册设备,生成设备节点,fops,不用自己写了,系统帮忙写好了,可以方便的开发。要不要用就看需求了,对于输入设备例如key等等确实很方便。
/sys/class/input/enentx/device 中可以查看属于输入子系统的设备信息
输入子系统参数内涵:
Type就是哪种输入设备,例如鼠标、键盘、触摸屏等类型
code就是按键的内涵,例如是电源开关、EXT、左键等,人为赋予的内涵
value就是该按键的状态,0没按下,1是按下(规定死的)
下面写一个单一设备的输入子系统代码~后续会出多个按键的。
这个呢会自动生成设备节点,所以不需要注册字符设备(或者说是它帮我们写了),字符设备驱动框架那一套流程不必再走一遍,不过还是可以和平台总线驱动结合的,实现设备和驱动分离。下面我们也懒得做了,反正是从设备树得到设备硬件信息,直接写就不套个平台总线设备驱动框架了。
先看设备树,是这样的:
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>;
};
};
在这个代码中,我们只用到 home 这个节点:
myinput-keys {
compatible = "myinput-keys";
home {
code = <KEY_HOME>;
gpios = <&gpx1 1 GPIO_ACTIVE_LOW>;
interrupt-parent = <&gpx1>;
interrupts = <1 IRQ_TYPE_EDGE_BOTH>;
};
代码:
13th_input_sys.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>
//这是要注册输入子设备函数,其参数要的结构体
//这个结构体描述的当然就是type,code,value了
struct input_dev *idev;
//以下是存从设备树拿到的硬件信息要用的
struct device_node *mynode; //设备树节点
int mygpio; //gpio号
int irqno; //中断号
//这是中断处理函数
static irqreturn_t keyirq_handler(int irqno,void *devid){
int value;
printk("receive a interrupt 9!\n");
// 4.1非设备树方式
// value = *(key_dev->virreg_base+1)&(0x1<<1);
// 4.2设备树方式
value = gpio_get_value(mygpio);
if(value==1){ //没按下 == key_event.value = 0
input_event(idev,EV_KEY,KEY_ENTER,0);//tpye,code,value
input_sync(idev);
//以上这两个语句就是应用程序read时,驱动传上去的
//说白了就是把哪种输入设备、这种输入设备的哪种键值、键值的状态给到应用程序
printk("key up!\n");
}
else if(value==0){ //按键按下
input_event(idev,EV_KEY,KEY_ENTER,1);//tpye,code,value
input_sync(idev);
printk("key down!\n");
}
return IRQ_HANDLED;
}
static int __init input_sys_init(void)
{
int ret;
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);
// 1.2设置keybit即键值/内涵
__set_bit(KEY_ENTER,idev->keybit);
// 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;
}
//3.硬件相关 用设备树写法
// 设备树方式:
//得到节点先
mynode = of_find_compatible_node(NULL,NULL,"myinput-keys");
if(mynode==NULL){
printk(KERN_EMERG "of_find_compatible_node error\n");
ret = -ENOMEM;
goto err_1;
}
//找到子节点
mynode = of_find_node_by_name(mynode,"home");
if(mynode==NULL){
printk(KERN_EMERG "of_find_node_by_name error\n");
ret = -ENOMEM;
goto err_1;
}
//得到gpio
mygpio = of_get_named_gpio_flags(mynode,"gpios",0,NULL);
if (!gpio_is_valid(mygpio))
printk("gpio isn't valid\n");
else printk("gpio num=%d",mygpio);
ret= gpio_request(mygpio, "gpios");
if(ret!=0){
printk(KERN_EMERG "gpio_request error\n");
ret = -ENOMEM;
goto err_1;
}
gpio_direction_input(mygpio);//初始化为输入模式
//获取中断号--申请中断
irqno = of_irq_get(mynode,0);
ret = request_irq(irqno,keyirq_handler,IRQ_TYPE_EDGE_BOTH,"keyirq",NULL);
if(ret!=0){ //返回0说明正确
printk(KERN_EMERG "irqno error\n");
goto err_2;
}
printk(KERN_EMERG "irqno is %d\n",irqno);
return 0;
err_2:
gpio_free(mygpio);
err_1:
input_unregister_device(idev);
err_0:
input_free_device(idev);
return ret;
}
static void __exit input_sys_exit(void)
{
printk(KERN_EMERG "input_sys_exit\n");
free_irq(irqno,NULL);
gpio_free(mygpio);
input_unregister_device(idev);
input_free_device(idev);
}
//
module_init(input_sys_init);
module_exit(input_sys_exit);
app_input_sys.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>
//驱动回应应用程序的read,传来的是input_event这个结构体类型的数据包
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){
//input_event这个结构体里面就包含了type,code,value,举例,如果是键盘的话
//那这三个值主要是用来给用户判断具体是哪个按键有动作,并且该动作是按下还是抬起
res = read(fd, (void *)(&myevent),sizeof(struct input_event));
if(myevent.type==EV_KEY){
if(myevent.code==KEY_ENTER){
if(myevent.value==1){ //按下1,抬起0
printf("app key down\n");
}
else if(myevent.value==0){
printf("app key up\n");
}
}
}
}
close(fd);
return ;
}