1:什么是输入设备
        
        常见的输入设备有键盘、鼠标、遥控杆、书写板、触摸屏等等,用户通过这些输入设备与Linux系统进行数据交换。
什么是输入系统?
        输入设备种类繁多,能否统一它们的接口?既在驱动层面统一,也在应用程序层面统一?可以的。
        Linux系统为了统一管理这些输入设备,实现了一套能兼容所有输入设备的框架:输入系统。驱动开发人员基于这套框架开发出程序,应用开发人员就可以使用统一的API去使用设备。

框架概述

假设用户程序直接访问/dev/input/event0设备节点,或者使用tslib
访问设备节点,数据的流程如下:
APP发起读操作,若无数据则休眠;
②用户操作设备,硬件上产生中断;
③输入系统驱动层对应的驱动程序处理中断:读取到数据,转换为标准的输入事件,向核心层汇报。所谓输入事件就是一个“struct input_event”结构体。
④核心层可以决定把输入事件转发给handler来处理:
APP对输入事件的处理。

1 编写APP需要掌握的知识

在开发板上执行该代码

hexdump /dev/input/event0

        上述该代码可以查看输入设备event0,当我们点击屏幕时,会出现如下代码

        在最下方,分别为每个数字代表含义。也就是我们通过点击屏幕,event0获得为了一系列输入事件,

        type3,对应EV_ABScode0x35对应ABS_MT_POSITION_Xcode0x36对应ABS_MT_POSITION_Y。上图中还发现有2个同步事件:它的typecodevalue都为0。表示电容屏上报了2次完整的数据。下面对其进行说明

1.内核中怎么表示一个输入设备?

        使用input_dev结构体来表示输入设备,

2.APP可以得到什么数据?

        可以得到一系列的输入事件,就是一个一个“struct input_event”,

        每个输入事件input_event中都含有发生时间:timeval表示的是“自系统启动以来过了多少时间”,它是一个结构体,含有“tv_sectv_usec”两项(即秒、微秒)
        

        输入事件input_event中更重要的是:type(哪类事件)code(哪个事件)value(事件值),细讲如下:

type:表示哪类事件
        比如EV_KEY表示按键类、EV_REL表示相对位移(比如鼠标),EV_ABS表示绝对位置(比如触摸屏)等
                
code:表示该类事件下的哪一个事件
        比如对于EV_KEY(按键)类事件,它表示键盘。键盘上有很多按键,比如数字键123,字母键ABC里等。
value:表示事件值
        对于按键,它的value可以是0(表示按键被按下)1(表示按键被松开)
2(表示长按); 对于触摸屏,它的value就是坐标值、压力值。
④事件之间的界线
APP读取数据时,可以得到一个或多个数据,比如一个触摸屏的一个触点会上报XY位置信息,也可能会上报压力值。

        APP怎么知道它已经读到了完整的数据?

驱动程序上报完一系列的数据后,会上报一个“同步事件”,表示数据上报完毕。APP读到“同步事件”时,就知道已经读完了当前的数据。 同步事件也是一个input_event结构体,它的typecodevalue三项都是0

2调试

        我们已经知道了每次输入事件会带来那些信息。

        其中输入设备的设备节点名为/dev/input/eventX(也可能是/dev/eventXX表示012等数字)。可以执行以下命令查看设备节点

