输入子系统解析

linux输入子系统之按键驱动 上一节中,我们讲解了Linux  input子系统的框架,到内核源码里详细分析了输入子系统的分离分层的框架等。 上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/17733993 这一节,我们来以输入子系统的框架来写一个按键驱动。 问:怎么写符合输入子系统框架的驱动程序? 答: 1. 分配一个input_dev结构体 2 阅读详情
   这节来了解输入子系统的架构,以前写的驱动架构,相应的应用程序不能通用,不能加到已有的应用上去,这节来了解通用的写法。
   原文:《韦东山Linux视频驱动第2期》学习总结之第13课(输入子系统) 
   内核的输入子系统是对分散的,多种不同类别的输入设备(如键盘,鼠标,跟踪球,操纵杆,触摸屏,加速计和手写板)等字符设备进行统一处理的一层抽象,就是在字符设备驱动上抽象出的一层。
   输入子系统包括两类驱动程序:事件驱动程序和设备驱动程序。事件驱动程序负责和应用程序的接口,而设备驱动程序负责和底层输入设备的通信。鼠标事件生成文件mousedev属于事件驱动程序,而PS/2鼠标驱动程序是设备驱动程序。事件驱动程序是标准的,对所有的输入类都是可用的,所以要实现的是设备驱动程序而不是事件驱动程序。设备驱动程序可以利用一个已经存在的,合适的事件驱动程序通过输入核心和用户应用程序通信。
    input子系统分三层,最上一层是event handler,中间是intput core,底层是input driver。input driver把event report到input core层。input core对event进行分发,传到event handler,相应的event handler层把event放到event buffer中,等待用户进程来取。它们的关系图:

  

注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"
怎么建立连接?
1. 分配一个input_handle结构体
2. 
input_handle.dev = input_dev;  // 指向左边的input_dev
input_handle.handler = input_handler;  // 指向右边的input_handler
3. 注册:
   input_handler->h_list = &input_handle;
   inpu_dev->h_list      = &input_handle;

它们的框架图:

    input_dev,input_handler,input_handle 是 input子系统的3个基本的数据结构。一类handler可以和多个硬件设备相关联,一个硬件设备可以和多个handler相关联。例如:一个触摸屏设备可以作为一个event设备,作为一个鼠标设备,也可以作为一个触摸设备,所以一个设备需要与多个平台驱动进行连接。而一个平台驱动也不只为一个设备服务,一个触摸平台驱动可能要为A,B,C 3个触摸设备提供上层驱动,所以需要这样一对多的连接。

  总结来说,输入子系统分上下两层。最上层是核心层input.c,里面有register_chrdev。这个注册的字符设备的fops里面只有一个open函数。该open函数根据打开的设备节点的次设备号,找到一个input_handler。把打开的文件的里面的fop指向这个handler里面的fops,并且打开这个handler里面的open函数。以后要读写的时候就调用这个handler里面的读写函数,如下代码片段1。
    input_handler通过input_register_handler向核心层注册一个input_handler结构体,并且在input_table数组第i个值指向这个注册的input_handler(“i”是根据handler里面的次设备号向右移5位算出的值)。这个input_table[i]就可在上诉open函数里根据打开的设备节点的次设备号找到相应input_handler。
    input_dev通过input_register_device向核心层注册一个input_dev结构体。
    当注册一个input_dev设备或者注册一个input_handler时,input_dev与input_handler会通过调用input_attach_handler函数搜索是否有合适的input_handler或者input_dev与之匹配。如果存在inputh_handler与注册的input_dev匹配或者存在input_dev与注册的inputh_handler匹配,则会调用input_handler里的connect函数。匹配的依据就是input_handler中的id_table与input_dev中的id里的信息是否相同。
    connect函数会建立一个input_handle结构体,这个结构体里面的handler指向匹配的input_handler,dev指向input_dev。并且这个input_handle会被放到input_handler和input_dev的h_list链表里面来。以后就可以从input_dev里面的h_list找到handle,再从handle里面的handler找到input_handler。反之亦然。   

    对于输入设备,我们知道一般需要读取其数据,那么经过上述的一些列动作后又是如何读取到数据的呢?如果没有数据可读并且是NONBLOCK的话,就立刻返回。如果是没有数据可读但是BLOCK的话,就进入睡眠。既然有睡眠,那何时被唤醒呢?在evdev_read函数中唤醒read函数。evdev_event是input_handler中的成员,当有数据可读(如触摸屏被按下,按键被按下)时,event函数会被调用。

 而event函数是怎么被调用到的?这就得看设备层了,设备层的驱动做了如下工作:

    <1>在中断函数中确定判断发生了什么事件,并且相应的值是多少;
    <2>通过input_event()函数上报事件;
    <3>通过input_sync()函数表明上报结束。
    分析input_event函数我们就可以知道input_handler中的event函数是如何被调用到的了。
    在input_event中,调用了input_handle_event函数,在input_handle_event函数中调用了input_pass_event函数。 在input_pass_event函数中,将input_handle从input_dev的h_list中一个个拿出来。如果找到一个input_handle被打开,则input_handle->input_handler即为input_dev所匹配的handler。
 输入子系统之编写驱动程序
