Linux /dev/uinput

转载 2013年12月03日 11:14:37

Linux /dev/uinput

1. 简介

uinput可在以下情况大显身手:

1) 不需要自己写驱动

2) 用户态向/dev/input/eventX写入事件,即用户向Kernel注入数据

2. 使用流程

2.1 打开UInput Device

APP:

  1. char *dev = "/dev/uinput“;
  2. open(dev, O_WRONLY | O_NDELAY);

Kernel:

static int uinput_open(struct inode *inode, struct file *file)
参数inode对应的是 主设备为10,子设备为223的node(即位用户态的dev)
参数file对应打开的文件。
动作:
创建了newdev-- uinput_device结构。
newdev->state = UIST_NEW_DEVICE;
file->private_data = newdev;



2.2 设置UInput Device

APP:

ioctl(fd, UI_SET_EVBIT, EV_KEY);

Kernel:
static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
参数file对应打开的文件。
参数cmd 对应用户态ioctl参数2。UI_SET_EVBIT
参数arg对应用户态ioctl参数3。EV_KEY
动作:
将driver参数传递过来。
udev = file->private_data;
udev->dev 是个input_dev 类型数据。 此时,它未初始化。
如果udev->dev为空,则使用 uinput_allocate_device(udev);申请input_dev结构

具体到CMD=UI_SET_EVBIT
uinput_set_bit(arg, evbit, EV_MAX);
首先判断newdev->state为UIST_CREATED,则返回错误码。
这就说明:设置bit,需要在create input device 之前。
具体动作为:udev->dev->evbit 设为EV_KEY.

注意:此处input device的evbit:
一个是evbit表示设备所支持的动作.:
#define EV_KEY 0x01 // 按键
#define EV_REL 0x02 // 释放

其它设置如下:
ret = ioctl(fd, UI_SET_RELBIT, REL_X); //鼠标
ret = ioctl(fd, UI_SET_RELBIT, REL_Y);
ret = ioctl(fd, UI_SET_EVBIT, EV_ABS);
ret = ioctl(fd, UI_SET_ABSBIT, ABS_X);
ret = ioctl(fd, UI_SET_ABSBIT, ABS_Y);
ret = ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE);
以上设置了Input Device关心或者说会产生的消息。


2.3 写入设备信息

APP:

  1. struct uinput_user_dev uinput;
  2. uinput.id.version = 4;
  3. uinput.id.bustype = BUS_USB;
  4. uinput.absmin[ABS_X] = 0;
  5. uinput.absmax[ABS_X] = 65535; //sam 把屏幕设为0-65535
  6. uinput.absmin[ABS_Y] = 0;
  7. uinput.absmax[ABS_Y] = 65535;
  8. uinput.absmin[ABS_PRESSURE] = 0;
  9. uinput.absmax[ABS_PRESSURE] = 0xfff;
  10. ret = write(fd, &uinput, sizeof(uinput));


Kernel:

Device status为UIST_NEW_DEVICE
并将udev->dev 这个input device 具体化。初始化该input_dev,之后,改变状态:
udev->state = UIST_SETUP_COMPLETE;


2.4 创建Input Device

APP:

注意,此处是创建了Input Device。而不是UInput Device。
ioctl(fd, UI_DEV_CREATE);
Kernel:
input_register_device(udev->dev); //向子系统注册该设备,之后中断时input_event()向子系统报告事件
udev->state = UIST_CREATED;

2.5 向Input Device发送Event

APP:

  1. struct input_event event = {0};
  2. gettimeofday(&event.time, NULL);
  3. event.type = EV_KEY;
  4. event.code = key;
  5. event.value = press ? 1:0;
  6. write(fd, &event, sizeof(event));

Kernel:
static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
因为此时state为UIST_CREATED
input_event(udev->dev, ev.type, ev.code, ev.value); // 发送event.


3. 总结

使用UInput的步骤为:
1. 打开设备。
2. 使用ioctl() 配置设备。
3. 使用write() 将input device信息设置好。
4. 使用ioctl(UI_DEV_CREATE)创建Input Device。(即使用write设置的)
5. 再使用write() 写入event.