ls /dev/input/* -l

怎么知道这些设备节点对应什么硬件呢?可以在板子上执行以下命令:

cat /proc/bus/input/devices

那么这里的INPSUHB对应的每一行是什么含义呢?

I:id of the device(设备ID)
        该参数由结构体struct input_id来进行描述,驱动程序中会定义这样的结构体:

N:name of the device
设备名称
P:physical path to the device in the system hierarchy
系统层次结构中设备的物理路径。
S:sysfs path
位于sys文件系统的路径
U:unique identification code for the device(if device has it)
设备的唯一标识码
H:list of input handles associated with the device.
与设备关联的输入句柄列表。
B:bitmaps(位图)
PROP:device properties and quirks(设备属性)
EV:types of events supported by the device(设备支持的事件类型)
KEY:keys/buttons this device has(此设备具有的键/按钮)
MSC:miscellaneous events supported by the device(设备支持的其他事件)
LED:leds present on the device(设备上的指示灯)
        值得注意的是 B 位图,比如上图中“B: EV=b”用来表示该设备支持哪类 输入事件。b 的二进制是 1011bit013 1,表示该设备支持 013 这 三类事件,即 EV_SYNEV_KEYEV_ABS
        再举一个例子,“B: ABS=2658000 3”如何理解?
它表示该设备支持 EV_ABS 这一类事件中的哪一些事件。这是 2 32 位的数字:0x26580000x3,高位在前低位在后,组成一个 64 位的数字: “0x2658000,00000003”,数值为 1 的位有:014748505354, 即:010x2f0x300x320x350x36这些事件。

3使用命令行读取事件

hexdump /dev/input/event0

        上述该代码可以查看输入设备event0,当我们点击屏幕时,会出现如下代码

        在最下方,分别为每个数字代表含义。也就是我们通过点击屏幕,event0获得为了一系列输入事件,

        type3,对应EV_ABScode0x35对应ABS_MT_POSITION_Xcode0x36对应ABS_MT_POSITION_Y。上图中还发现有2个同步事件:它的typecodevalue都为0。表示电容屏上报了2次完整的数据。

3:代码实现

1:获取输入类型信息

  ./01_get_input_info /dev/input/event0

        //这是我们定义的事件类型,方便将 类型的二进制转换为 字符串显示

char *ev_names[]

        // 打开 事件  /dev/input/event0

fd = open(argv[1], O_RDWR);

        //使用 ioctl() 系统调用从一个输入设备文件中获取设备的 输入子系统标识符(ID 信息),  bustype;   vendor;   product;   version;四类信息

err = ioctl(fd, EVIOCGID, &id);

        //EVIOCGBIT(EV, LEN) 用于获取输入设备支持的 EV 类型位图ioctl 会把结果写入 evbit 中。第一个参数 0 表示你要获取 事件类型(如 EV_KEY、EV_REL 等);第二个参数 sizeof(evbit) 是分配的内存大小,通常是 long evbit[NBITS(EV_MAX)] 这样的位图数组;

len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);

        //之后将 支持的事件类型打印出来  B 位图,比如上图中“B: EV=b”用来表示该设备支持哪类 输入事件。b 的二进制是 1011bit013 1,表示该设备支持 013 这 三类事件,即 EV_SYNEV_KEYEV_ABS


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

/* ./01_get_input_info /dev/input/event0 */
int main(int argc, char **argv)
{
	int fd;
	int err;
	int len;
	int i;
	unsigned char byte;
	int bit;
	struct input_id id;
	unsigned int evbit[2];
	char *ev_names[] = {
		"EV_SYN ",
		"EV_KEY ",
		"EV_REL ",
		"EV_ABS ",
		"EV_MSC ",
		"EV_SW	",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"EV_LED ",
		"EV_SND ",
		"NULL ",
		"EV_REP ",
		"EV_FF	",
		"EV_PWR ",
	};

	if (argc != 2)
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}

	fd = open(argv[1], O_RDWR);
	if (fd < 0)
	{
		printf("open %s err\n", argv[1]);
		return -1;
	}

	err = ioctl(fd, EVIOCGID, &id);
	if (err == 0)
	{
		printf("bustype = 0x%x\n", id.bustype);
		printf("vendor	= 0x%x\n", id.vendor);
		printf("product = 0x%x\n", id.product);
		printf("version = 0x%x\n", id.version);
	}

	len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
	if (len > 0 && len <= sizeof(evbit))
	{
		printf("support ev type: ");
		for (i = 0; i < len; i++)
		{
			byte = ((unsigned char *)evbit)[i];
			for (bit = 0; bit < 8; bit++)
			{
				if (byte & (1 << bit))
				{
					printf("%s ", ev_names[i * 8 + bit]);
				}
			}
		}
		printf("\n\r");
	}

	return 0;
}