怎么写符合输入子系统框架的驱动程序?
1. 分配一个input_dev结构体
2. 设置
3. 注册
4. 硬件相关的代码,比如在中断服务程序里上报事件
下面开始编写代码:
1. 从入口函数开始执行
struct pin_desc{
	int irq;
	char *name;
	unsigned int pin;
	unsigned int key_val;
};
struct pin_desc pins_desc[4] = {
	{IRQ_EINT0,  "S2", S3C2410_GPF0,   KEY_L},
	{IRQ_EINT2,  "S3", S3C2410_GPF2,   KEY_S},
	{IRQ_EINT11, "S4", S3C2410_GPG3,   KEY_ENTER},
	{IRQ_EINT19, "S5",  S3C2410_GPG11, KEY_LEFTSHIFT},
};

static struct input_dev *buttons_dev;
static struct pin_desc *irq_pd;
static struct timer_list buttons_timer;

static int buttons_init(void)
{
	int i;	
	/* 1. 分配一个input_dev结构体 */
	buttons_dev = input_allocate_device();;

	/* 2. 设置 */
	/* 2.1 能产生哪类事件 */
	set_bit(EV_KEY, buttons_dev->evbit);
	set_bit(EV_REP, buttons_dev->evbit);  //能产生重复类事件
	
	/* 2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */
	set_bit(KEY_L, buttons_dev->keybit);
	set_bit(KEY_S, buttons_dev->keybit);
	set_bit(KEY_ENTER, buttons_dev->keybit);
	set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);

	/* 3. 注册 ,把我们的input_dev结构放入设备链表;最终会在某个数据里面找到 input_handler结构,找到它的 .fops,调用.fops的read()、write()方法。*/
	input_register_device(buttons_dev);
	
	/* 4. 硬件相关的操作,防抖动 */
	init_timer(&buttons_timer);
	buttons_timer.function = buttons_timer_function;
	add_timer(&buttons_timer);
	
	for (i = 0; i < 4; i++)
	{
		request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
	}
	
	return 0;
}
module_init(buttons_init);
struct input_dev的结构中的几个成员:
struct input_dev {
unsigned long evbit[NBITS(EV_MAX)];   // 表示能产生哪类事件
unsigned long keybit[NBITS(KEY_MAX)]; // 表示能产生哪些按键
unsigned long relbit[NBITS(REL_MAX)]; // 表示能产生哪些相对位移事件, x,y,滚轮
unsigned long absbit[NBITS(ABS_MAX)]; // 表示能产生哪些绝对位移事件, x,y
};
2. 编写 定时器事件和中断处理事件
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
	/* 10ms后启动定时器 */
	irq_pd = (struct pin_desc *)dev_id;
	mod_timer(&buttons_timer, jiffies+HZ/100);
	return IRQ_RETVAL(IRQ_HANDLED);
}

static void buttons_timer_function(unsigned long data)
{
	struct pin_desc * pindesc = irq_pd;
	unsigned int pinval;
	if (!pindesc)
		return;
	
	pinval = s3c2410_gpio_getpin(pindesc->pin);

	if (pinval)
	{
		/* 松开 : 最后一个参数: 0-松开, 1-按下 */
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
		input_sync(buttons_dev); //上报同步事件,表示事件已上报完成
	}
	else
	{
		/* 按下 */
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);
		input_sync(buttons_dev);
	}
}

以前要唤醒应用程序并发信号 ,input中只要上报事件就可以了。

3. 出口函数

