高级驱动——二、input子系统(按键中断)

一,input子系统的作用和框架

1,输入设备的分类-----按产生的数据

1》按键设备------键值,按键状态等
	keypad   button
	
2》绝对坐标设备 ------x坐标,y坐标等
	触摸屏,陀螺仪,gsonser等
	
3》相对坐标设备 ------ x坐标,y坐标等
	鼠标

2,input子系统作用

1》规范了输入设备驱动的统一的管理(编写)方式
2》给应用层提供统一的数据格式

3,input子系统的框架

应用层: 
	app.c 
-----------------------------------------------
驱动框架:------input子系统 
	
	input-handler层:-----drivers/input/evdev.c
		作用: 
			1,与应用层进行交互
			2,实现fops中的接口
	--------------------------------------------
	input-core层: -------- drivers/input/input.c
		作用: 
			1,维护整个input子系统
			2,给上下两层提供实现接口
			3,申请设备号 ----主设备:13
	--------------------------------------------
	input-dev层: ---------自己编写
		作用: 
			1,与硬件进行交互
			2,实现硬件的初始化

二,input子系统驱动编程 ------key为例

1,确保input子系统上两层的代码被编译进内核镜像 -----zImage中

make menuconfig
	Device Drivers  --->
		Input device support  --->			//drivers/input/input.c
			<*>   Event interface			//drivers/input/evdev.c

重新编译内核,并更新:
make -j2 zImage
cp arch/arm/boot/zImage /tftpboot/

2,实现input-dev层代码 -----------// 参考:Documentation/input/input-programming.txt

实现步骤:
1》分配input_dev 对象空间
2》初始化input_dev对象
3》注册input_dev对象
4》申请中断
5》实现中断处理函数

//input_dev层框架实现
	参考代码: input_simple_v1
	
//input_dev对象的类型
struct input_dev {
	//辅助信息
	const char *name;
	const char *phys;
	const char *uniq;
	struct input_id id;

	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];			//标记当前的输入设备是哪种类型(使能)
	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];		//表示能够产生哪个按键的信息,如: KEY_UP,KEY_DOWN
	unsigned long relbit[BITS_TO_LONGS(REL_CNT)];		//表示能够产生相对坐标设备中哪个坐标信息,如:x坐标,y坐标
	unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];		//表示能够产生绝对坐标设备中哪个坐标信息,如:x坐标,y坐标
	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;			//父类

	struct list_head	h_list;
	struct list_head	node;
};
	
//编译并在开发板中加载:
[root@farsight /drv_module]# insmod input_simple_key_drv.ko
--------------^_^ input_simple_key_drv_init---------------
input: Unspecified device as /devices/virtual/input/input0
[root@farsight /drv_module]# ls -l /dev/event0
crw-rw----    1 0        0          13,  64 Jan  1 00:00 /dev/event0		//加载input-dev层驱动模块,会自动创建设备节点
	
	
//上报数据 
	参考代码: input_simple_v2
	
//上报的数据类型
struct input_event {
	struct timeval time;  //时间戳
	__u16 type;		//输入设备类型类型
	__u16 code;		//数据类型
	__s32 value;	//具体的数值
};

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
//参数1 ----- input_dev 对象 
//参数2 ----- 设备类型 
//参数3 ----- 数据类型 
//参数4 ----- 具体的数值
	
//编译并在开发板中加载:
[root@farsight /drv_module]# insmod input_simple_key_drv.ko
--------------^_^ input_simple_key_drv_init---------------
input: Unspecified device as /devices/virtual/input/input2
[root@farsight /drv_module]# ls -l /dev/event0
crw-rw----    1 0        0          13,  64 Jan  1 00:38 /dev/event0
[root@farsight /drv_module]# ./input_app
--------------^_^ key_interrupt_handler---------------
按下--->下键
--------------^_^ key_interrupt_handler---------------
--------------^_^ key_interrupt_handler---------------
--------------^_^ key_interrupt_handler---------------

三,input子系统框架代码跟读

1,按框架的层跟读

input_handler层:drivers/input/evdev.c