2:无阻塞 \阻塞 获取输入事件信息

以无阻塞 \阻塞 方式获取信息

./01_get_input_info /dev/input/event0 noblock

fd = open(argv[1], O_RDWR | O_NONBLOCK);  //无阻塞方式

fd = open(argv[1], O_RDWR);   //阻塞方式

        //获取ID信息 后续并打印

err = ioctl(fd, EVIOCGID, &id);

        //获取 事件类型 后续并打印

len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);

        //在WHILE循环中,重复读取fd文件的event信息event 结构体包括
            struct timeval time;  // 时间戳
            __u16 type;           // 事件类型,比如 EV_KEY、EV_REL
            __u16 code;           // 键码、坐标、按钮等
            __s32 value;          // 值:按下=1,抬起=0,移动量等

       //如果是阻塞状态   如果没有点击屏幕 代码会阻塞在read函数这里,直到数据到来

        //无阻塞状态, 他会一直输出 read err  ,如果点击屏幕,会输出get event,之后继续输出read err

while (1)

    {

//读取 Linux 输入设备(如 /dev/input/eventX)的一条事件数据

        len = read(fd, &event, sizeof(event));

        if (len == sizeof(event))

        {

            printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);

        }

        else

        {

            printf("read err %d\n", len);

        }

    }


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

/* ./01_get_input_info /dev/input/event0 noblock */
int main(int argc, char **argv)
{
	int fd;
	int err;
	int len;
	int i;
	unsigned char byte;
	int bit;
	struct input_id id;
	unsigned int evbit[2];
	struct input_event event;

	char *ev_names[] = {
		"EV_SYN ",
		"EV_KEY ",
		"EV_REL ",
		"EV_ABS ",
		"EV_MSC ",
		"EV_SW	",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"EV_LED ",
		"EV_SND ",
		"NULL ",
		"EV_REP ",
		"EV_FF	",
		"EV_PWR ",
	};

	if (argc < 2)
	{
		printf("Usage: %s <dev> [noblock]\n", argv[0]);
		return -1;
	}

	if (argc == 3 && !strcmp(argv[2], "noblock"))
	{
		fd = open(argv[1], O_RDWR | O_NONBLOCK);
	}
	else
	{
		fd = open(argv[1], O_RDWR);
	}
	if (fd < 0)
	{
		printf("open %s err\n", argv[1]);
		return -1;
	}

	err = ioctl(fd, EVIOCGID, &id);
	if (err == 0)
	{
		printf("bustype = 0x%x\n", id.bustype);
		printf("vendor	= 0x%x\n", id.vendor);
		printf("product = 0x%x\n", id.product);
		printf("version = 0x%x\n", id.version);
	}

	len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
	if (len > 0 && len <= sizeof(evbit))
	{
		printf("support ev type: ");
		for (i = 0; i < len; i++)
		{
			byte = ((unsigned char *)evbit)[i];
			for (bit = 0; bit < 8; bit++)
			{
				if (byte & (1 << bit))
				{
					printf("%s ", ev_names[i * 8 + bit]);
				}
			}
		}
		printf("\n");
	}

	while (1)
	{
		len = read(fd, &event, sizeof(event));
		if (len == sizeof(event))
		{
			printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
		}
		else
		{
			printf("read err %d\n", len);
		}
	}

	return 0;
}

3:POLL方式获取输入事件信息

        POLL 机制、SELECT 机制是完全一样的,只是 APP 接口函数不一样。 简单地说,它们就是“定个闹钟”:在调用 pollselect 函数时可以传入 “超时时间”。在这段时间内,条件合适时(比如有数据可读、有空间可写)就会 立刻返回,否则等到“超时时间”结束时返回错误。