4. 实例

  1. /* Globals */
  2. static int uinp_fd = -1;
  3. struct uinput_user_dev uinp;// uInput device structure
  4. struct input_event event; // Input device structure
  5. /* Setup the uinput device */
  6. int setup_uinput_device()
  7. {
  8. // Temporary variable
  9. int i=0;
  10. // Open the input device
  11. uinp_fd = open("/dev/uinput", O_WRONLY | O_NDELAY);
  12. if (uinp_fd == NULL)
  13. {
  14. Dashboard January 2007 Issue
  15. printf("Unable to open /dev/uinput/n");
  16. return -1;
  17. }
  18. memset(&uinp,0,sizeof(uinp));// Intialize the uInput device to NULL
  19. strncpy(uinp.name, "PolyVision Touch Screen", UINPUT_MAX_NAME_SIZE);
  20. uinp.id.version = 4;
  21. uinp.id.bustype = BUS_USB;
  22. // Setup the uinput device
  23. ioctl(uinp_fd, UI_SET_EVBIT, EV_KEY);
  24. ioctl(uinp_fd, UI_SET_EVBIT, EV_REL);
  25. ioctl(uinp_fd, UI_SET_RELBIT, REL_X);
  26. ioctl(uinp_fd, UI_SET_RELBIT, REL_Y);
  27. for (i=0; i < 256; i++) {
  28. ioctl(uinp_fd, UI_SET_KEYBIT, i);
  29. }
  30. ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MOUSE);
  31. ioctl(uinp_fd, UI_SET_KEYBIT, BTN_TOUCH);
  32. ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MOUSE);
  33. ioctl(uinp_fd, UI_SET_KEYBIT, BTN_LEFT);
  34. ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MIDDLE);
  35. ioctl(uinp_fd, UI_SET_KEYBIT, BTN_RIGHT);
  36. ioctl(uinp_fd, UI_SET_KEYBIT, BTN_FORWARD);
  37. ioctl(uinp_fd, UI_SET_KEYBIT, BTN_BACK);
  38. /* Create input device into input sub-system */
  39. write(uinp_fd, &uinp, sizeof(uinp));
  40. if (ioctl(uinp_fd, UI_DEV_CREATE))
  41. {
  42. printf("Unable to create UINPUT device.");
  43. return -1;
  44. }
  45. return 1;
  46. }
  47. void send_click_events( )
  48. {
  49. // Move pointer to (100,100) location
  50. memset(&event, 0, sizeof(event));
  51. gettimeofday(&event.time, NULL);
  52. event.type = EV_REL;
  53. event.code = REL_X;
  54. event.value = 100;
  55. write(uinp_fd, &event, sizeof(event));
  56. event.type = EV_REL;
  57. event.code = REL_Y;
  58. event.value = 100;
  59. write(uinp_fd, &event, sizeof(event));
  60. event.type = EV_SYN;
  61. event.code = SYN_REPORT;
  62. event.value = 0;
  63. write(uinp_fd, &event, sizeof(event));
  64. // Report BUTTON CLICK - PRESS event
  65. memset(&event, 0, sizeof(event));
  66. gettimeofday(&event.time, NULL);
  67. event.type = EV_KEY;
  68. event.code = BTN_LEFT;
  69. event.value = 1;
  70. write(uinp_fd, &event, sizeof(event));
  71. event.type = EV_SYN;
  72. event.code = SYN_REPORT;
  73. event.value = 0;
  74. write(uinp_fd, &event, sizeof(event));
  75. // Report BUTTON CLICK - RELEASE event
  76. memset(&event, 0, sizeof(event));
  77. gettimeofday(&event.time, NULL);
  78. event.type = EV_KEY;
  79. event.code = BTN_LEFT;
  80. event.value = 0;
  81. write(uinp_fd, &event, sizeof(event));
  82. event.type = EV_SYN;
  83. event.code = SYN_REPORT;
  84. event.value = 0;
  85. write(uinp_fd, &event, sizeof(event));
  86. }
  87. void send_a_button()
  88. {
  89. // Report BUTTON CLICK - PRESS event
  90. memset(&event, 0, sizeof(event));
  91. gettimeofday(&event.time, NULL);
  92. event.type = EV_KEY;
  93. event.code = KEY_A;
  94. event.value = 1;
  95. write(uinp_fd, &event, sizeof(event));
  96. event.type = EV_SYN;
  97. event.code = SYN_REPORT;
  98. event.value = 0;
  99. write(uinp_fd, &event, sizeof(event));
  100. // Report BUTTON CLICK - RELEASE event
  101. memset(&event, 0, sizeof(event));
  102. gettimeofday(&event.time, NULL);
  103. event.type = EV_KEY;
  104. event.code = KEY_A;
  105. event.value = 0;
  106. write(uinp_fd, &event, sizeof(event));
  107. event.type = EV_SYN;
  108. event.code = SYN_REPORT;
  109. event.value = 0;
  110. write(uinp_fd, &event, sizeof(event));
  111. }
  112. /* This function will open the uInput device. Please make
  113. sure that you have inserted the uinput.ko into kernel. */
  114. int main()
  115. {
  116. // Return an error if device not found.
  117. if (setup_uinput_device() < 0)
  118. {
  119. printf("Unable to find uinput device/n");
  120. return -1;
  121. }
  122. send_a_button(); // Send a "A" key
  123. send_click_events(); // Send mouse event
  124. /* Destroy the input device */
  125. ioctl(uinp_fd, UI_DEV_DESTROY);
  126. /* Close the UINPUT device */
  127. close(uinp_fd);
  128. }






