The application code is as follows:
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <linux/uinput.h> void emit(int fd, int type, int code, int val) { struct input_event ie; ie.type = type; ie.code = code; ie.value = val; /* timestamp values below are ignored */ ie.time.tv_sec = 0; ie.time.tv_usec = 0; write(fd, &ie, sizeof(ie)); } int main(void) { struct uinput_setup usetup; int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); /* * The ioctls below will enable the device that is about to be * created, to pass key events, in this case the space key. */ ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_KEYBIT, KEY_SPACE); memset(&usetup, 0, sizeof(usetup)); usetup.id.bustype = BUS_USB; usetup.id.vendor = 0x1234; /* sample vendor */ usetup.id.product = 0x5678; /* sample product */ strcpy(usetup.name, "Example device"); ioctl(fd, UI_DEV_SETUP, &usetup); ioctl(fd, UI_DEV_CREATE); /* * On UI_DEV_CREATE the kernel will create the device node for this * device. We are inserting a pause here so that userspace has time * to detect, initialize the new device, and can start listening to * the event, otherwise it will not notice the event we are about * to send. This pause is only needed in our example code! */ sleep(10); /* Key press, report the event, send key release, and report again */ emit(fd, EV_KEY, KEY_SPACE, 1); emit(fd, EV_SYN, SYN_REPORT, 0); emit(fd, EV_KEY, KEY_SPACE, 0); emit(fd, EV_SYN, SYN_REPORT, 0); /* * Give userspace some time to read the events before we destroy the * device with UI_DEV_DESTOY. */ sleep(10); ioctl(fd, UI_DEV_DESTROY); close(fd); return 0; }
Start with open
open
open("/dev/uinput", O_WRONLY | O_NONBLOCK);
Corresponding driver code
static int uinput_open(struct inode *inode, struct file *file) { struct uinput_device *newdev; newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL); if (!newdev) return -ENOMEM; mutex_init(&newdev->mutex); spin_lock_init(&newdev->requests_lock); init_waitqueue_head(&newdev->requests_waitq); init_waitqueue_head(&newdev->waitq); newdev->state = UIST_NEW_DEVICE; file->private_data = newdev; nonseekable_open(inode, file); return 0; }
You can see that it is simply uinput_device applies for space, initializes the lock and work queue, and sets the status to UIST_NEW_DEVICE
ioctl
ioctl(fd, UI_SET_EVBIT, EV_KEY);
The corresponding relationship is as follows
uinput_ioctl() -> uinput_ioctl_handler()
static long uinput_ioctl_handler(struct file *file, unsigned int cmd, unsigned long arg, void __user *p) { int retval; struct uinput_device *udev = file->private_data; struct uinput_ff_upload ff_up; struct uinput_ff_erase ff_erase; struct uinput_request *req; char *phys; const char *name; unsigned int size; retval = mutex_lock_interruptible(&udev->mutex); if (retval) return retval; if (!udev->dev) { udev->dev = input_allocate_device(); if (!udev->dev) { retval = -ENOMEM; goto out; } } ... switch (cmd) { case UI_SET_EVBIT: retval = uinput_set_bit(arg, evbit, EV_MAX); goto out; ... }
During ioctl, it will judge whether udev - > dev is empty. This dev is input_dev, if it is empty, it will apply for allocation of input_dev, which is the same as the input device registration previously analyzed.
uinput_ set_ The prototype of bit is as follows:
#define uinput_set_bit(_arg, _bit, _max) \ ({ \ int __ret = 0; \ if (udev->state == UIST_CREATED) \ __ret = -EINVAL; \ else if ((_arg) > (_max)) \ __ret = -EINVAL; \ else set_bit((_arg), udev->dev->_bit); \ __ret; \ })
You can see that it is to operate the evbit of the input device and set the supported events
For ioctl(fd, UI_SET_KEYBIT, KEY_SPACE);
case UI_SET_KEYBIT: retval = uinput_set_bit(arg, keybit, KEY_MAX); goto out;
In fact, keybit is set to indicate the supported key values, which is no different from the previous analysis
memset(&usetup, 0, sizeof(usetup)); usetup.id.bustype = BUS_USB; usetup.id.vendor = 0x1234; /* sample vendor */ usetup.id.product = 0x5678; /* sample product */ strcpy(usetup.name, "Example device"); ioctl(fd, UI_DEV_SETUP, &usetup);
Corresponding to uinput in ioctl_ dev_ setup()
static int uinput_dev_setup(struct uinput_device *udev, struct uinput_setup __user *arg) { struct uinput_setup setup; struct input_dev *dev; if (udev->state == UIST_CREATED) return -EINVAL; if (copy_from_user(&setup, arg, sizeof(setup))) return -EFAULT; if (!setup.name[0]) return -EINVAL; dev = udev->dev; dev->id = setup.id; udev->ff_effects_max = setup.ff_effects_max; kfree(dev->name); dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL); if (!dev->name) return -ENOMEM; udev->state = UIST_SETUP_COMPLETE; return 0; }
Compare GPIO keys C kernel registration method
In fact, what is operated here is the content of the red box. The status changes to UIST_SETUP_COMPLETE
Keep looking
ioctl(fd, UI_DEV_CREATE);
Corresponding input_create_device
static int uinput_create_device(struct uinput_device *udev) { struct input_dev *dev = udev->dev; int error, nslot; if (udev->state != UIST_SETUP_COMPLETE) { printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); return -EINVAL; } ... dev->event = uinput_dev_event; input_set_drvdata(udev->dev, udev); error = input_register_device(udev->dev); if (error) goto fail2; udev->state = UIST_CREATED; return 0; fail2: input_ff_destroy(dev); fail1: uinput_destroy_device(udev); return error; }
The above code ignores EV_ABS and ev_ For FF related content, here we mainly focus on EV_KEY
Note that dev - > event here is the assigned uinput_dev_event, remember first
Remember to register the input device. As analyzed earlier, you will find the handler, register the event, and register the handle
Finally, the status becomes UIST_CREATED
In the whole process, the status changes to UIST_ NEW_ DEVICE --> UIST_ SETUP_ COMPLETE --> UIST_ CREATED
The above has registered an input device, and the following analysis is to simulate button reporting
emit(fd, EV_KEY, KEY_SPACE, 1);
Corresponding uinput_write
static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { struct uinput_device *udev = file->private_data; int retval; if (count == 0) return 0; retval = mutex_lock_interruptible(&udev->mutex); if (retval) return retval; retval = udev->state == UIST_CREATED ? uinput_inject_events(udev, buffer, count) : uinput_setup_device_legacy(udev, buffer, count); mutex_unlock(&udev->mutex); return retval; }
For UIST status_ When created, call uinput directly_ inject_ Events means injecting events.
When the status is not UIST_ When created, that is to say, the input device will report an event before it is registered, then uinput will be executed_ setup_ device_ Legacy, this function will allocate input device space to register input. This is a method that does not follow the specified process, so we do not analyze it. We mainly analyze the process of entering events after registering input devices normally
static ssize_t uinput_inject_events(struct uinput_device *udev, const char __user *buffer, size_t count) { struct input_event ev; size_t bytes = 0; if (count != 0 && count < input_event_size()) return -EINVAL; while (bytes + input_event_size() <= count) { /* * Note that even if some events were fetched successfully * we are still going to return EFAULT instead of partial * count to let userspace know that it got it's buffers * all wrong. */ if (input_event_from_user(buffer + bytes, &ev)) return -EFAULT; input_event(udev->dev, ev.type, ev.code, ev.value); bytes += input_event_size(); cond_resched(); } return bytes; }
It can be seen that the data in user space is copied through input_event report, then
emit(fd, EV_SYN, SYN_REPORT, 0);
This statement is also executed for the input in the kernel_ Sync is actually input_ Encapsulation of event.
The above completes the analysis of the reporting of simulated key events by uinput.
In general, uinput is easy to understand because it is implemented based on input, but the idea is very ingenious, and there are still a lot of Daniel.