用法如下。
APP 先调用 open 函数时。
APP 不是直接调用 read 函数,而是先调用 poll select 函数,这 2 个函
数中可以传入“超时时间”。它们的作用是:如果驱动程序中有数据,则立刻返
回;否则就休眠。在休眠期间,如果有人操作了硬件,驱动程序获得数据后就
会把 APP 唤醒,导致 poll select 立刻返回;如果在“超时时间”内无人操
作硬件,则时间到后 poll select 函数也会返回。APP 可以根据函数的返回
值判断返回原因:有数据?无数据超时返回?
APP 根据 poll select 的返回值判断有数据之后,就调用 read 函数读取
数据时,这时就会立刻获得数据。
poll/select 函数可以监测多个文件,可以监测多种事件

./01_get_input_info /dev/input/event0

        //定义poll结构体,包括 int   fd;        // 文件描述符(如 /dev/input/event0)                                         short events;    // 要监听的事件(如 POLLIN 可读)

                  short revents;   // 实际发生的事件,由 poll() 填写

struct pollfd fds[1];

fd = open(argv[1], O_RDWR | O_NONBLOCK) ; //无阻塞方式打开文件 /dev/input/event0

        //获取ID,后续并打印

err = ioctl(fd, EVIOCGID, &id);

        //获取 事件类型 后续并打印

len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);

while (1)

    {

        fds[0].fd = fd;

               //有数据可读(如键盘输入等)

        fds[0].events = POLLIN;

        fds[0].revents = 0;

                //是在调用 Linux 的 poll() 系统调用,阻塞等待一组文件描述符的事件发生,超时时间为 5000 毫秒(=5秒)

        ret = poll(fds, nfds, 5000);

                //5s之内,有输入到来,则输出 get event        否则 超时判断 

        if (ret > 0)

        {

            if (fds[0].revents == POLLIN)

            {

                while (read(fd, &event, sizeof(event)) == sizeof(event))

                {

                    printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);

                }

            }

        }

        else if (ret == 0)

        {

            printf("time out\n");

        }

        else

        {

            printf("poll err\n");

        }

    }


#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>

/* ./01_get_input_info /dev/input/event0 */
int main(int argc, char **argv)
{
	int fd;
	int err;
	int len;
	int ret;
	int i;
	unsigned char byte;
	int bit;
	struct input_id id;
	unsigned int evbit[2];
	struct input_event event;
	struct pollfd fds[1];
	nfds_t nfds = 1;

	char *ev_names[] = {
		"EV_SYN ",
		"EV_KEY ",
		"EV_REL ",
		"EV_ABS ",
		"EV_MSC ",
		"EV_SW	",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"EV_LED ",
		"EV_SND ",
		"NULL ",
		"EV_REP ",
		"EV_FF	",
		"EV_PWR ",
	};

	if (argc != 2)
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}

	fd = open(argv[1], O_RDWR | O_NONBLOCK);
	if (fd < 0)
	{
		printf("open %s err\n", argv[1]);
		return -1;
	}

	err = ioctl(fd, EVIOCGID, &id);
	if (err == 0)
	{
		printf("bustype = 0x%x\n", id.bustype);
		printf("vendor	= 0x%x\n", id.vendor);
		printf("product = 0x%x\n", id.product);
		printf("version = 0x%x\n", id.version);
	}

	len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
	if (len > 0 && len <= sizeof(evbit))
	{
		printf("support ev type: ");
		for (i = 0; i < len; i++)
		{
			byte = ((unsigned char *)evbit)[i];
			for (bit = 0; bit < 8; bit++)
			{
				if (byte & (1 << bit))
				{
					printf("%s ", ev_names[i * 8 + bit]);
				}
			}
		}
		printf("\n");
	}

	while (1)
	{
		fds[0].fd = fd;
		fds[0].events = POLLIN;
		fds[0].revents = 0;
		ret = poll(fds, nfds, 5000);
		if (ret > 0)
		{
			if (fds[0].revents == POLLIN)
			{
				while (read(fd, &event, sizeof(event)) == sizeof(event))
				{
					printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
				}
			}
		}
		else if (ret == 0)
		{
			printf("time out\n");
		}
		else
		{
			printf("poll err\n");
		}
	}

	return 0;
}

