手把手教你写Linux设备驱动---input子系统(一)--input事件应用程序的读写实现(基于友善之臂4412开发板)

     这节,我们来说下input子系统,什么是input子系统?

input子系统就是输入子系统。

     输入子系统是 Linux内核用于管理各种输入设备 (键盘,鼠标,遥控杆,书写板等等 )的部分,用户通过输入子系统进行内核,命令行,图形接口之间的交换。输入子系统在内核里实现,因为设备经常要通过特定的硬件接口被访问 (例如串口, ps/2, usb等等 ),这些硬件接口由内核保护和管理。内核给用户导出一套固定的硬件无关的 input API,供用户空间程序使用。

输入子系统分为三块: input core, drivers和 event handlers。正常的路径是从底层硬件到驱动,从驱动到 input core,

从 input core到 event handler,从 event handler到 user space。在应用程序的角度,我们只要关心user space就行了

其余的部分我们不用关心。

我们先从应用程序的角度来认识下input子系统,我们可以从这个文件可以看到对应的设备
/proc/bus/input/devices

I: Bus=0003 Vendor=046d Product=c018 Version=0111

N: Name=" USB Optical Mouse"

P: Phys=usb-0000:00:1d.1-2/input0

S: Sysfs=/class/input/input24

U: Uniq=

H: Handlers=mouse1 event2

B: EV=7

B: KEY=70000 0 0 0 0 0 0 0 0

B: REL=103

在Linux系统下cat //proc/bus/input/devices即可看到以上相关的信息,这些devices主要是用来描述注册在input子系统的一些

设备文件,可能有鼠标,键盘,触摸屏,重力传感器,温度传感器等等的设备。

那我们如何来描述这样的一个设备呢?Linux系统为我们提供了以下这个头文件:

#include <linux/input.h>

在这个文件中,我们可以找到这个结构体:

描述一个输入事件
/*
 * The event structure itself
 */

struct input_event {
	struct timeval time;
	__u16 type;
	__u16 code;
	__s32 value;
};

先来解读下这个结构体的含义:

struct timeval结构体在time.h中的定义为:

struct timeval
{
__time_t tv_sec;        /* Seconds. */
__suseconds_t tv_usec;    /*Microseconds. */
};

其中,tv_sec为Epoch到创建struct timeval时的秒数,tv_usec为微秒数,即秒后面的零头。

type域显示了被报告事件的类型,例如,一个 key press或者 button press, relative motion(比如移动鼠标 )

或者 absolute motion(比如移动游戏杆 ); code域告诉你是哪一个key或者坐标轴在被操作; 

value域告诉你现在的状态或者运动情况是什么。

那么,最主要的事件有以下三种:

相对事件(鼠标),绝对事件(触摸屏),键盘事件。
例如:我们说说鼠标,我们在移动鼠标的时候鼠标就是一个相对事件,所以type的类型也就是底层上报给用户的事件为

相对事件类型,那么code表示的就是相对于鼠标当前的位置的X或者Y的坐标,value也就是相对于当前的位置偏移多少。

事件类型(type)在input.h主要有以下:

/*
 * Event types
 */

#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
#define EV_SND			0x12
#define EV_REP			0x14
#define EV_FF			0x15
#define EV_PWR			0x16
#define EV_FF_STATUS		0x17
#define EV_MAX			0x1f
#define EV_CNT			(EV_MAX+1)

我们这节主要来写写鼠标的事件读取还有控制,所以我们找到鼠标相关的code:

/*
 * Relative axes
 */
//在这里,我们暂时只会用来REL_X和REL_Y这两个参数
#define REL_X			0x00    //相对X坐标
#define REL_Y			0x01    //相对Y坐标
#define REL_Z			0x02
#define REL_RX			0x03
#define REL_RY			0x04
#define REL_RZ			0x05
#define REL_HWHEEL		0x06
#define REL_DIAL		0x07
#define REL_WHEEL		0x08
#define REL_MISC		0x09
#define REL_MAX			0x0f
#define REL_CNT			(REL_MAX+1)

