经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Linux/Shell » 查看文章
嵌入式Linux—输入子系统
来源:cnblogs  作者:Tayoou  时间:2023/2/27 9:44:27  对本文有异议

输入系统

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

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

  1. // include/linux/input.h
  2. struct input_dev {
  3. const char *name; //设备名称
  4. const char *phys; //设备物理路径
  5. const char *uniq; //设备唯一标识码
  6. struct input_id id;
  7. unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
  8. unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //支持什么类型的输入事件
  9. unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //支持按键输入事件的话,支持哪些按键(键盘)
  10. unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //支持相对位移事件的话,支持哪些
  11. unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
  12. unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
  13. unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
  14. unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
  15. unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
  16. unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
  17. .......
  18. };

查看所有的输入设备:

  1. ls /dev/input/* -l

查看输入设备的信息:

  1. cat /proc/bus/input/devices

得到如下信息:

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

APP可以获得什么数据

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

Type的内容:

  1. // include/uapi/linux/input-event-codes.h
  2. /*
  3. * Event types
  4. */
  5. #define EV_SYN 0x00 //同步事件
  6. #define EV_KEY 0x01 //键盘事件
  7. #define EV_REL 0x02 //相对位移事件
  8. #define EV_ABS 0x03 //绝对位移事件
  9. #define EV_MSC 0x04
  10. #define EV_SW 0x05
  11. #define EV_LED 0x11
  12. #define EV_SND 0x12
  13. #define EV_REP 0x14
  14. #define EV_FF 0x15
  15. #define EV_PWR 0x16
  16. #define EV_FF_STATUS 0x17
  17. #define EV_MAX 0x1f
  18. #define EV_CNT (EV_MAX+1)

code的内容(以EV_KEY举例)

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

获取输入设备信息实例

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

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

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

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

  1. len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit);
  2. //len是evbit的实际读取大小,如果单独使用sizeof(evbit)得到len,将发生段错误
源码:
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <sys/ioctl.h>
  6. #include <linux/input.h>
  7. /* 用法:./get_input_info /dev/input/event0 */
  8. int main(int argc, char const **argv)
  9. {
  10. int fd;
  11. struct input_id id;
  12. int err;
  13. unsigned char byte;
  14. unsigned int evbit[2];
  15. int i;
  16. int bit;
  17. unsigned int len;
  18. char *ev_names[] = {
  19. "EV_SYN ",
  20. "EV_KEY ",
  21. "EV_REL ",
  22. "EV_ABS ",
  23. "EV_MSC ",
  24. "EV_SW ",
  25. "NULL ",
  26. "NULL ",
  27. "NULL ",
  28. "NULL ",
  29. "NULL ",
  30. "NULL ",
  31. "NULL ",
  32. "NULL ",
  33. "NULL ",
  34. "NULL ",
  35. "NULL ",
  36. "EV_LED ",
  37. "EV_SND ",
  38. "NULL ",
  39. "EV_REP ",
  40. "EV_FF ",
  41. "EV_PWR ",
  42. };
  43. if(argc != 2)
  44. {
  45. printf("Usage: %s <dev>\n", argv[0]);
  46. return -1;
  47. }
  48. fd = open(argv[1], O_RDWR);
  49. if(fd == -1)
  50. {
  51. printf("can not open %s\n", argv[1]);
  52. return -1;
  53. }
  54. err = ioctl(fd, EVIOCGID, &id); //返回输入设备ID
  55. if(err == 0)
  56. {
  57. printf("bustype = 0x%x\n", id.bustype );
  58. printf("vendor = 0x%x\n", id.vendor );
  59. printf("product = 0x%x\n", id.product );
  60. printf("version = 0x%x\n", id.version );
  61. }
  62. len = ioctl(fd, EVIOCGBIT(0,sizeof(evbit)), evbit); //返回输入事件类型
  63. printf("support ev type:\n");
  64. for(i = 0;i < len;i++)
  65. {
  66. byte = ((unsigned char *)evbit)[i];
  67. for(bit = 0;bit < 8;bit++)
  68. {
  69. if(byte & (1<<bit))
  70. {
  71. printf("%s \n", ev_names[i*8 + bit]);
  72. }
  73. }
  74. }
  75. return 0;
  76. }