4:select方式获取输入事件信息


#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>

/* According to earlier standards */
#include <sys/time.h>


/* ./01_get_input_info /dev/input/event0 */
int main(int argc, char **argv)
{
	int fd;
	int err;
	int len;
	int ret;
	int i;
	unsigned char byte;
	int bit;
	struct input_id id;
	unsigned int evbit[2];
	struct input_event event;
	int nfds;
	struct timeval tv;
	fd_set readfds;
	
	char *ev_names[] = {
		"EV_SYN ",
		"EV_KEY ",
		"EV_REL ",
		"EV_ABS ",
		"EV_MSC ",
		"EV_SW	",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"EV_LED ",
		"EV_SND ",
		"NULL ",
		"EV_REP ",
		"EV_FF	",
		"EV_PWR ",
		};
	
	if (argc != 2)
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}

	fd = open(argv[1], O_RDWR | O_NONBLOCK);
	if (fd < 0)
	{
		printf("open %s err\n", argv[1]);
		return -1;
	}

	err = ioctl(fd, EVIOCGID, &id);
	if (err == 0)
	{
		printf("bustype = 0x%x\n", id.bustype );
		printf("vendor	= 0x%x\n", id.vendor  );
		printf("product = 0x%x\n", id.product );
		printf("version = 0x%x\n", id.version );
	}

	len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
	if (len > 0 && len <= sizeof(evbit))
	{
		printf("support ev type: ");
		for (i = 0; i < len; i++)
		{
			byte = ((unsigned char *)evbit)[i];
			for (bit = 0; bit < 8; bit++)
			{
				if (byte & (1<<bit)) {
					printf("%s ", ev_names[i*8 + bit]);
				}
			}
		}
		printf("\n");
	}

	while (1)
	{
		/* 设置超时时间 */
		tv.tv_sec  = 5;
		tv.tv_usec = 0;
		
		/* 想监测哪些文件? */
		FD_ZERO(&readfds);    /* 先全部清零 */	
		FD_SET(fd, &readfds); /* 想监测fd */
		
		/* 函数原型为:
			int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
         * 我们为了"read"而监测, 所以只需要提供readfds
		 */
		nfds = fd + 1; /* nfds 是最大的文件句柄+1, 注意: 不是文件个数, 这与poll不一样 */ 
		ret = select(nfds, &readfds, NULL, NULL, &tv);
		if (ret > 0)  /* 有文件可以提供数据了 */
		{
			/* 再次确认fd有数据 */
			if (FD_ISSET(fd, &readfds))
			{
				while (read(fd, &event, sizeof(event)) == sizeof(event))
				{
					printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
				}
			}
		}
		else if (ret == 0)  /* 超时 */
		{
			printf("time out\n");
		}
		else   /* -1: error */
		{
			printf("select err\n");
		}
	}

	return 0;
}

5:异步通知方式获取输入事件信息

        异步通知,就是 APP 可以忙自己的事,当驱动程序用数据时它会主动 给 APP 发信号,这会导致 APP 执行信号处理函数。
        谁发:驱动程序发
发什么:信号
发什么信号:SIGIO
怎么发:内核里提供有函数
发给谁:APPAPP 要把自己告诉驱动
APP 收到后做什么:执行信号处理函数
信号处理函数和信号,之间怎么挂钩:APP 注册信号处理函数
        驱动程序通知 APP 时,它会发出“SIGIO”这个信号,表示有“IO 事件” 要处理。
        就 APP 而言,你想处理 SIGIO 信息,那么需要提供信号处理函数,并且要 跟 SIGIO 挂钩。这可以通过一个 signal 函数来“给某个信号注册处理函数”, 用法如下
        
