Linux /dev/uinput

http://blog.csdn.net/myarrow/article/details/12105973

 

Linux /dev/uinput

分类: Linux Kernel

目录(?)[+]

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




 
 
 
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.


posted @ 2015-01-26 19:39  alxe_yu  阅读(905)  评论(0)    收藏  举报