输入系统

常见的输入设备有键盘、鼠标、遥控杆、书写板、触摸屏等等,用户通过这些输入设备与Linux系统进行数据交换。

内核中怎样表示一个输入设备

// include/linux/input.h
struct input_dev {
const char *name; //设备名称
const char *phys; //设备物理路径
const char *uniq; //设备唯一标识码
struct input_id id; unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //支持什么类型的输入事件
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //支持按键输入事件的话,支持哪些按键(键盘)
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //支持相对位移事件的话,支持哪些
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
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)]; ....... };

查看所有的输入设备:

ls /dev/input/* -l

查看输入设备的信息:

cat /proc/bus/input/devices

得到如下信息:

[root@imx6ull:~]# cat /proc/bus/input/devices
I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="20cc000.snvs:snvs-powerkey"
P: Phys=snvs-pwrkey/input0
S: Sysfs=/devices/soc0/soc/2000000.aips-bus/20cc000.snvs/20cc000.snvs:snvs-powerkey/input/input0
U: Uniq=
H: Handlers=kbd event0 evbug
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0 I: Bus=0018 Vendor=dead Product=beef Version=28bb //设备ID(定义在input.h的struct input_id结构体)
N: Name="goodix-ts" //名称
P: Phys=input/ts //物理地址
S: Sysfs=/devices/virtual/input/input1 //sys系统地址
U: Uniq= //标识号(无)
H: Handlers=event1 evbug
B: PROP=2 //设备属性
B: EV=b //支持何种输入事件
B: KEY=1c00 0 0 0 0 0 0 0 0 0 0 //设备具有的键
B: ABS=6e18000 0 I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="gpio-keys"
P: Phys=gpio-keys/input0
S: Sysfs=/devices/soc0/gpio-keys/input/input2
U: Uniq=
H: Handlers=kbd event2 evbug
B: PROP=0
B: EV=3
B: KEY=c

APP可以获得什么数据

// include/linux/input.h
struct input_value {
__u16 type; //当前数据的事件类型
__u16 code; //当前事件类型下的哪一个事件
__s32 value; //
};

Type的内容:

// include/uapi/linux/input-event-codes.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的内容(以EV_KEY举例)

// include/uapi/linux/input-event-codes.h
#define KEY_RESERVED 0
#define KEY_ESC 1
#define KEY_1 2
#define KEY_2 3
#define KEY_3 4
#define KEY_4 5
#define KEY_5 6
#define KEY_6 7
#define KEY_7 8
#define KEY_8 9
#define KEY_9 10
#define KEY_0 11

获取输入设备信息实例

两个ioctl的request参数说明(input.h)
request 说明
EVIOCGID 返回输入设备ID
EVIOCGBIT(ev,len) 获取输入设备支持的事件类型列表

ev值的说明:ev参数表示要获取的事件类型,它是一个整数值

  • 当ev=0,表示要获取输入设备支持的所有事件类型列表,包括键盘事件、鼠标事件、相对事件、绝对事件、事件同步、杂项事件等。
  • 当ev=1,表示要获取输入设备支持的键盘事件类型列表。
  • 当ev=2,表示要获取输入设备支持的相对事件类型列表。

EVIOCGBIT的iotcl调用说明:必须使用

len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit);
//len是evbit的实际读取大小,如果单独使用sizeof(evbit)得到len,将发生段错误
源码:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/input.h> /* 用法:./get_input_info /dev/input/event0 */
int main(int argc, char const **argv)
{
int fd;
struct input_id id;
int err;
unsigned char byte;
unsigned int evbit[2];
int i;
int bit;
unsigned int len;
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 == -1)
{
printf("can not open %s\n", argv[1]);
return -1;
}
err = ioctl(fd, EVIOCGID, &id); //返回输入设备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); //返回输入事件类型
printf("support ev type:\n");
for(i = 0;i < len;i++)
{
byte = ((unsigned char *)evbit)[i];
for(bit = 0;bit < 8;bit++)
{
if(byte & (1<<bit))
{
printf("%s \n", ev_names[i*8 + bit]);
}
} }
return 0; }
实验结果:
[root@imx6ull:/mnt]# ./get_input_info /dev/input/event0
bustype = 0x19
vendor = 0x0
product = 0x0
version = 0x0
support ev type:
EV_SYN
EV_KEY
[root@imx6ull:/mnt]# ./get_input_info /dev/input/event1
bustype = 0x18
vendor = 0xdead
product = 0xbeef
version = 0x28bb
support ev type:
EV_SYN
EV_KEY
EV_ABS
[root@imx6ull:~]# cat /proc/bus/input/devices
I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="20cc000.snvs:snvs-powerkey"
P: Phys=snvs-pwrkey/input0
S: Sysfs=/devices/soc0/soc/2000000.aips-bus/20cc000.snvs/20cc000.snvs:snvs-powerkey/input/input0
U: Uniq=
H: Handlers=kbd event0 evbug
B: PROP=0
B: EV=3
B: KEY=100000 0 0 0 I: Bus=0018 Vendor=dead Product=beef Version=28bb
N: Name="goodix-ts"
P: Phys=input/ts
S: Sysfs=/devices/virtual/input/input1
U: Uniq=
H: Handlers=event1 evbug
B: PROP=2
B: EV=b
B: KEY=1c00 0 0 0 0 0 0 0 0 0 0
B: ABS=6e18000 0 I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="gpio-keys"
P: Phys=gpio-keys/input0
S: Sysfs=/devices/soc0/gpio-keys/input/input2
U: Uniq=
H: Handlers=kbd event2 evbug
B: PROP=0
B: EV=3
B: KEY=c