UInput添加的Input Device在/proc的反应:
#cat /proc/bus/input/device
I: Bus=0003 Vendor=0000 Product=0000 Version=0004
N: Name="uinput"
P: Phys=
S: Sysfs=/class/input/input6
H: Handlers=event1 mouse1
B: EV=f
B: KEY=400 0 670000 ffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
B: REL=3
B: ABS=1000003
解释如下:
Bus=0003 Vendor=0000 Product=0000 Version=0004
这是在第一次write时设置的:
uinp.id.version = 4;
uinp.id.bustype = BUS_USB;
struct input_id {
__u16 bustype;
__u16 vendor;
__u16 product;
__u16 version;
};

EV=f



后记:
后来的工作中,Sam又看到Hi3716C中,如何使用Driver将红外遥控器模拟成一个Keyboard.
http://blog.sina.com.cn/s/blog_602f877001019wtx.html


其实原理非常类似. 都需要指出支持什么Type的Event.
注3:不同类型的Input Event:
#define EV_SYN 0x00 表示设备支持所有的事件
#define EV_KEY 0x01 键盘或者按键,表示一个键码
#define EV_REL 0x02 鼠标设备,表示一个相对的光标位置结果
#define EV_ABS 0x03 手写板产生的值,其是一个绝对整数值
#define EV_MSC 0x04 其他类型
#define EV_LED 0x11 LED灯设备
#define EV_SND 0x12 蜂鸣器,输入声音
#define EV_REP 0x14 允许重复按键类型
#define EV_PWR 0x16 电源管理事件
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)


也要指出每种Type的Event中又分别支持什么具体值.
然后才是创建Device.

相关文章推荐

Linux uinput驱动分析

Linux版本:linux-3.13.3 uinput是Linux提供的一个可以在用户空间创建input设备的驱动程序,init部分代码如下: 877 static int __init uinp...

Linux /dev/uinput

1. 简介     uinput可在以下情况大显身手:     1) 不需要自己写驱动      2) 用户态向/dev/input/eventX写入事件 2. 使用流程 2.1 打开UInput D...
  • MyArrow
  • MyArrow
  • 2013年09月27日 18:01
  • 11232

Linux 使用uinput创建虚拟input设备

参考了这里:http://thiemonge.org/getting-started-with-uinput 代码如下:

Android系统利用uinput设备驱动实现虚拟输入设备

以下是ubuntu下的测试用例: 1.testuinput.c #include #include #include #include #include int main(){ st...

使用UInput模拟系统键盘鼠标动作 UInput driver分析

作者:Sam (甄峰)  sam_code@hotmail.com   在最近的项目中,Sam需要将设备数据解析后以系统键盘鼠标的消息发送出去。选用UInput(关于UInputDriver以及编...
  • tankaro
  • tankaro
  • 2013年06月21日 11:05
  • 2550

【RFB】Linux uinput 分析,虚拟鼠标,键盘

http://blog.csdn.net/tianshuai11/article/details/7630847 Linux 有自己的 input 子系统,可以统一管理鼠标和键盘事件。 基于输入子...

uinput模拟鼠标

#include #include #include #include #include #include #include #include #include void mou...

SenchaArchitect-2.0.0.000442

  • 2012年08月21日 09:07
  • 28.81MB
  • 下载

Linux命令行学习笔记

Linux学习笔记 命令行学习笔记(一) 进入中文虚拟终端: zhcon --utf8 常用命令: ls 显示 ls -a 显示全部 ls -al ...
  • charlv
  • charlv
  • 2011年09月20日 17:34
  • 500
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux /dev/uinput
举报原因:
原因补充:

(最多只允许输入30个字)