Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

linux uinput: simple example?

Tags:

linux

uinput

I'm having some problems getting both sides of code using uinput working.

Based on Getting started with uinput: the user level input subsystem[dead link; archived] I put together the following writer (minus error handling):

int main(int ac, char **av)
{
    int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
    int ret = ioctl(fd, UI_SET_EVBIT, EV_ABS);
    ret = ioctl(fd, UI_SET_ABSBIT, ABS_X);

    struct uinput_user_dev uidev = {0};
    snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-rotary");
    uidev.absmin[ABS_X] = 0;
    uidev.absmax[ABS_X] = 255;
    ret = write(fd, &uidev, sizeof(uidev));
    ret = ioctl(fd, UI_DEV_CREATE);

    struct input_event ev = {0};
    ev.type = EV_ABS;
    ev.code = ABS_X;
    ev.value = 42;

    ret = write(fd, &ev, sizeof(ev));

    getchar();

    ret = ioctl(fd, UI_DEV_DESTROY);
    return EXIT_SUCCESS;
}

That seems to work, at least the full input_event structure seems to be written.

I then wrote the most naive reader of events I could come up with:

int main(int ac, char **av)
{
    int fd = open(av[1], O_RDONLY);

    char name[256] = "unknown";
    ioctl(fd, EVIOCGNAME(sizeof(name)), name);
    printf("reading from %s\n", name);

    struct input_event ev = {0};
    int ret = read(fd, &ev, sizeof(ev));
    printf("Read an event! %i\n", ret);
    printf("ev.time.tv_sec: %li\n", ev.time.tv_sec);
    printf("ev.time.tv_usec: %li\n", ev.time.tv_usec);
    printf("ev.type: %hi\n", ev.type);
    printf("ev.code: %hi\n", ev.code);
    printf("ev.value: %li\n", ev.value);

    return EXIT_SUCCESS;
}

Unfortunately the reader side doesn't work at all; only manages to read 8 bytes each time, which isn't nearly a full input_event structure.

What silly mistake am I making?

like image 995
Magnus Avatar asked Nov 01 '14 21:11

Magnus


2 Answers

You should also be writing a sync event after the actual event. In your writer side code:

struct input_event ev = {0};
ev.type = EV_ABS;
ev.code = ABS_X;
ev.value = 42;

usleep(1500);

memset(&ev, 0, sizeof(ev));
ev.type = EV_SYN;
ev.code = 0;
ev.value = 0;

ret = write(fd, &ev, sizeof(ev));

getchar();
like image 84
Komal Padia Avatar answered Oct 19 '22 03:10

Komal Padia


TL;DR: The kernel expects an EV_SYN event of code SYN_REPORT because individual events may be grouped together, i.e., when they happen at the same point in time.


You can think of it as the kernel interpreting a group of events rather than individual events as specified in struct input_event. An EV_SYN event delimits these groups of events with SYN_REPORT as code, i.e., this provides a grouping mechanism for signaling events that occur simultaneously.

For example, imagine you touch with your finger on the surface of a touchpad:

touchpad

Given the definition of struct input_event in linux/input.h:

struct input_event {
/* ... */
__u16 type;   /* e.g., EV_ABS, EV_REL */
__u16 code;   /* e.g., ABS_X for EV_ABS */
__s32 value;  /* e.g., the value of the x coordinate for ABS_X */
};

It isn't possible to create a single EV_ABS event that can hold both the codes ABS_X and ABS_Y, as well as the values for specifying the x and y coordinates, respectively – there is just a single code member in struct input_event1.

Instead, two EV_ABS events2 will be created:

  • One with the code ABS_X and the x coordinate as value.
  • Another with the code ABS_Y and the y coordinate as value.

It wouldn't be entirely correct to interpret these two events as two events separated in time, i.e., one that indicates a change of the x coordinate and another that occurs later in time and indicates a change of the y coordinate.

Instead of two in-time-separated events, these events should be grouped together and interpreted by the kernel as a single unit of input data change that indicates a change of both the x and y coordinates at the same time. This is why the EV_SYN mechanism described above exists: by generating one EV_SYN event with the SYN_REPORT code just after these two EV_ABS events (i.e., ABS_X and ABS_Y), we are able to group them as a unit.


1This is directly related to the fact that a struct input_event is of fixed size and can't grow arbitrarily.

2Tecnically, more events may be created. For example, another event of type EV_KEY with the code BTN_TOUCH will likely be created. However, this is irrelevant for the point I want to make.

like image 42
ネロク・ゴ Avatar answered Oct 19 '22 02:10

ネロク・ゴ