那么value,就是选择具体的type,具体的code以后所反应出来的值,鼠标就是相对于当前X或者相对于当前Y的值。
接下来,我们来看一下如何来读取鼠标事件,写一段代码测试一下:

#include <stdio.h>
#include <linux/input.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
/*
struct input_event {
        struct timeval time;
        __u16 type;
        __u16 code;
        __s32 value;
};
*/
/*
Event types
#define EV_SYN                  0x00
#define EV_KEY                  0x01
#define EV_REL                  0x02
#define EV_ABS                  0x03
*/
/*
 Relative axes

#define REL_X                   0x00
#define REL_Y                   0x01
#define REL_Z                   0x02
#define REL_RX                  0x03
#define REL_RY                  0x04
#define REL_RZ                  0x05
#define REL_HWHEEL              0x06
#define REL_DIAL                0x07
#define REL_WHEEL               0x08
#define REL_MISC                0x09
#define REL_MAX                 0x0f
#define REL_CNT                 (REL_MAX+1)
*/

//event8  mouse
//event9  keyboard
int main(void)
{
	//1、定义一个结构体变量用来描述input事件
	struct input_event event_mouse ;
	//2、打开input设备的事件节点  我的电脑鼠标事件的节点是event3
	int fd = open("/dev/input/event3",O_RDWR);
	int value ;
	int type ;
	int buffer[10]={0};
	if(-1 == fd){
		printf("open mouse event fair!\n");
		return -1 ;
	}
	while(1){
		//3、读事件
		read(fd ,&event_mouse ,sizeof(event_mouse));
		//4、判断事件类型,并打印键码
		switch(event_mouse.type){
			case EV_SYN:
			     printf("sync!\n");
			     break ;
			case EV_REL:
			//鼠标事件,XY相对位移
			//code表示相对位移X或者Y,当判断是X时,打印X的相对位移value
			//当判断是Y时,打印Y的相对位移value
			if(event_mouse.code == REL_X){
			     printf("event_mouse.code_X:%d\n",event_mouse.code);
			     printf("event_mouse.value_X:%d\n",event_mouse.value);
			}
			if(event_mouse.code == REL_Y){
			     printf("event_mouse.code_Y:%d\n",event_mouse.code);
			     printf("event_mouse.value_Y:%d\n",event_mouse.value);
			}
			defalut:
			break ;
		}
	}
	return 0 ;
}

编译完后,执行,当移动鼠标的时候我们就可以看到以下信息:

说明,此时已经读到鼠标事件所反应出来的值了。

那么,既然可以做读的操作,那肯定也就可以写了,可不可以写一个程序,让鼠标画个正方形,或者让它自动按着自己

的路径去移动?答案是肯定可以的,要不然游戏外挂都是怎么做出来的?大家说是吧,下面我们就让鼠标来自己画一个

正方形,上代码:

#include <stdio.h>
#include <linux/input.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
/*
struct input_event {
        struct timeval time;
        __u16 type;
        __u16 code;
        __s32 value;
};
*/
/*
Event types
#define EV_SYN                  0x00
#define EV_KEY                  0x01
#define EV_REL                  0x02
#define EV_ABS                  0x03
*/
/*
 Relative axes

#define REL_X                   0x00
#define REL_Y                   0x01
#define REL_Z                   0x02
#define REL_RX                  0x03
#define REL_RY                  0x04
#define REL_RZ                  0x05
#define REL_HWHEEL              0x06
#define REL_DIAL                0x07
#define REL_WHEEL               0x08
#define REL_MISC                0x09
#define REL_MAX                 0x0f
#define REL_CNT                 (REL_MAX+1)
*/