module_init(evdev_init);
module_exit(evdev_exit);
				|
	static int __init evdev_init(void)
						|
		return input_register_handler(&evdev_handler);
											|
						static struct input_handler evdev_handler = {
							.event		= evdev_event,
							.connect	= evdev_connect,
							.disconnect	= evdev_disconnect,
							.fops		= &evdev_fops,				//fops对象
							.minor		= EVDEV_MINOR_BASE,      //#define EVDEV_MINOR_BASE	64  ------次设备号的最小值
							.name		= "evdev",
							.id_table	= evdev_ids,	
						};	

		int input_register_handler(struct input_handler *handler)
		{
			//1,初始化链表
				INIT_LIST_HEAD(&handler->h_list);
			
			//2,记录handler对象到数组:input_table
			input_table[handler->minor >> 5] = handler;
			
			
			//3,将handler对象插入到链表:input_handler_list
			list_add_tail(&handler->node, &input_handler_list);
			
			//4,遍历input_dev_list链表,与input_handler对象进行匹配
			list_for_each_entry(dev, &input_dev_list, node)
				input_attach_handler(dev, handler);
						|
				id = input_match_device(handler, dev);	//匹配input_dev与input_handler
				
				error = handler->connect(handler, dev, id);  //匹配成功,调用input_handler中的connect函数
					
		}
		
		static struct input_handler evdev_handler = {
			.event		= evdev_event,
			.connect	= evdev_connect,
							|
			static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)	
			{
				//1,在数组evdev_table中找到一个空的位置
				for (minor = 0; minor < EVDEV_MINORS; minor++)
					if (!evdev_table[minor])
						break;
					
				//2,分配了struct evdev对象的空间
				struct evdev *evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
				
				//3,初始化相关的数据
				INIT_LIST_HEAD(&evdev->client_list);
				spin_lock_init(&evdev->client_lock);
				mutex_init(&evdev->mutex);
				init_waitqueue_head(&evdev->wait);
				
				//4,设置设备节点名称:  ls /dev/event0
				dev_set_name(&evdev->dev, "event%d", minor);
				evdev->exist = true;
				evdev->minor = minor;
				
				//5,将input_dev对象和input_handler对象记录到handle中
				evdev->handle.dev = input_get_device(dev);
				evdev->handle.name = dev_name(&evdev->dev);
				evdev->handle.handler = handler;
				evdev->handle.private = evdev;
				
				//6,创建设备节点
				evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
				evdev->dev.class = &input_class;
				evdev->dev.parent = &dev->dev;
				evdev->dev.release = evdev_free;
				device_initialize(&evdev->dev);
				error = device_add(&evdev->dev);
				
				//7,注册handle对象
				error = input_register_handle(&evdev->handle);
				
				//8,将evdev对象的地址保存到数组:evdev_table中
				error = evdev_install_chrdev(evdev);
							|
						evdev_table[evdev->minor] = evdev;
			}
	//创建设备节点函数跟读device_create()
	struct device *device_create(struct class *class, struct device *parent,
		     dev_t devt, void *drvdata, const char *fmt, ...)
				|
		dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
		{
			//1,分配设备对象空间
			struct device *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
			
			dev->devt = devt;
			dev->class = class;
			dev->parent = parent;
			dev->release = device_create_release;
			dev_set_drvdata(dev, drvdata);
			
			retval = device_register(dev);
						|
				device_initialize(dev);
				return device_add(dev);

		}
------------------------------------------------------------------------------
input_core层:drivers/input/input.c
subsys_initcall(input_init);
module_exit(input_exit);
				|
	static int __init input_init(void)
					|
			//1,注册类
			err = class_register(&input_class);
			
			//2,初始化/proc中进程相关数据
			err = input_proc_init();
			
			//3,申请设备号
			err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
															|
										static const struct file_operations input_fops = {
											.owner = THIS_MODULE,
											.open = input_open_file,
											.llseek = noop_llseek,
										};