实验结果:
  1. [root@imx6ull:/mnt]# ./get_input_info /dev/input/event0
  2. bustype = 0x19
  3. vendor = 0x0
  4. product = 0x0
  5. version = 0x0
  6. support ev type:
  7. EV_SYN
  8. EV_KEY
  9. [root@imx6ull:/mnt]# ./get_input_info /dev/input/event1
  10. bustype = 0x18
  11. vendor = 0xdead
  12. product = 0xbeef
  13. version = 0x28bb
  14. support ev type:
  15. EV_SYN
  16. EV_KEY
  17. EV_ABS
  1. [root@imx6ull:~]# cat /proc/bus/input/devices
  2. I: Bus=0019 Vendor=0000 Product=0000 Version=0000
  3. N: Name="20cc000.snvs:snvs-powerkey"
  4. P: Phys=snvs-pwrkey/input0
  5. S: Sysfs=/devices/soc0/soc/2000000.aips-bus/20cc000.snvs/20cc000.snvs:snvs-powerkey/input/input0
  6. U: Uniq=
  7. H: Handlers=kbd event0 evbug
  8. B: PROP=0
  9. B: EV=3
  10. B: KEY=100000 0 0 0
  11. I: Bus=0018 Vendor=dead Product=beef Version=28bb
  12. N: Name="goodix-ts"
  13. P: Phys=input/ts
  14. S: Sysfs=/devices/virtual/input/input1
  15. U: Uniq=
  16. H: Handlers=event1 evbug
  17. B: PROP=2
  18. B: EV=b
  19. B: KEY=1c00 0 0 0 0 0 0 0 0 0 0
  20. B: ABS=6e18000 0
  21. I: Bus=0019 Vendor=0001 Product=0001 Version=0100
  22. N: Name="gpio-keys"
  23. P: Phys=gpio-keys/input0
  24. S: Sysfs=/devices/soc0/gpio-keys/input/input2
  25. U: Uniq=
  26. H: Handlers=kbd event2 evbug
  27. B: PROP=0
  28. B: EV=3
  29. B: KEY=c

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

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

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

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <sys/ioctl.h>
  6. #include <linux/input.h>
  7. #include <unistd.h>
  8. #include <string.h>
  9. /* 用法:./get_input_info /dev/input/event0 */
  10. int main(int argc, char const **argv)
  11. {
  12. int fd;
  13. unsigned int len;
  14. struct input_event event; //read读到的是input_event类型的结构体
  15. if(argc < 2)
  16. {
  17. printf("Usage: %s <dev> [noblock]\n", argv[0]);
  18. return -1;
  19. }
  20. if(argc == 3 && !strcmp(argv[2], "noblock"))
  21. {
  22. fd = open(argv[1], O_RDWR | O_NONBLOCK); //非阻塞(查询)
  23. }
  24. else
  25. {
  26. fd = open(argv[1], O_RDWR);
  27. }
  28. if(fd == -1)
  29. {
  30. printf("can not open %s\n", argv[1]);
  31. return -1;
  32. }
  33. while(1)
  34. {
  35. len = read(fd, &event, sizeof(event)); //阻塞方式下,进程阻塞在此
  36. if(len == sizeof(event))
  37. {
  38. printf("type = 0x%x, code = 0x%x, value = 0x%x", event.type, event.code, event.value);
  39. }
  40. else
  41. {
  42. printf("read err %d", len);
  43. }
  44. }
  45. return 0;
  46. }
实验现象:
  • 查询方式(非阻塞):反复查询,输出"read err",直到操作输入设备时,输出内容更改为输入事件内容
  • 休眠-唤醒方式(阻塞):只有操作屏幕,才会输出事件内容