除了注册 SIGIO 的处理函数,APP 还要做什么事?想想这几个问题:
内核里有那么多驱动,你想让哪一个驱动给你发 SIGIO 信号?
APP 要打开驱动程序的设备节点。
驱动程序怎么知道要发信号给你而不是别人?APP 要把自己的进程 ID 告诉
驱动程序。
APP 有时候想收到信号,有时候又不想收到信号:应该可以把 APP 的意愿告
诉驱动:设置 Flag 里面的 FASYNC 位为 1,使能“异步通知”

代码编写

        事件处理函数,如果点击了屏幕,则会调用该函数进行事件处理

void my_sig_handler(int sig)

{

    struct input_event event;

    while (read(fd, &event, sizeof(event)) == sizeof(event))

    {

        printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);

    }

}

           /* 注册信号处理函数 ,将该信号与 事件处理函数挂钩 */SIGIO 是当 某个文件描述符可进行 I/O 操作时,内核向进程发送的信号。事件处理函数用来在接收到 SIGIO 信号时执行。

signal(SIGIO, my_sig_handler);

        /* 打开驱动程序 */

fd = open(argv[1], O_RDWR | O_NONBLOCK);

err = ioctl(fd, EVIOCGID, &id);

len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);

              //把当前进程的 PID 告诉内核,以便内核在该文件描述符 fd 上发生异步事件(如 I/O 可读)时,能将 SIGIO 信号发送给这个进程。

fcntl(fd, F_SETOWN, getpid());

        /* 使能"异步通知" */

flags = fcntl(fd, F_GETFL);

fcntl(fd, F_SETFL, flags | FASYNC);


#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>

int fd;

void my_sig_handler(int sig)
{
	struct input_event event;
	while (read(fd, &event, sizeof(event)) == sizeof(event))
	{
		printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
	}
}

/* ./05_input_read_fasync /dev/input/event0 */
int main(int argc, char **argv)
{
	int err;
	int len;
	int ret;
	int i;
	unsigned char byte;
	int bit;
	struct input_id id;
	unsigned int evbit[2];
	unsigned int flags;
	int count = 0;

	char *ev_names[] = {
		"EV_SYN ",
		"EV_KEY ",
		"EV_REL ",
		"EV_ABS ",
		"EV_MSC ",
		"EV_SW	",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"NULL ",
		"EV_LED ",
		"EV_SND ",
		"NULL ",
		"EV_REP ",
		"EV_FF	",
		"EV_PWR ",
	};

	if (argc != 2)
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}

	/* 注册信号处理函数 */
	signal(SIGIO, my_sig_handler);

	/* 打开驱动程序 */
	fd = open(argv[1], O_RDWR | O_NONBLOCK);
	if (fd < 0)
	{
		printf("open %s err\n", argv[1]);
		return -1;
	}

	err = ioctl(fd, EVIOCGID, &id);
	if (err == 0)
	{
		printf("bustype = 0x%x\n", id.bustype);
		printf("vendor	= 0x%x\n", id.vendor);
		printf("product = 0x%x\n", id.product);
		printf("version = 0x%x\n", id.version);
	}

	len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit);
	if (len > 0 && len <= sizeof(evbit))
	{
		printf("support ev type: ");
		for (i = 0; i < len; i++)
		{
			byte = ((unsigned char *)evbit)[i];
			for (bit = 0; bit < 8; bit++)
			{
				if (byte & (1 << bit))
				{
					printf("%s ", ev_names[i * 8 + bit]);
				}
			}
		}
		printf("\n");
	}

	/* 把APP的进程号告诉驱动程序 */
	fcntl(fd, F_SETOWN, getpid());

	/* 使能"异步通知" */
	flags = fcntl(fd, F_GETFL);
	fcntl(fd, F_SETFL, flags | FASYNC);

	while (1)
	{
		printf("main loop count = %d\n", count++);
		sleep(2);
	}

	return 0;
}

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