输入子系统2

3, 初始化input device

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");

4,驱动多个按键

一个按键有多个与其相关的元素:
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