uinput可在以下情况大显身手:
1) 不需要自己写驱动
2) 用户态向/dev/input/eventX写入事件,即用户向Kernel注入数据
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;
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关心或者说会产生的消息。
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;
APP:
注意,此处是创建了Input Device。而不是UInput Device。 ioctl(fd, UI_DEV_CREATE); Kernel: input_register_device(udev->dev); //向子系统注册该设备,之后中断时input_event()向子系统报告事件 udev->state = UIST_CREATED;
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.
使用UInput的步骤为: 1. 打开设备。 2. 使用ioctl() 配置设备。 3. 使用write() 将input device信息设置好。 4. 使用ioctl(UI_DEV_CREATE)创建Input Device。(即使用write设置的) 5. 再使用write() 写入event.
/* 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.