POLL方式读输入事件

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

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <sys/ioctl.h>
  6. #include <linux/input.h>
  7. #include <unistd.h>
  8. #include <string.h>
  9. #include <poll.h>
  10. /* 用法:./get_input_info /dev/input/event0 */
  11. int main(int argc, char const **argv)
  12. {
  13. int fd;
  14. struct input_event event; //read读到的是input_event类型的结构体
  15. struct pollfd pollfd;
  16. nfds_t nfds = 1; //同时打开一个文件
  17. if(argc != 2)
  18. {
  19. printf("Usage: %s <dev>\n", argv[0]);
  20. return -1;
  21. }
  22. fd = open(argv[1], O_RDWR | O_NONBLOCK); //非阻塞(查询)
  23. if(fd == -1)
  24. {
  25. printf("can not open %s\n", argv[1]);
  26. return -1;
  27. }
  28. while(1)
  29. {
  30. pollfd.fd = fd;
  31. pollfd.events = POLLIN;
  32. pollfd.revents = 0; //revents初始化为0,当有输入事件传入,内核改写revents
  33. poll(&pollfd, nfds, 3000); //poll等待时间为3s
  34. if(pollfd.revents == POLLIN) //只有poll函数返回了数据,才调用read
  35. {
  36. while(read(fd, &event, sizeof(event)) == sizeof(event)) //把一次获取到的数据读完再退出
  37. {
  38. printf("type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
  39. }
  40. }
  41. else if(pollfd.revents == 0)
  42. {
  43. printf("time out\n");
  44. }
  45. else
  46. {
  47. printf("read err\n");
  48. }
  49. }
  50. return 0;
  51. }
关于POLL实现多路复用IO
  1. struct pollfd pollfd[n]; //n为文件个数
  2. nfds_t nfds = n; //同时打开n个文件
  3. .......
  4. if(pollfd[0].revents == POLLIN){} //依次访问revents
  5. if(pollfd[1].revents == POLLIN){}
  6. .......

异步通知方式读输入事件

[补充]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).
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <sys/ioctl.h>
  6. #include <linux/input.h>
  7. #include <unistd.h>
  8. #include <string.h>
  9. #include <signal.h>
  10. int fd;
  11. void sig_func(int sig)
  12. {
  13. struct input_event event;
  14. while(read(fd, &event, sizeof(event)) == sizeof(event))
  15. {
  16. printf("type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
  17. }
  18. }
  19. /* 用法:./get_input_info /dev/input/event0 */
  20. int main(int argc, char const **argv)
  21. {
  22. int count = 0;
  23. unsigned short flag;
  24. if(argc != 2)
  25. {
  26. printf("Usage: %s <dev>\n", argv[0]);
  27. return -1;
  28. }
  29. signal(SIGIO, sig_func); //1.注册信号处理函数(信号类型为IO类型)
  30. fd = open(argv[1], O_RDWR | O_NONBLOCK); //2.打开驱动(一定要用非阻塞方式,否则无输入事件进程一直被阻塞)
  31. if(fd == -1)
  32. {
  33. printf("can not open %s\n", argv[1]);
  34. return -1;
  35. }
  36. fcntl(fd ,F_SETOWN, getpid()); //3.告知驱动程序app进程ID
  37. flag = fcntl(fd, F_GETFL); //4.获得文件状态标记
  38. fcntl(fd, F_SETFL, flag | FASYNC); //5.设置文件状态标记(将进程添加到驱动fasync事件等待队列)
  39. while(1)
  40. {
  41. printf("count = %d\n", count++);
  42. sleep(2);
  43. }
  44. return 0;
  45. }
实验结果:
  1. [root@imx6ull:/mnt]# ./get_input_info /dev/input/event1
  2. count = 0
  3. count = 1
  4. count = 2 //无输入事件时正常计数
  5. type = 0x3, code = 0x39, value = 0x6
  6. type = 0x3, code = 0x35, value = 0x1a6
  7. type = 0x3, code = 0x36, value = 0x131
  8. type = 0x3, code = 0x30, value = 0x1f
  9. type = 0x3, code = 0x3a, value = 0x1f
  10. type = 0x1, code = 0x14a, value = 0x1
  11. type = 0x0, code = 0x0, value = 0x0
  12. count = 3
  13. type = 0x3, code = 0x35, value = 0x1a7
  14. type = 0x0, code = 0x0, value = 0x0
  15. count = 4
  16. type = 0x3, code = 0x35, value = 0x1a9
  17. type = 0x0, code = 0x0, value = 0x0
  18. count = 5
  19. type = 0x3, code = 0x35, value = 0x1a8
  20. type = 0x0, code = 0x0, value = 0x0
  21. count = 6

原文链接:https://www.cnblogs.com/Tayoou/p/17157871.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号