输入子系统学习

主要内容:
1,输入子系统的作用和框架
2,输入子系统的编程方式--学会最简单的输入子系统的开发方式
3,输入子系统和平台总线的结合编程方式---驱动所有按键
4,输入子系统的工作原理和代码分析---学习内核的设计思想

1,输入子系统的作用和框架

什么是输入设备:
	1,按键/keyboard
	2, 鼠标 mouse
	3, 触摸屏 touchscreen :gt811, ft56xx
	4, 游戏杆 joystick 

	有多个输入设备需要驱动的时候,假如不考虑输入子系统
		a, gt811
				设备号,创建文件,硬件初始化,实现fop,阻塞
				硬件初始化
		b, ft56xx
				设备号,创建文件,硬件初始化,实现fop,阻塞
				硬件初始化	
		
		多个输入设备有共同点:
				获取到数据(操作硬件),上报给用户(xxx_read, copy_to_user, 阻塞)
						差异化                           通用
	
		多个输入设备,有部分差异,也有部分通用
		内核就会考虑,将通用代码编写好,将差异化的代码留给驱动工程师

		设计成输入子系统:使得应用编程人员和驱动编程人员编程的时候变得简单统一
			1, 兼容所有的输入设备
			2, 统一的编程驱动方法(实现差异化硬件操作)
			3,	统一的应用操作接口:/dev/input/event0,event1
							open("/dev/input/event0"),
							read(fd, struct input_event): struct input_event buff可以认为是一个统一的数据包

框架:驱动分成三层

			应用层
	---------------------------------
	input handler层:数据处理者
		完成fop:实现xxx_read(), xxx_open
		将数据交给用户:数据从input device层
		不知道具体数据是什么,只知道把数据给用户
	----------------------------------------------------------
	input 核心层:管理层
	----------------------------------------------------------
	input device设备层:
		抽象出一个对象,描述输入设备信息
		初始化输入设备硬件,获取到数据
		知道具体的数据是什么,但是不知道数据如何给用户
	---------------------------------
	硬件层:mouse
			ts, keybaord,joystick

编程: 主要在input device层

2, 输入子系统的编程方式--学会简单的输入子系统的开发方式

	前提: input 核心层代码和 input handler 层需要在内核中必须有:
			drivers/input/evdev.c		//  event input handler
			driver/input/input.c		//	核心层

			linux@ubuntu:~/Level10/day5/linux-5.4.79/drivers/input$ ls
			apm-power.c   input.c          input-poller.c  Makefile         serio
			evbug.c       input-compat.c   input-poller.h  matrix-keymap.c  sparse-keymap.c
			evdev.c       input-compat.h   joydev.c        misc             tablet
			ff-core.c     input-leds.c     joystick        mouse            touchscreen
			ff-memless.c  input-mt.c       Kconfig         mousedev.c
			gameport      input-polldev.c  keyboard        rmi4
	如果没有,可以使用 make menuconfig 在内核中进行配置
			Device Drivers  --->
				Input device support  ---> 
						-*- Generic input layer (needed for keyboard, mouse, ...)  // input.c
						<*>   Event interface   //input handler层--evdev.c

	编写步骤:
			1. 分配一个input device对象
			2. 初始化input device对象
			3. 注册input device对象

	上报数据:
			void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
			参数1:当前input device上报数据
			参数2:上报的是哪种数据类型 EV_KEY,EV_ABS
			参数3:具体数据是什么 KEY_POWER
			参数4:值是什么

	用户空间读到的数据: 统一的数据包
	struct input_event{
			struct timeval time;时间戳
			__u16 type; //数据类型
			__u16 code;//具体数据是什么
			__s32 value;//值是是什么
	}
/*
设备树文件:
   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);	

	pInputdev->name = "button_dev";//随便取个名字 不然会报错

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

[root@farsight ]# ls /dev/input/
event0
[root@farsight ]# insmod simple_input_dev.ko
[   51.361454] simple_input_dev: loading out-of-tree module taints kernel.
[   51.367989] input: button_dev as /devices/virtual/input/input1
[root@farsight ]# ls /dev/input/
event0  event1
[root@farsight ]#
[   24.691143] ----------GPX1DAT = 0-----------
[   24.695394] __KERN__:Enter down
[   24.850114] --------------input_key_irq_handler------------------
[   24.854734] ----------GPX1DAT = 4-----------
[   24.858985] __KERN__:Enter up

相匹配的APP程序代码

#include <stdio.h>
#include <stdlib.h>
#include <linux/input.h>
#include <fcntl.h>

struct input_event *pEvent;

int main(int argc, char *argv[])
{
	int fd,ret;
	fd = open("/dev/input/event1",O_RDWR); 	// 地址以及设备文件名需要按实际情况修改

	if(fd < 0)
	{
		perror("open");
		exit(1);
	}

	while(1){
		ret = read(fd, (void*)pEvent, sizeof(struct input_event));

		if(pEvent->type == EV_KEY)	 //如果产生的是“按键”类型数据
			if(pEvent->code == KEY_POWER) 	//如果按键值是POWER
				if(pEvent->value)	//如果传输的数据大于0
				{
					printf("____APP____:Key up\n");
				}
				else
				{
					printf("____APP____:Key down\n");
				}
	}

	close(fd);

	return 0;
}

效果展示:

[ 5238.782829] --------------input_key_irq_handler------------------
[ 5238.787451] ----------GPX1DAT = 0-----------
[ 5238.791703] __KERN__:Enter down
[ 5239.004857] --------------input_key_irq_handler------------------
[ 5239.009476] ----------GPX1DAT = 4-----------
[ 5239.013728] __KERN__:Enter up
[ 5242.390972] --------------input_key_irq_handler------------------
[ 5242.395588] ----------GPX1DAT = 0-----------
[ 5242.399839] __KERN__:Enter down
[ 5242.609469] --------------input_key_irq_handler------------------
[ 5242.614089] ----------GPX1DAT = 4-----------
[ 5242.618341] __KERN__:Enter up