结论:EV值与程序输出的type结果一致

查询和休眠唤醒方式读输入事件

所谓的阻塞与非阻塞,是在open处声明。当设置为阻塞方式,如果没有输入事件,整个进程都在阻塞态

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <unistd.h>
#include <string.h> /* 用法:./get_input_info /dev/input/event0 */
int main(int argc, char const **argv)
{
int fd;
unsigned int len;
struct input_event event; //read读到的是input_event类型的结构体 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 == -1)
{
printf("can not open %s\n", argv[1]);
return -1;
} while(1)
{
len = read(fd, &event, sizeof(event)); //阻塞方式下,进程阻塞在此
if(len == sizeof(event))
{
printf("type = 0x%x, code = 0x%x, value = 0x%x", event.type, event.code, event.value);
}
else
{
printf("read err %d", len);
}
}
return 0; }
实验现象:
  • 查询方式(非阻塞):反复查询,输出"read err",直到操作输入设备时,输出内容更改为输入事件内容
  • 休眠-唤醒方式(阻塞):只有操作屏幕,才会输出事件内容

POLL方式读输入事件

poll会在设定的时间内进行监听,当改时间内有输入事件返回或超过设定时间没有事件返回,poll都将唤醒。poll/select函数可以监测多个文件,可以监测多种事件。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <unistd.h>
#include <string.h>
#include <poll.h> /* 用法:./get_input_info /dev/input/event0 */
int main(int argc, char const **argv)
{
int fd;
struct input_event event; //read读到的是input_event类型的结构体
struct pollfd pollfd;
nfds_t nfds = 1; //同时打开一个文件 if(argc != 2)
{
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR | O_NONBLOCK); //非阻塞(查询)
if(fd == -1)
{
printf("can not open %s\n", argv[1]);
return -1;
}
while(1)
{
pollfd.fd = fd;
pollfd.events = POLLIN;
pollfd.revents = 0; //revents初始化为0,当有输入事件传入,内核改写revents
poll(&pollfd, nfds, 3000); //poll等待时间为3s
if(pollfd.revents == POLLIN) //只有poll函数返回了数据,才调用read
{
while(read(fd, &event, sizeof(event)) == sizeof(event)) //把一次获取到的数据读完再退出
{
printf("type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
}
}
else if(pollfd.revents == 0)
{
printf("time out\n");
}
else
{
printf("read err\n");
}
}
return 0; }
关于POLL实现多路复用IO
struct pollfd pollfd[n];    //n为文件个数
nfds_t nfds = n; //同时打开n个文件 ....... if(pollfd[0].revents == POLLIN){} //依次访问revents
if(pollfd[1].revents == POLLIN){} .......

异步通知方式读输入事件

[补充]fcntl的五个功能:

  • 复制一个现有的描述符(cmd=F_DUPFD).
  • 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
  • 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
  • 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
  • 获得/设置记录锁(cmd=F_GETLK , F_SETLK或F_SETLKW).
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <unistd.h>
#include <string.h>
#include <signal.h> int fd; void sig_func(int sig)
{
struct input_event event;
while(read(fd, &event, sizeof(event)) == sizeof(event))
{
printf("type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
}
} /* 用法:./get_input_info /dev/input/event0 */
int main(int argc, char const **argv)
{
int count = 0;
unsigned short flag;
if(argc != 2)
{
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
signal(SIGIO, sig_func); //1.注册信号处理函数(信号类型为IO类型)
fd = open(argv[1], O_RDWR | O_NONBLOCK); //2.打开驱动(一定要用非阻塞方式,否则无输入事件进程一直被阻塞)
if(fd == -1)
{
printf("can not open %s\n", argv[1]);
return -1;
}
fcntl(fd ,F_SETOWN, getpid()); //3.告知驱动程序app进程ID
flag = fcntl(fd, F_GETFL); //4.获得文件状态标记
fcntl(fd, F_SETFL, flag | FASYNC); //5.设置文件状态标记(将进程添加到驱动fasync事件等待队列) while(1)
{
printf("count = %d\n", count++);
sleep(2);
}
return 0; }
实验结果:
[root@imx6ull:/mnt]# ./get_input_info /dev/input/event1
count = 0
count = 1
count = 2 //无输入事件时正常计数
type = 0x3, code = 0x39, value = 0x6
type = 0x3, code = 0x35, value = 0x1a6
type = 0x3, code = 0x36, value = 0x131
type = 0x3, code = 0x30, value = 0x1f
type = 0x3, code = 0x3a, value = 0x1f
type = 0x1, code = 0x14a, value = 0x1
type = 0x0, code = 0x0, value = 0x0
count = 3
type = 0x3, code = 0x35, value = 0x1a7
type = 0x0, code = 0x0, value = 0x0
count = 4
type = 0x3, code = 0x35, value = 0x1a9
type = 0x0, code = 0x0, value = 0x0
count = 5
type = 0x3, code = 0x35, value = 0x1a8
type = 0x0, code = 0x0, value = 0x0
count = 6

嵌入式Linux—输入子系统的更多相关文章

  1. linux输入子系统概念介绍

    在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架.自动创建设备节点.linux中断.poll机制.异步通知.同步互斥.非阻塞.定时器去抖动. 上一节文章链接:http://blo ...

  2. Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值

    Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值 题外话:一个问题研究到最后,那边记录文档的前半部分基本上都是没用的,甚至是错误的. 重点在最后,前边不过一些假想猜測. ht ...

  3. linux输入子系统(input subsystem)之evdev.c事件处理过程

    1.代码 input_subsys.drv.c 在linux输入子系统(input subsystem)之按键输入和LED控制的基础上有小改动,input_subsys_test.c不变. input ...

  4. Linux输入子系统(转)

    Linux输入子系统(Input Subsystem) 1.1.input子系统概述 输入设备(如按键,键盘,触摸屏,鼠标等)是典型的字符设备,其一般的工作机制是低层在按键,触摸等动作发生时产生一个中 ...

  5. Linux输入子系统(Input Subsystem)

    Linux输入子系统(Input Subsystem) http://blog.csdn.net/lbmygf/article/details/7360084 input子系统分析  http://b ...

  6. Linux输入子系统框架分析(1)

    在Linux下的输入设备键盘.触摸屏.鼠标等都能够用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层.事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备 ...

  7. Linux输入子系统详解

    input输入子系统框架  linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Input ...

  8. linux输入子系统

    linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(InputCore)和输入子系统设备驱 ...

  9. linux输入子系统简述【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/7678035 1,linux输入子系统简述 其实驱动这部分大多还是转载别人的,linux ...

  10. 7.Linux 输入子系统分析

    为什么要引入输入子系统? 在前面我们写了一些简单的字符设备的驱动程序,我们是怎么样打开一个设备并操作的呢? 一般都是在执行应用程序时,open一个特定的设备文件,如:/dev/buttons .... ...

随机推荐

  1. redis集群之分片集群的原理和常用代理环境部署

    上篇文章刚刚介绍完redis的主从复制集群,但主从复制集群主要是为了解决redis集群的单点故障问题,通过整合哨兵能实现集群的高可用:但是却无法解决数据容量以及单节点的压力问题,所以本文继续介绍red ...

  2. JavaEE Day04 MySQL多表&事务

    今日内容 多表查询 事务 DCL用于控制权限和管理用户,DBA完成:SQL中四类DDL  DML  DQL  DCL 一.多表查询 1.多表查询_概述 1.1 查询语法     select      ...

  3. Spring框架之IOC入门

    1.开发环境 IDEA版本: 2022.1.4 Maven版本:3.8.6 Spring版本:6.0.2 2.案例分析 2.1 自定义Man类 2.2 自定义Company类 2.3 自定义Compa ...

  4. docker部署项目

    @ 目录 前言 一.下载安装docker: 1.前提工作 1.1 查看linux版本 1.2 yum包更新到最新 1.3 安装工具包 1.4 设置yum源并更新yum包索引 2.安装docker 2. ...

  5. 解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

    大家好,又见面了. 本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容,将会通过系列专题,讲清楚缓存的方方面面.如果感兴趣,欢迎关注以获取后续更新. 上一篇文章中,我们继Guava Cac ...

  6. 使用NPOI core插入图片

    闲的无聊,封装一个NPOI core插入图片,下面贴上代码,有注释,我就不讲解了 public class ExcelHelper { /// <summary> /// excel插入图 ...

  7. 记一次 .NET 某工控MES程序 崩溃分析

    一:背景 1.讲故事 前几天有位朋友找到我,说他的程序出现了偶发性崩溃,已经抓到了dump文件,Windows事件日志显示的崩溃点在 clr.dll 中,让我帮忙看下是怎么回事,那到底怎么回事呢? 上 ...

  8. 周结之json补充、正则re模块、hashlib模块、logging模块

    周结 目录 周结 json补充 正则表达式 re模块 第三方模块的下载 request模块 办公自动化openpyxl模块 hashlib加密模块 subprocess模块 logging日志模块 j ...

  9. vivo 实时计算平台建设实践

    作者:vivo 互联网实时计算团队- Chen Tao 本文根据"2022 vivo开发者大会"现场演讲内容整理而成. vivo 实时计算平台是 vivo 实时团队基于 Apach ...

  10. 2023牛客寒假算法基础集训营1 ACDEFGHKLM

    比赛链接 A 题解 知识点:模拟. 显然. (用char输入到一半直接给答案跳出,WA了两小时,无话可说. 时间复杂度 \(O(1)\) 空间复杂度 \(O(1)\) 代码 #include < ...