//event8  mouse
//event9  keyboard
int main(void)
{
	//1、定义一个结构体变量用来描述input事件
	struct input_event event_mouse ;
	//2、打开input设备的事件节点  我的电脑鼠标事件的节点是event3
	int fd = open("/dev/input/event3",O_RDWR);
	int value ;
	int type ;
	int i ;
	int buffer[10]={0};
	if(-1 == fd){
		printf("open mouse event fair!\n");
		return -1 ;
	}
	while(1){
		//3、写事件
		for(i = 0 ; i < 20 ; i++){
		   event_mouse.type = EV_REL ;
		   event_mouse.code = REL_X ;
		   event_mouse.value = i ;
		   write(fd,&event_mouse,sizeof(event_mouse));
		   event_mouse.code = 0 ;
		   event_mouse.value = 0;
		   event_mouse.type = EV_SYN ;
		   write(fd,&event_mouse,sizeof(event_mouse));
		   usleep(50000);
		}
		for(i = 0 ; i < 20 ; i++){
		   event_mouse.type = EV_REL ;
		   event_mouse.code = REL_Y ;
		   event_mouse.value = i ;
		   write(fd,&event_mouse,sizeof(event_mouse));
		   event_mouse.code = 0 ;
		   event_mouse.value = 0 ;
		   event_mouse.type = EV_SYN ;
		   write(fd,&event_mouse,sizeof(event_mouse));
		   usleep(50000);
		}
		for(i = 0 ; i > -20 ; i--){
		   event_mouse.type = EV_REL ;
		   event_mouse.code = REL_X ;
		   event_mouse.value = i ;
		   write(fd,&event_mouse,sizeof(event_mouse));
		   event_mouse.code = 0 ;
		   event_mouse.value = 0;
		   event_mouse.type = EV_SYN ;
		   write(fd,&event_mouse,sizeof(event_mouse));
		   usleep(50000);
		}
		for(i = 0 ; i > -20 ; i--){
		   event_mouse.type = EV_REL ;
		   event_mouse.code = REL_Y ;
		   event_mouse.value = i ;
		   write(fd,&event_mouse,sizeof(event_mouse));
		   event_mouse.code = 0 ;
		   event_mouse.value = 0 ;
		   event_mouse.type = EV_SYN ;
		   write(fd,&event_mouse,sizeof(event_mouse));
		   usleep(50000);
		}

	}
	return 0 ;
}

执行结果:
此时鼠标是在移动的,由于以下的图片是禁止的,效果留给大家自己去尝试。


下节,我们将来实现一下input驱动。

时间: 2024-11-03 10:00:21

手把手教你写Linux设备驱动---input子系统(一)--input事件应用程序的读写实现(基于友善之臂4412开发板)的相关文章

手把手教你写Linux设备驱动---input子系统(三)--电容屏事件坐标读取(基于友善之臂4412开发板)

前面我们学习了鼠标是如何如何通过应用程序来读取事件和坐标值的,后面也写了一个简单的input系统的按键驱动程序. 博文如下,讲的内容非常清楚,给小白来入手当然是非常容易的: http://blog.csdn.net/morixinguan/article/details/69808832 这节,我们来学习一下触摸屏事件获取,然后上一个基于4412开发板ft5x0x型号的x,y坐标值读取,后面我们将从零开始实现这款触摸屏的驱动程序: 首先,我们要明白一个概念,触摸屏在input系统中是一类什么事件

手把手教你写Linux设备驱动---input子系统(二)--按键驱动实现(一)(基于友善之臂4412开发板)

在上一节里,我们用一个应用程序实现了鼠标的控制,并控制鼠标用相对位移不断的画一个正方形,感觉非常有意思,这一节,我们将通过一个简单按键实例来真正的实现一个input设备驱动程序. http://blog.csdn.net/morixinguan/article/details/69808832 在写Input驱动之前,我们要了解下这个结构体,在此,我们要包含相应的头文件: #include <linux/input.h> 我们在这个头文件中找到了以下结构体,它就是input设备的核心: //用

手把手教你写Linux设备驱动---input子系统(四)--电容屏驱动ft5x06编写(一)(基于友善之臂4412开发板)