static void buttons_exit(void)
{
	int i;
	for (i = 0; i < 4; i++)
	{
		free_irq(pins_desc[i].irq, &pins_desc[i]);
	}

	del_timer(&buttons_timer);
	input_unregister_device(buttons_dev);
	input_free_device(buttons_dev);	
}
module_exit(buttons_exit)
因为视频中没有说测试程序怎么写,所以在网上找了下,下面列出来,但不是这个驱动的 只能当学习了解一下:测试程序 - 关于Linux设备驱动中input子系统的介绍
测试程序:
#include<stdio.h>
#include<fcntl.h>
#include<errno.h>
#include<stdlib.h>
#include<linux/input.h>
int main(void )
{
  int fd;
  int key_value,i=0,count;
  struct input_event ev_key;
  fd=open("/dev/input/event0",0666);
  if(fd<0){
	  perror("open device");
	  exit(1);
  }
  while(1){
  count=read(fd,&ev_key,sizeof(struct input_event));
  for(i=0;i<(int)count/sizeof(struct input_event);i++)
  {
	  if(EV_KEY==ev_key.type)
	  {
		  int num=ev_key.code%10-1;
		  printf("type:%d,code:%d ,value:%d\n key%d pressed!\n",ev_key.type,ev_key.code,ev_key.value,num);
	  }
	  if(EV_SYN==ev_key.type)
	  printf("syn event\n");
	}
  }
  close(fd);
  return 0;
}

小知识:
 根据我们输入或删除显示字符的是 -sh 进程,执行:#ps  可以看查看到它的PID 号是770,可以通过命令:# ls  -l   /proc/770/fd   查看它打开了哪些文件。

Linux输入子系统实战:触摸屏事件捕获与解析 本文详细介绍了Linux输入子系统在触摸屏事件捕获与解析中的实战应用。通过识别设备文件、读取input_event结构体,解析单点与多点触控数据,并探讨坐标校准技巧和性能优化方法,帮助开发者高效处理触摸屏输入,提升嵌入式设备的交互体验。 阅读详情

相关推荐

Linux按键驱动实现全解析:从GPIO到输入子系统

本文全面解析Linux按键驱动的实现方式,涵盖GPIO中断驱动、GPIO轮询驱动和串口编码键盘驱动三种方案。通过设备树配置、输入子系统事件上报机制和实战案例,详细讲解从硬件连接到应用层处理的完整流程,帮助开发者高效实现稳定可靠的Linux按键功能。

a1b2c3d的博客 856

Linux——Linux驱动之使用输入子系统设计按键驱动实战(输入子系统基本概念、代码获取上报信息、相关函数解析

我们上一次的博文直接使用中断设计的按键驱动程序,实际上,我们使用的按键也是属于一种输入设备,像鼠标、键盘等都是,那么你应该意识到Linux系统对于某一类设备肯定会搞出一套标准的框架出来,那么本节博文我们学习下输入子系统的概念,并且使用输入子系统设计一个按键驱动。

数字世界 万物互联 4008

Linux输入子系统实战:从事件捕获到多指触控解析

本文深入解析Linux输入子系统,从事件捕获基础到多指触控协议实战。详细介绍了如何通过/dev/input/eventX设备节点读取输入数据,解析input_event结构体,并利用ioctl获取设备信息。内容涵盖单点与多点触控数据处理、同步事件机制及常见调试技巧,为开发者提供全面的Linux输入设备开发指南。

night的博客 561

Linux输入子系统简析

linux, 输入子系统,输入驱动

JiMoKuangXiangQu的专栏 1084

嵌入式Linux驱动教程(东山2期)

1.没有废话,句句都是干货!学习后保证可以跟着视频完成相应的实验。2.现场从0编写/调试工作中的绝大部分驱动,内容--- 理论 +

12.Linux之输入子系统分析(详解)

在此节之前,我们学的都是简单的字符驱动,涉及的内容有字符驱动的框架、自动创建设备节点、linux中断、poll机制、异步通知、同步互斥/非阻塞、定时器去抖动。 其中驱动框架如下: 1)写file_operations结构体的成员函数: .open()、.read()、.write() 2)在入口函数里通过register_chrdev()创建驱动名,生成主设备号,赋入file_operati...

weixin_33830216的博客 280

触摸屏驱动三部曲之输入子系统

这篇主要是讲解输入子系统的框架,希望能通过触摸屏来窥探整个输入子系统的框架。

若云流风的专栏 2764

Linux输入子系统框架原理解析

这篇文章主要介绍了Linux输入子系统框架原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 nput输入子系统框架 linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。 一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过 input driver -> Input core

weixin_45727997的博客 436

输入子系统模型解析和原理分析

一、输入子系统模型解析 1.为什么需要输入子系统 按写按键驱动时:需要注册1个字符启动;open,read与用户程序交互;硬件设置。 这些数据都是要输入的,所以可以全部整合成输入子系统。 2.输入子系统的模型 输入子系统由设备驱动层(input device driver),核心层(input core)和事件驱动层(event driver)三部份组成。 任何一次输入事件,如鼠标移动,按...

