Linux devuinput

it2026-08-10  1

1. 简介

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

    1) 不需要自己写驱动

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

2. 使用流程

2.1 打开UInput Device

APP:

 

 

char *dev = "/dev/uinput“;

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: 

 

 

struct uinput_user_dev uinput;

uinput.id.version = 4;

uinput.id.bustype = BUS_USB;

uinput.absmin[ABS_X] = 0;

uinput.absmax[ABS_X] = 65535; //sam 把屏幕设为0-65535

uinput.absmin[ABS_Y] = 0;

uinput.absmax[ABS_Y] = 65535;

uinput.absmin[ABS_PRESSURE] = 0;

uinput.absmax[ABS_PRESSURE] = 0xfff;

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:

 

 

struct input_event event = {0};

gettimeofday(&event.time, NULL);

event.type = EV_KEY;

event.code = key;

event.value = press ? 1:0;

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. 实例

 

/* Globals */

static int uinp_fd = -1;

struct uinput_user_dev uinp; // uInput device structure

struct input_event event; // Input device structure

/* Setup the uinput device */

int setup_uinput_device()

{

// Temporary variable

int i=0;

 

// Open the input device

uinp_fd = open("/dev/uinput", O_WRONLY | O_NDELAY);

if (uinp_fd == NULL)

{

Dashboard January 2007 Issue

printf("Unable to open /dev/uinput/n");

return -1;

}

 

memset(&uinp,0,sizeof(uinp)); // Intialize the uInput device to NULL

strncpy(uinp.name, "PolyVision Touch Screen", UINPUT_MAX_NAME_SIZE);

uinp.id.version = 4;

uinp.id.bustype = BUS_USB;

 

// Setup the uinput device

ioctl(uinp_fd, UI_SET_EVBIT, EV_KEY);

ioctl(uinp_fd, UI_SET_EVBIT, EV_REL);

ioctl(uinp_fd, UI_SET_RELBIT, REL_X);

ioctl(uinp_fd, UI_SET_RELBIT, REL_Y);

for (i=0; i < 256; i++) {

ioctl(uinp_fd, UI_SET_KEYBIT, i);

}

 

ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MOUSE);

ioctl(uinp_fd, UI_SET_KEYBIT, BTN_TOUCH);

ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MOUSE);

ioctl(uinp_fd, UI_SET_KEYBIT, BTN_LEFT);

ioctl(uinp_fd, UI_SET_KEYBIT, BTN_MIDDLE);

ioctl(uinp_fd, UI_SET_KEYBIT, BTN_RIGHT);

ioctl(uinp_fd, UI_SET_KEYBIT, BTN_FORWARD);

ioctl(uinp_fd, UI_SET_KEYBIT, BTN_BACK);

 

/* Create input device into input sub-system */

write(uinp_fd, &uinp, sizeof(uinp));

if (ioctl(uinp_fd, UI_DEV_CREATE))

{

printf("Unable to create UINPUT device.");

return -1;

}

return 1;

}

 

void send_click_events( )

{

// Move pointer to (100,100) location

memset(&event, 0, sizeof(event));

gettimeofday(&event.time, NULL);

event.type = EV_REL;

event.code = REL_X;

event.value = 100;

write(uinp_fd, &event, sizeof(event));

 

event.type = EV_REL;

event.code = REL_Y;

event.value = 100;

write(uinp_fd, &event, sizeof(event));

 

event.type = EV_SYN;

event.code = SYN_REPORT;

event.value = 0;

write(uinp_fd, &event, sizeof(event));

 

// Report BUTTON CLICK - PRESS event

memset(&event, 0, sizeof(event));

gettimeofday(&event.time, NULL);

event.type = EV_KEY;

event.code = BTN_LEFT;

event.value = 1;

write(uinp_fd, &event, sizeof(event));

 

event.type = EV_SYN;

event.code = SYN_REPORT;

event.value = 0;

write(uinp_fd, &event, sizeof(event));

 

// Report BUTTON CLICK - RELEASE event

memset(&event, 0, sizeof(event));

gettimeofday(&event.time, NULL);

event.type = EV_KEY;

event.code = BTN_LEFT;

event.value = 0;

 

write(uinp_fd, &event, sizeof(event));

event.type = EV_SYN;

event.code = SYN_REPORT;

event.value = 0;

write(uinp_fd, &event, sizeof(event));

}

 

void send_a_button()

{

// Report BUTTON CLICK - PRESS event

memset(&event, 0, sizeof(event));

gettimeofday(&event.time, NULL);

event.type = EV_KEY;

event.code = KEY_A;

event.value = 1;

write(uinp_fd, &event, sizeof(event));

 

event.type = EV_SYN;

event.code = SYN_REPORT;

event.value = 0;

write(uinp_fd, &event, sizeof(event));

 

// Report BUTTON CLICK - RELEASE event

memset(&event, 0, sizeof(event));

gettimeofday(&event.time, NULL);

event.type = EV_KEY;

event.code = KEY_A;

event.value = 0;

 

write(uinp_fd, &event, sizeof(event));

event.type = EV_SYN;

event.code = SYN_REPORT;

event.value = 0;

write(uinp_fd, &event, sizeof(event));

}

 

/* This function will open the uInput device. Please make

sure that you have inserted the uinput.ko into kernel. */

int main()

{

// Return an error if device not found.

if (setup_uinput_device() < 0)

{

printf("Unable to find uinput device/n");

return -1;

}

 

send_a_button(); // Send a "A" key

send_click_events(); // Send mouse event

 

/* Destroy the input device */

ioctl(uinp_fd, UI_DEV_DESTROY);

 

/* Close the UINPUT device */

close(uinp_fd);

}

      UInput添加的Input Device在/proc的反应: #cat /proc/bus/input/devices 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.

 

 

 

 

 

 

最新回复(0)