struct input_dev {//表示的是一个具体的输入设备,描述设备能够产生什么数据 const char *name; // sysfs中给用户看的信息 const char *phys; const char *uniq; struct input_id id; //evbit实际是一个位表,描述输入设备能够产生什么类型数据 unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; // EV_KEY,EV_ABS, EV_REL //表示能够产生哪种按键 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//KEY_POWER.. 能够表示768bit,直接用24个long来表示 // KEY_CNT == 768 BITS_TO_LONGS== nr/32 = 768/32==24 //表示能够产生哪种相对坐标数据 unsigned long relbit[BITS_TO_LONGS(REL_CNT)];// REL_X //表示能够产生哪种绝对坐标数据 unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //ABS_X unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; struct device dev; // 继承device对象 struct list_head h_list; struct list_head node; //表示节点 }
不同输入设备能够产生不同的数据:
#define KEY_VOLUMEDOWN 114 #define KEY_VOLUMEUP 115 #define KEY_POWER 116 /* SC System Power Down */
#define ABS_X 0x00 #define ABS_Y 0x01 #define ABS_PRESSURE 0x18 #define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ #define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ #define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ #define ABS_MT_POSITION_X 0x35 /* Center X touch position */ #define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */
#define REL_X 0x00 #define REL_Y 0x01 #define REL_WHEEL 0x08
如何表示不同数据类型:
#define EV_SYN 0x00 //表示同步数据类型 #define EV_KEY 0x01 //表示按键数据类型 #define EV_REL 0x02 //表示相对坐标数据类型 #define EV_ABS 0x03 //表示绝对坐标数据类型 #define EV_MSC 0x04 //表示杂项 #define EV_SW 0x05 //开关 #define EV_LED 0x11 //led指示数据 #define EV_SND 0x12 //声音数据
参考代码
//当前设备能够产生“按键”类型数据 --setbit函数就是将某位置1 __set_bit(EV_KEY,pInputdev->evbit); //当前设备能够产生power按键值 __set_bit(KEY_POWER,pInputdev->keybit);
另外一种设置bit的方法
//原方法: __set_bit(KEY_POWER,pInputdev->keybit); //另一种方法: inputdev->keybit[BIT_WORD(KEY_POWER)] |= BIT_MASK(KEY_POWER);// 116%32 inputdev->keybit[116/32] |= 1 << 116%32;// 116%32 //本质都一样,只不过是写法不同,建议使用第一种,但第二种要看得懂
上报数据的时候:
方法1:通用方法
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value) //value上报按键的时候一定0或者1
方法2:封装方法
input_report_key(struct input_dev * dev, unsigned int code, int value) //追溯下去,底层函数依然是调用input_event input_event(dev, EV_KEY, code, !!value); //value上报按键的时候一定0或者1
示例:
/* 设备树文件: key_int_node{ compatible = "test_key"; interrupt-parent = <&gpx1>; interrupts = <2 0>; }; */ #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 GPX1CON_ADDR 0x11000c20 #define GPX1_SIZE 8 struct input_dev *pInputdev; int irqno; void* reg_virt_base; 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"); return 0; } //通过节点去获取到中断号码 irqno = irq_of_parse_and_map(np,0); printk("irqno = %d\n",irqno); return irqno; } irqreturn_t input_key_irq_handler(int irqno, void *devid) { unsigned int GPX1DAT; printk("--------------%s------------------\n",__FUNCTION__); GPX1DAT = readl(reg_virt_base + 4); printk("----------GPX1DAT = %d-----------\n",GPX1DAT); if( GPX1DAT &= 0x1<<2 ){//抬起 printk("__KERN__:Enter up\n"); input_event(pInputdev,EV_KEY,KEY_POWER,0); //上报数据 input_sync(pInputdev); //上报数据结束 } else{//按下 printk("__KERN__:Enter down\n"); input_event(pInputdev,EV_KEY,KEY_POWER,1); //上报数据 input_sync(pInputdev); //上报数据结束 } return IRQ_HANDLED; } static int __init simple_input_dev_init(void) { /* 1,分配一个input device对象 2,初始化input device对象 3,注册input device对象 */ int ret ; pInputdev = input_allocate_device(); if(pInputdev == NULL) { printk(KERN_ERR "input_allocate_device error"); return -ENOMEM; } //当前设备能够产生“按键”类型数据 __set_bit(EV_KEY,pInputdev->evbit); //当前设备能够产生power按键值 __set_bit(KEY_POWER,pInputdev->keybit); //添加设备信息 -- /sys/class/input/eventX/device pInputdev->name = "simple input key";//随便取个名字 不然会报错 pInputdev->phys = "key/input/input0"; pInputdev->uniq = "simple key0 for 4412"; pInputdev->id.bustype = BUS_HOST; //控制模式 :GPIO pInputdev->id.vendor = 0x1234; //厂商代码 pInputdev->id.product = 0x6666; //产品代码 pInputdev->id.version = 0x0001; //版本号 ret = input_register_device(pInputdev); if(ret != 0) { printk(KERN_ERR "input_register_device error"); goto err0; } irqno = get_irqno_from_node(); ret = request_irq(irqno, input_key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "IRQ_EINT10", NULL); if(ret != 0) { printk("request_irq ERROR!\n"); goto err1; } reg_virt_base= ioremap(GPX1CON_ADDR, GPX1_SIZE); return 0; err1: input_unregister_device(pInputdev); err0: input_free_device(pInputdev); return ret; } static void __exit simple_input_dev_exit(void) { free_irq(irqno,NULL); input_unregister_device(pInputdev); input_free_device(pInputdev); } module_init(simple_input_dev_init); module_exit(simple_input_dev_exit); MODULE_LICENSE("GPL");
效果展示
替换代码展示:
/* 设备树文件: key_int_node{ compatible = "test_key"; interrupt-parent = <&gpx1>; interrupts = <2 0>; }; */ #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 GPX1CON_ADDR 0x11000c20 #define GPX1_SIZE 8 struct input_dev *pInputdev; int irqno; void* reg_virt_base; 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"); return 0; } //通过节点去获取到中断号码 irqno = irq_of_parse_and_map(np,0); printk("irqno = %d\n",irqno); return irqno; } irqreturn_t input_key_irq_handler(int irqno, void *devid) { unsigned int GPX1DAT; printk("--------------%s------------------\n",__FUNCTION__); GPX1DAT = readl(reg_virt_base + 4); printk("----------GPX1DAT = %d-----------\n",GPX1DAT); if( GPX1DAT &= 0x1<<2 ){//抬起 printk("__KERN__:Enter up\n"); //input_event(pInputdev,EV_KEY,KEY_POWER,0); //上报数据 input_report_key(pInputdev,KEY_POWER,0); //上报数据 input_sync(pInputdev); //上报数据结束 } else{//按下 printk("__KERN__:Enter down\n"); //input_event(pInputdev,EV_KEY,KEY_POWER,1); //上报数据 input_report_key(pInputdev, KEY_POWER, 1); //上报数据 input_sync(pInputdev); //上报数据结束 } return IRQ_HANDLED; } static int __init simple_input_dev_init(void) { /* 1,分配一个input device对象 2,初始化input device对象 3,注册input device对象 */ int ret ; pInputdev = input_allocate_device(); if(pInputdev == NULL) { printk(KERN_ERR "input_allocate_device error"); return -ENOMEM; } //当前设备能够产生“按键”类型数据 __set_bit(EV_KEY,pInputdev->evbit); //pInputdev->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); printk("__KERN__:evbit = %lu\n",*(pInputdev->evbit)); //当前设备能够产生power按键值 //__set_bit(KEY_POWER,pInputdev->keybit); pInputdev->keybit[BIT_WORD(KEY_POWER)] |= BIT_MASK(KEY_POWER); //添加设备信息 -- /sys/class/input/eventX/device pInputdev->name = "simple input key";//随便取个名字 不然会报错 pInputdev->phys = "key/input/input0"; pInputdev->uniq = "simple key0 for 4412"; pInputdev->id.bustype = BUS_HOST; //控制模式 :GPIO pInputdev->id.vendor = 0x1234; //厂商代码 pInputdev->id.product = 0x6666; //产品代码 pInputdev->id.version = 0x0001; //版本号 ret = input_register_device(pInputdev); if(ret != 0) { printk(KERN_ERR "input_register_device error"); goto err0; } irqno = get_irqno_from_node(); ret = request_irq(irqno, input_key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "IRQ_EINT10", NULL); if(ret != 0) { printk("request_irq ERROR!\n"); goto err1; } reg_virt_base= ioremap(GPX1CON_ADDR, GPX1_SIZE); return 0; err1: input_unregister_device(pInputdev); err0: input_free_device(pInputdev); return ret; } static void __exit simple_input_dev_exit(void) { free_irq(irqno,NULL); input_unregister_device(pInputdev); input_free_device(pInputdev); } module_init(simple_input_dev_init); module_exit(simple_input_dev_exit); MODULE_LICENSE("GPL");
一个按键有多个与其相关的元素:
a, 中断号码
b, 按键的状态--gpio的数据寄存器获取到
c, 按键的值--code
在设备树文件中设置这几个元素:
key_int_node{ compatible = "test_key"; #address-cells = <1>; #size-cells = <1>; key_int@0 { key_name = "key2_power_eint"; key_code = <116>; gpio = <&gpx1 1 0>; reg = <0x11000C20 0x18>; interrupt-parent = <&gpx1>; interrupts = <1 0>; }; key_int@1 { key_name = "key3_vup_eint"; key_code = <115>; gpio = <&gpx1 2 0>; reg = <0x11000C20 0x18>; interrupt-parent = <&gpx1>; interrupts = <2 0>; }; key_int@2 { key_name = "key4_vdown_eint"; key_code = <114>; gpio = <&gpx3 2 0>; reg = <0x11000C60 0x18>; interrupt-parent = <&gpx3>; interrupts = <2 0>; }; }; 更新编译设备树文件: make dtbs
在代码中也会设计这几个元素
在代码中获取节点:
of_get_next_child(const struct device_node * node, struct device_node * prev) 参数1:表示节点 参数2:之前的节点,如果是第一个节点,设置成NULL // 通过节点去获取到中断号码 irqno = irq_of_parse_and_map(cnp, 0); //获取key name of_property_read_string(cnp, "key_name", &key_name); //获取key code of_property_read_u32(cnp, "key_code", &code); gpionum = of_get_named_gpio(cnp, "gpio", 0); printk("name = %s, code = %d, gpionum = %d,irqno = %d\n", key_name, code, gpionum,irqno); //设计一个对象出来 struct key_desc{ char *name; int irqno; int key_code; int gpionum; void *reg_base; struct device_node *cnp;// 可以随时去获取节点各个信息 };
参考代码:
驱动代码 simple_input.c
/* 设备树文件: key_int_node{ compatible = "test_key"; #address-cells = <1>; #size-cells = <1>; key_int@0 { key_name = "key2_power_eint"; key_code = <116>; gpio = <&gpx1 1 0>; reg = <0x11000C20 0x18>; interrupt-parent = <&gpx1>; interrupts = <1 0>; }; key_int@1 { key_name = "key3_vup_eint"; key_code = <115>; gpio = <&gpx1 2 0>; reg = <0x11000C20 0x18>; interrupt-parent = <&gpx1>; interrupts = <2 0>; }; key_int@2 { key_name = "key4_vdown_eint"; key_code = <114>; gpio = <&gpx3 2 0>; reg = <0x11000C60 0x18>; interrupt-parent = <&gpx3>; interrupts = <2 0>; }; }; */ #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 <linux/of_gpio.h> #include <asm/io.h> #define KEY_NUMS 3 //设计一个对象出来 struct key_desc{ int irqno; void* reg_virt_base; const char* key_name; u32 code; int gpionum; struct device_node *cnp; //方便随时获取节点各个信息 }; struct key_desc all_key[KEY_NUMS]; //数量设备树文件的子节点数一致即可 struct input_dev *pInputdev; void get_all_child_from_node(void) { struct device_node *cnp; struct device_node *prev = NULL; int i; //获取到设备树中的节点 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"); return; } i = 0; do{ //获取子节点 cnp = of_get_next_child(np, prev); if(cnp != NULL) { all_key[i].cnp = cnp; i++; } prev = cnp; //把当前子节点赋值给prev }while(of_get_next_child(np,prev) != NULL); } irqreturn_t input_key_irq_handler(int irqno, void *devid) { struct key_desc* pdesc; int value; printk("--------------%s------------------\n",__FUNCTION__); //区分不同的按键 pdesc = (struct key_desc* )devid; //直接通过gpio获取按键状态 value = gpio_get_value(pdesc->gpionum); if(value){//抬起 printk("__KERN__: %d Enter up\n",pdesc->code); //input_event(pInputdev,EV_KEY,KEY_POWER,0); //上报数据 input_report_key(pInputdev,pdesc->code,0); //上报数据 input_sync(pInputdev); //上报数据结束 } else{//按下 printk("__KERN__: %d Enter down\n",pdesc->code); //input_event(pInputdev,EV_KEY,KEY_POWER,1); //上报数据 input_report_key(pInputdev, pdesc->code, 1); //上报数据 input_sync(pInputdev); //上报数据结束 } return IRQ_HANDLED; } static int __init simple_input_dev_init(void) { /* 1,分配一个input device对象 2,初始化input device对象 3,注册input device对象 */ int ret ,i; pInputdev = input_allocate_device(); if(pInputdev == NULL) { printk(KERN_ERR "input_allocate_device error"); return -ENOMEM; } get_all_child_from_node(); //添加设备信息 -- /sys/class/input/eventX/device pInputdev->name = "simple input key";//随便取个名字 不然会报错 pInputdev->phys = "key/input/input0"; pInputdev->uniq = "simple key0 for 4412"; pInputdev->id.bustype = BUS_HOST; //控制模式 :GPIO pInputdev->id.vendor = 0x1234; //厂商代码 pInputdev->id.product = 0x6666; //产品代码 pInputdev->id.version = 0x0001; //版本号 //当前设备能够产生“按键”类型数据 __set_bit(EV_KEY,pInputdev->evbit); //pInputdev->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); printk("__KERN__:evbit = %lu\n",*(pInputdev->evbit)); //当前设备能够产生power按键值 __set_bit(KEY_POWER,pInputdev->keybit); //pInputdev->keybit[BIT_WORD(KEY_POWER)] |= BIT_MASK(KEY_POWER); for(i=0; i<KEY_NUMS; i++) { //设置Keybit,支持哪些按键 //获取以整数形式存储于设备树中的<键值>--key_code of_property_read_u32(all_key[i].cnp, "key_code" , &all_key[i].code); __set_bit(all_key[i].code, pInputdev->keybit); //通过节点去获取到中断号码 all_key[i].irqno = irq_of_parse_and_map(all_key[i].cnp,0); //记录以字符串格式存储于设备树中的<名字>--key_name of_property_read_string(all_key[i].cnp, "key_name", &all_key[i].key_name); //记录节点gpio all_key[i].gpionum = of_get_named_gpio(all_key[i].cnp, "gpio", 0); //此处也可以写作 of_get_gpio(struct device_node * np, int index) //但是设备树中定义gpio时 必须写作 gpios = <&gpx1 1 0>; //而非gpio = <&gpx1 1 0>; (差个s) printk("__KERN__:name = %s, code = %d, irq = %d\n",pInputdev->name,all_key[i].code,all_key[i].irqno); ret = request_irq(all_key[i].irqno, input_key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, all_key[i].key_name, &all_key[i]); //&all_key[i] 作为 区分是哪个按键中断调用的中断函数 来使用 if(ret != 0) { printk("request_irq ERROR!\n"); goto err0; return 0; } } ret = input_register_device(pInputdev); //注册只需注册一次即可。 if(ret != 0) { printk(KERN_ERR "input_register_device error"); goto err1; return 0; } return 0; err1: for(i=0; i<KEY_NUMS; i++) { free_irq(all_key[i].irqno,&all_key[i]); } err0: input_free_device(pInputdev); return ret; } static void __exit simple_input_dev_exit(void) { int i; for(i=0; i<KEY_NUMS; i++) { free_irq(all_key[i].irqno,&all_key[i]); } input_unregister_device(pInputdev); input_free_device(pInputdev); } module_init(simple_input_dev_init); module_exit(simple_input_dev_exit); MODULE_LICENSE("GPL");
与之对应的APP代码 simple_input_app.c
#include <stdio.h> #include <stdlib.h> #include <linux/input.h> #include <fcntl.h> int main(int argc, char *argv[]) { int fd,ret; struct input_event Event; fd = open("/dev/input/event1",O_RDWR); // 地址以及设备文件名需要按实际情况修改 if(fd < 0) { perror("open"); exit(1); } while(1){ ret = read(fd, &Event, sizeof(struct input_event)); if(ret < 0) { perror("read"); exit(1); } printf("__APP__: type = %d , code = %d \n", Event.type , Event.code); if(Event.type == EV_KEY) //如果产生的是“按键”类型数据 { switch(Event.code) { case KEY_POWER: if(Event.value) //如果传输的数据大于0 { printf("____APP____:KEY_POWER Key up\n"); } else { printf("____APP____:KEY_POWER Key down\n"); } break; case KEY_VOLUMEUP: if(Event.value) //如果传输的数据大于0 { printf("____APP____:KEY_VOLUMEUP Key up\n"); } else { printf("____APP____:KEY_VOLUMEUP Key down\n"); } break; case KEY_VOLUMEDOWN: if(Event.value) //如果传输的数据大于0 { printf("____APP____:KEY_VOLUMEDOWN Key up\n"); } else { printf("____APP____:KEY_VOLUMEDOWN Key down\n"); } break; default:printf("____APP____:The Key is unregist\n"); } } } close(fd); return 0; }
Makefile代码
ROOTFS_DIR = /home/linux/Level11 APP_NAME = simple_input_app CROSS_COMPILE = arm-none-linux-gnueabi- CC = $(CROSS_COMPILE)gcc ifeq ($(KERNELRELEASE), ) KERNEL_DIR = /home/linux/kernel/linux-3.14-fs4412 CUR_DIR = $(shell pwd) all : make -C $(KERNEL_DIR) M=$(CUR_DIR) modules -j6 ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- $(CC) $(APP_NAME).c -o $(APP_NAME) clean : make -C $(KERNEL_DIR) M=$(CUR_DIR) clean rm $(APP_NAME) ./1/$(APP_NAME) clear install: cp -raf *.ko $(APP_NAME) ./1/ #cp -raf *.ko ./1/ else obj-m += simple_input_dev.o #obj-m += simple_input_app.o endif