------------------------------------------------------------------------------
input_dev层:
module_init(input_simple_key_drv_init);
module_exit(input_simple_key_drv_exit);
					|
		//1》分配input_dev 对象空间 
		key_dev = input_allocate_device();
	
		//2》初始化input_dev对象
		key_dev->evbit[0] = BIT_MASK(EV_KEY);
		key_dev->keybit[BIT_WORD(KEY_DOWN)] = BIT_MASK(KEY_DOWN);
		
		//3》注册input_dev对象
		ret = input_register_device(key_dev);
					|
				//1,将input_dev对象插入到链表:input_dev_list
				list_add_tail(&dev->node, &input_dev_list);
				
				//2,遍历链表:input_handler_list,与input_dev对象匹配
					list_for_each_entry(handler, &input_handler_list, node)
						input_attach_handler(dev, handler);
									|
							id = input_match_device(handler, dev);	//匹配input_dev与input_handler
				
							error = handler->connect(handler, dev, id);  //匹配成功,调用input_handler中的connect函数

		//4》申请中断
		irqno = IRQ_EINT(1);
		if (request_irq(irqno, key_interrupt_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key_eint1", NULL)) 

//保存设备数据的对象:struct evdev_client 
struct evdev_client {
	unsigned int head;
	unsigned int tail;
	unsigned int packet_head; /* [future] position of the first element of next packet */
	spinlock_t buffer_lock; /* protects access to buffer, head and tail */
	struct fasync_struct *fasync;
	struct evdev *evdev;
	struct list_head node;
	unsigned int bufsize;
	struct input_event buffer[];   //数据包的数组--柔性数组,或者可伸缩的数组,只能用于结构体中,必须作为结构体的最后一个成员。
};

2,按应用到驱动调用过程跟读 ----open()函数

应用层: 
	if((fd = open("/dev/event0",O_RDWR)) < 0){
		
------------------------------------------------------
内核: 
	VFS:		sys_open(), sys_read(),.....
	
驱动: 
		err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
														|
								static const struct file_operations input_fops = {
												.open = input_open_file,
															|
											const struct file_operations *old_fops, *new_fops = NULL;
											//1,从数组:input_table中获取input_handler对象
											struct input_handler *handler = input_table[iminor(inode) >> 5];
											
											//2,从input_handler对象获取fops对象
											new_fops = fops_get(handler->fops);
											
											//3,将fops对象地址保存到file->f_op
											old_fops = file->f_op;
											file->f_op = new_fops;
	
											//4,调用input_handler中fops中的open函数
											err = new_fops->open(inode, file);

示例代码

1、pdrv


#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>



static struct input_dev *key_dev;
int irqno;
//5》实现中断处理函数
static irqreturn_t key_interrupt_handler(int irq, void *dummy)
{
	int value;
	printk("--------------^_^ %s---------------\n",__FUNCTION__);

	// 1,获取按键的信息
	value = gpio_get_value(S5PV210_GPH0(1));
	
	// 2,上报按键的数据
	if(value){
		//松开
    	input_report_key(key_dev, KEY_DOWN, 0);
    	input_sync(key_dev);  //每次上报完,需要调用该函数表示上报数据结束
    }else{
		//按下
		input_event(key_dev,EV_KEY,KEY_DOWN,1);
		input_sync(key_dev);
	}
    return IRQ_HANDLED;
}

static int __init input_simple_key_drv_init(void)
{
	int ret;
	printk("--------------^_^ %s---------------\n",__FUNCTION__);
	//1》分配input_dev 对象空间 
	key_dev = input_allocate_device();
	if (!key_dev) {
		printk(KERN_ERR "Not enough memory\n");
		return -ENOMEM;
	}
	
	//2》初始化input_dev对象
	key_dev->evbit[0] = BIT_MASK(EV_KEY);
	key_dev->keybit[BIT_WORD(KEY_DOWN)] = BIT_MASK(KEY_DOWN);
	
	//3》注册input_dev对象
	ret = input_register_device(key_dev);
	if (ret) {
		printk(KERN_ERR "Failed to register device\n");
		goto err_free_dev;
	}
	
	//4》申请中断
	irqno = IRQ_EINT(1);
	if (request_irq(irqno, key_interrupt_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key_eint1", NULL)) {
		printk(KERN_ERR "Can't allocate irq %d\n", irqno);
		ret =  -EBUSY;
		goto err_unregister_dev;
	}

	return 0;
err_unregister_dev:
	input_unregister_device(key_dev);
 err_free_dev:
	input_free_device(key_dev);
	return ret;
}

static void __exit input_simple_key_drv_exit(void)
{
	printk("----------^_^ %s----------------\n",__FUNCTION__);
	free_irq(irqno, NULL);
    input_unregister_device(key_dev);
    input_free_device(key_dev);
}

module_init(input_simple_key_drv_init);
module_exit(input_simple_key_drv_exit);
MODULE_LICENSE("GPL");



2、app

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>


int main(void)
{
	int fd;
	int ret;
	struct input_event event;

	if((fd = open("/dev/event0",O_RDWR)) < 0){
		perror("open");
		exit(1);
	}

	while(1){
		if((ret = read(fd,&event,sizeof(event))) < 0){
			perror("read");
			exit(1);
		}
		if(event.type == EV_KEY){
			if(event.code == KEY_DOWN){
				if(event.value)
					printf("按下--->下键\n");
				else
					printf("松开--->下键\n");
			}
		}
	}

	close(fd);

	return 0;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在开发输入子系统设备驱动时,通常遵循以下步骤: 1. 包含必要的头文件:在驱动程序的源文件中,你需要包含一些必要的头文件,例如`linux/input.h`、`linux/module.h`、`linux/init.h`等。 2. 定义驱动模块:使用`module_init`宏定义一个初始化函数,用来加载驱动程序。例如: ```c static int __init myinput_init(void) { // 驱动初始化逻辑 return 0; } module_init(myinput_init); ``` 3. 注册输入设备:在初始化函数中,你需要创建一个输入设备并进行注册。你可以使用`input_allocate_device`函数分配一个输入设备结构体,并设置一些属性,例如设备名称、支持的事件类型等。然后,使用`input_register_device`函数注册输入设备。例如: ```c static int __init myinput_init(void) { struct input_dev *myinput_dev; myinput_dev = input_allocate_device(); if (!myinput_dev) { pr_err("Failed to allocate input device\n"); return -ENOMEM; } // 设置设备名称、支持的事件类型等 input_register_device(myinput_dev); return 0; } ``` 4. 处理输入事件:注册完输入设备后,你需要实现一个中断处理函数或者定时器处理函数,用来处理输入事件。当触发输入事件时,驱动程序会调用该函数进行处理。你可以使用`input_report_*`系列函数上报输入事件,例如鼠标移动、按键按下等。例如: ```c static irqreturn_t myinput_interrupt(int irq, void *dev_id) { // 处理输入事件的逻辑 input_report_key(myinput_dev, KEY_A, 1); // 模拟按下 A 键 input_sync(myinput_dev); // 同步输入事件 return IRQ_HANDLED; } ``` 这只是一个简单的示例,实际的输入子系统设备驱动可能还需要处理更多的细节和特定的硬件接口。更详细的编写方法和实现细节可以参考Linux内核源码中的驱动示例和相关文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值