这一节,我们将从零开始写tiny4412的触摸屏驱动ft5x06,写这节博客之前,先了解下需要什么知识: 1.i2c驱动相关的知识 2.输入子系统 3.中断 4.工作队列 关于i2c驱动相关的知识,在后期的博文里会专门写几篇博文来进行总结,这里就不再说i2c相关的知识,我们先知道怎么用就行了. 首先,我在ts.h构造了一个ts_info_st结构体,用来存放触摸屏的中断线,x坐标,y坐标,压力值. 用ts_st构造了该触摸屏的设备结构体. 我们还是直接看点实际的东西,上代码: ts.h #ifn

手把手教你写Linux设备驱动---中断(一)(基于友善之臂4412开发板)

今天,我们要来实现一个基于tiny4412开发板上的最简本的按键中断驱动程序,那么,写这个程序之前,我们先来了解下Linux中断的基本知识. 在Linux内核中,每一个能够发出中断请求的硬件设备控制器都有一条名为IRQ的输出线.所有现在存在的IRQ线都与一个名为可编程中断控制器的硬件电路的输入引脚相连,我们可以来看下4412上与板子上相连的按键. 下面这张电路图,也就是4412板子上按键的电路图和CPU的连接关系图: 我们明显可以看到,4个按键分别接在GPX3这几个引脚上,对应着引脚,接下来我们

手把手教你写Linux设备驱动---定时器(一)(基于友善之臂4412开发板)

这个专题我们来说下Linux中的定时器. 在Linux内核中,有这样的一个定时器,叫做内核定时器,内核定时器用于控制某个函数,也就是定时器将要处理的函数在未来的某个特定的时间内执行.内核定时器注册的处理函数只执行一次,即不是循环执行的. 如果对延迟的精度要求不高的话,最简单的实现方法如下---忙等待: Unsigned long j = jiffies + jit_delay * HZ; While(jiffies < j) { -- } 下面来说下具体的参数代表的含义: jiffies:全局变

手把手教你写Linux设备驱动---中断(二)--tasklet实现(基于友善之臂4412开发板)

上节:http://blog.csdn.net/morixinguan/article/details/68958185 在上一节博文中,教会了大家如何来写一个Linux设备的中断程序,实现也非常简单,我们来回顾一下具体的操作流程,只要遵循以下几个步骤即可实现最简单的中断处理程序: 使用中断相关的API和定义时要包含以下头文件: #include <linux/interrupt.h> 然后写中断需要以下步骤 1.申请中断号 使用gpio_to_irq函数,可以从返回值获取到对应的中断号 2.

手把手教你从零实现Linux misc设备驱动二(基于友善之臂4412开发板)

上一节,我教大家实现了一个最简单的MISC设备驱动,那么这节,我们将用一个实例来驱动蜂鸣器,这里为了方便,我就不再写应用程序进行测试,直接在驱动里调用open函数,这个程序是在Android系统里跑起来,后面我会教大家如何在Android下写应用测试程序. 我们参考以前写的蜂鸣器驱动程序,将它移植到我们这个程序里,让它成为一个MISC设备. 参考以前写的文章: http://blog.csdn.net/morixinguan/article/details/50628588 接下来,看看代码:

手把手教你从零实现Linux misc设备驱动一(基于友善之臂4412开发板)

关于如何来写一个misc设备,在前面有篇文章已经介绍了大致的流程,现在就让我们来实现一个最简单的misc设备驱动. http://blog.csdn.net/morixinguan/article/details/52700146 关于前面的字符设备有以下四篇文章,可以做参考: http://blog.csdn.net/morixinguan/article/details/55002774 http://blog.csdn.net/morixinguan/article/details/550

手把手教你写Linux设备驱动---中断(三)--workqueue实现(基于友善之臂4412开发板)

上节,我们讲到如何来实现tasklet小任务机制 http://blog.csdn.net/morixinguan/article/details/69666935 这节,我们来实现一下中断下半部的工作队列: 在写这个demo之前,我们要了解一下工作队列的相关数据结构还有API. 需要包含的头文件: #include <linux/workqueue.h> 基本的数据结构: //工作队列结构 struct work_struct { atomic_long_t data; //链表处理 str