3.输入子系统2

本文详细介绍了Linux内核输入子系统中input_device的初始化过程,包括位表设置、按键信息的添加以及设备树中按键子节点的定义。通过实例展示了如何在设备树中定义按键中断、坐标等信息,并阐述了如何在内核代码中初始化这些按键,实现驱动的加载。此外,还提供了完整的内核模块代码示例,展示了如何处理按键中断并上报按键事件。
摘要由CSDN通过智能技术生成

input device的初始化解释

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; //表示节点

}

不同输入设备能够产生不同的数据:
1,按键/keyboard: 产生键值,实际是一个数字
#define KEY_VOLUMEDOWN 114
#define KEY_VOLUMEUP 115
#define KEY_POWER 116 /* SC System Power Down /
2,ts/gsensor:产生坐标,绝对坐标, 有一个明确坐标系,并且原点(0,0),最大值(800,480)
#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 */
3,mouse:产生坐标,相对坐标,坐标值是相对于之前一个点坐标
#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 //声音数据

在这里插入图片描述

添加input device的信息

在init函数中添加设备信息

//添加设备信息--/sys/class/input/eventx/device/
	inputdev->name = "simple input key";
	inputdev->phys = "key/input/input0";
	inputdev->uniq = "simple key0 for 4412";
	inputdev->id.bustype = BUS_HOST;
	inputdev->id.vendor =0x1234 ;
	inputdev->id.product = 0x8888;
	inputdev->id.version = 0x0001;

设置位表和上报数据的另一种方式

另外一种设置bit的方法
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)
方法2:封装:
input_report_key(struct input_dev * dev, unsigned int code, int value)
|
input_event(dev, EV_KEY, code, !!value); //上报按键的时候一定0或者1

设备树中定义按键信息的子节点

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
	更新设备树文件:
	 cp -raf arch/arm/boot/dts/exynos4412-fs4412.dtb /tftpboot/

初始化所有的按键并驱动起来

在代码中也会设计这几个元素
在代码中获取节点:
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_allkey.c

#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{
	char *name;
	int irqno;
	int key_code;
	int gpionum;
	void *reg_base;
	struct device_node *cnp;// 可以随时去获取节点各个信息
};

struct key_desc all_key[KEY_NUMS];


struct input_dev *inputdev;

void get_all_child_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");
	}

	struct device_node *cnp;
	struct device_node *prev = NULL;

	int i = 0;

	do{
			//获取到子节点
			cnp = of_get_next_child(np, prev);
			if(cnp != NULL){
				all_key[i++].cnp = cnp;//将当前的节点记录下来
				printk("get child node ok!\n");
			}else{
				printk("get child node failed\n");
			}

			prev = cnp; //把当前的设置位prev

		}while(of_get_next_child(np, prev) != NULL);

}


irqreturn_t  input_key_irq_handler(int irqno, void *devid)
{
	printk("-------%s-------------\n", __FUNCTION__);

	//区分不同的按键
	struct key_desc *pdesc = (struct key_desc *)devid;

	int gpionum = of_get_named_gpio(pdesc->cnp, "gpio", 0);
	
	
	//直接通过gpio获取按键状态
	int value = gpio_get_value(gpionum);
	
	if(value){//抬起
		
		input_report_key(inputdev, pdesc->key_code, 0);
		input_sync(inputdev);//上报数据结束
			
	}else{
		input_report_key(inputdev, pdesc->key_code, 1);
		input_sync(inputdev);//上报数据结束
	}


	return IRQ_HANDLED;
}


static int __init simple_input_init(void)
{

	//编写输入子系统代码
	/*
		1,分配一个input device对象
		2, 初始化input  device对象
		3,注册input device对象
	*/
	int ret;
	
	inputdev  =  input_allocate_device();
	if(inputdev == NULL)
	{
		printk(KERN_ERR "input_allocate_device error\n");
		return -ENOMEM;
	}

	get_all_child_from_node();

	//添加设备信息--/sys/class/input/eventx/device/
	inputdev->name = "simple input key";
	inputdev->phys = "key/input/input0";
	inputdev->uniq = "simple key0 for 4412";
	inputdev->id.bustype = BUS_HOST;
	inputdev->id.vendor =0x1234 ;
	inputdev->id.product = 0x8888;
	inputdev->id.version = 0x0001;

	//当前设备能够产生按键数据--将某个bit置1
	__set_bit(EV_KEY,  inputdev->evbit);
	//表示当前设备能够产生power按键
	//__set_bit(KEY_POWER, inputdev->keybit);
	//另外一种设置bit的方式

	int i;
	int code;
	int irqno;
	char *key_name;
	for(i=0; i<KEY_NUMS; i++)
	{
		//设置keybit,支持哪些按键
		// 按键值从设备设备树
		
		struct device_node *cnp = all_key[i].cnp;
		
		of_property_read_u32(cnp,"key_code", &code);
		__set_bit(code, inputdev->keybit);
		all_key[i].key_code = code; //先记录下来


		
		irqno = irq_of_parse_and_map(cnp, 0);
		all_key[i].irqno = irqno;//先记录下来

		
		of_property_read_string(cnp, "key_name",  &key_name);
		all_key[i].name = key_name;

		printk("name = %s, code = %d,irqno = %d\n",
						key_name, code,irqno);	

		int irqflags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING;
		
		ret = request_irq(irqno, input_key_irq_handler, irqflags, 
					key_name, &all_key[i]);
		if(ret != 0)
		{
			printk("request_irq error\n");
			goto err_0;
		}
		
	}

	ret = input_register_device(inputdev);
	if(ret != 0)
	{
		printk(KERN_ERR "input_register_device error\n");
		goto err_0;
	}

	return 0;

err_1:
	input_unregister_device(inputdev);
err_0:
	input_free_device(inputdev);
	return ret;
}



static void __exit simple_input_exit(void)
{
	int i;
	for(i=0; i<KEY_NUMS; i++)
		free_irq(all_key[i].irqno, &all_key[i]);
		
	input_unregister_device(inputdev);
	input_free_device(inputdev);
}


module_init(simple_input_init);
module_exit(simple_input_exit);
MODULE_LICENSE("GPL");




Makefile



ROOTFS_DIR = /opt/4412/rootfs

MODULE_NAME = simple_input_allkey

ifeq ($(KERNELRELEASE), )

KERNEL_DIR = /home/linux/linux-3.14.10
CUR_DIR = $(shell pwd)

all :
	make -C  $(KERNEL_DIR) M=$(CUR_DIR) modules

clean :
	make -C  $(KERNEL_DIR) M=$(CUR_DIR) clean	

install:
	cp -raf *.ko $(ROOTFS_DIR)/drv_module


else

obj-m += $(MODULE_NAME).o


endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值