weixin_30642305的博客 274

Linux内核的输入子系统框架解析

我们自己写驱动的流程一般是: 自己确定或由系统自动分配主设备号; 建立fops结构; 使用register_chrdev在初始化函数中进行注册; 定义入口函数MODULE_INIT()和出口函数MODULE_EXIT()。 但这种我们自己写的驱动程序,只有自己可以调用。因为这种驱动不标准,只有别人知道驱动用法的情况下才能使用。当我们使用QT等标准程序时,这类标准程序不能打开像我们这样的野驱动,所以,我们应该让我们的驱动程序融入“标准”中去。这个标准就是linux提供的输入子系统框架。 1、Linux输入

Leon的博客 1135

输入子系统input)框架解析(基于Linux3.4.2)

我们自己写驱动的流程一般是: 自己确定或由系统自动分配主设备号; 建立fops结构; 使用register_chrdev在初始化函数中进行注册; 定义入口函数MODULE_INIT()和出口函数MODULE_EXIT()。 但这种我们自己写的驱动程序,只有自己可以调用。因为这种驱动不标准,只有别人知道驱动用法的情况下才能使用。当我们使用QT等标准程序时,这类标准程序不能打开像我们这样的野驱动,...

Leon的博客 594

内核输入子系统input解析

Android、X windows、qt等众多应用对于linux系统中键盘、鼠标、触摸屏等输入设备的支持都通过、或越来越倾向于标准的input输入子系统。     因为input子系统已经完成了字符驱动的文件操作接口,所以编写驱动的核心工作是完成input系统留出的接口,工作量不大。但如果你想更灵活的应用它,就需要好好的分析下input子系统了。 一、input输入子系统框架     下图是

yuantian2987的专栏 512

输入子系统模型解析

输入子系统模型解析1.为什么需要输入子系统? 2.输入子系统的介绍 3.输入子系统案例分析 范例代码#include<linux/init.h> #include<linux/module.h> #include<linux/input.h> #include<linux/miscdevice.h> #include<linux/fs.h> #include<linux/interrupt.h

夜黯竹亭风的博客 628

OpenHarmony 之多模态输入子系统源码深度架构解析

/ 创建自定义事件处理器public:// 自定义按键处理逻辑// 执行自定义操作// 自定义鼠标/触摸处理逻辑设备可以拥有自己的pro 文件用来映射自己特殊的键值,它的作用是将Linux scancode 转化为Openharmonykeycode,scancode 就是硬件直接扫描到的数字,而这些数字会通过这个 pro文件找到对应的字符串也就是keycode.按键配置文件:“/vendor/etc/keymap/” + fileName + “.pro” cat。

m0_67505153的博客 501

Linux输入子系统:从struct input_event到实战设备事件捕获与解析

本文深入解析Linux输入子系统,从struct input_event结构体到实战设备事件捕获与解析,涵盖鼠标、键盘和触屏等多种输入设备的处理技巧。通过详细的代码示例和调试经验,帮助开发者掌握输入事件的捕获、解析与优化方法,提升Linux环境下输入设备开发的效率与准确性。

weixin_28720767的博客 103

深入Linux输入子系统:从驱动开发到调试实战全解析

在Linux系统开发中,输入子系统是连接物理输入设备与用户应用的核心框架,它遵循机制与策略分离的设计哲学。其核心原理基于典型的三层架构:驱动层负责硬件交互与原始数据采集,核心层进行事件路由与抽象,事件层则通过标准接口(如evdev)向用户空间提供统一的事件流。这一设计的技术价值在于实现了硬件多样性的统一抽象,为上层应用提供了稳定、一致的输入API。在嵌入式设备、工控HMI、移动终端等应用场景中,深入理解输入子系统对于开发稳定的触摸屏、键盘、鼠标等设备驱动至关重要。本文聚焦于input_register_de

weixin_29174013的博客 262

Linux输入子系统实战:从struct input_event到鼠标、键盘、触屏事件解析与编程

本文深入解析Linux输入子系统,从struct input_event数据结构到鼠标、键盘、触屏事件的处理与编程实践。通过实战案例和调试技巧,帮助开发者掌握输入设备事件流的解析方法,提升外设驱动开发效率。

weixin_28720767的博客 174
上一篇: USB驱动程序之USB设备驱动程序
下一篇: SourceInsight 使用小技巧
ymangu
博客等级 码龄16年 202粉丝 249原创
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值