We are testing a C code that creates/closes uinput devices, and have run into a strange issue. We have a basic api for interacting with the uinput library :
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "uinput_api.h"
int uinput_open() {
int uinput_fd;
uinput_fd = open(UINPUT_DEFAULT_PATH, O_WRONLY | O_NONBLOCK);
return uinput_fd;
}
int uinput_close(int fd) {
if (ioctl(fd, UI_DEV_DESTROY, NULL) == -1) {
printf("ioctl failed and returned errno %s \n", strerror(errno));
close(fd);
return -1;
}
return close(fd);
}
int uinput_enable_event(int uinput_fd, uint16_t event_code) {
if (ioctl(uinput_fd, UI_SET_EVBIT, EV_KEY) == -1) {
return -1;
}
return ioctl(uinput_fd, UI_SET_KEYBIT, event_code);
}
int uinput_create_device(int uinput_fd, struct uinput_setup *usetup) {
if (ioctl(uinput_fd, UI_DEV_SETUP, &usetup) == -1) {
return -1;
}
if (ioctl(uinput_fd, UI_DEV_CREATE) == -1) {
return -1;
}
return 0;
}
int uinput_emit_event(int uinput_fd, uint16_t event_type, uint16_t event_code, int32_t eventvalue) {
struct input_event event;
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, 0);
event.type = event_type;
event.code = event_code;
event.value = eventvalue;
ssize_t bytes;
bytes = write(uinput_fd, &event, sizeof(struct input_event));
if (bytes != sizeof(struct input_event)) {
return -1;
}
return 0;
}
int uinput_emit_event_combo(int uinput_fd, const uint16_t *key_codes, size_t length) {
int retval = 0;
size_t i;
for (i = 0; i < length; ++i) {
if (uinput_emit_event(uinput_fd, EV_KEY, key_codes[i], KEY_PRESSED) == -1) {
retval = -1;
break; /* The combination or the device is
somehow broken: there's no sense to
press any of the rest of the
keys. It's like pressing physical keys
one by one and then discovering that
one of the keys required for this
combination is missing or broken. */
}
}
/* Try to release every pressed key, no matter what. */
while (i--) {
if (uinput_emit_event(uinput_fd, EV_KEY, key_codes[i], KEY_RELEASED) == -1) {
retval = -1;
}
}
return retval;
}
int uinput_emit_syn(int uinput_fd) { return uinput_emit_event(uinput_fd, EV_SYN, SYN_REPORT, KEY_RELEASED); }
When we test each of these functions individually, they all work ok.
We then wrote an api that is supposed to be later used as a python module :
#include "event_codes.h"
#include "uinput_api.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
// Externe
// enum for mouvements
enum movements { M_SCROLL_RIGHT, M_SCROLL_LEFT, M_SCROLL_UP, M_SCROLL_DOWN, M_ZOOM_IN, M_ZOOM_OUT };
// init
int init_uinput_device();
// passer le mouvement
int send_movement(int fd, enum movements mouvement_id);
// fermer
int close_uinput_device(int fd);
// interne
int get_movement_value(enum movements mouvement_id);
const int event_tab[] = {SCROLL_UP, SCROLL_DOWN, SCROLL_RIGHT, SCROLL_LEFT, ZOOM_IN, ZOOM_OUT};
int get_movement_value(enum movements mouvement_id) {
switch (mouvement_id) {
case M_SCROLL_RIGHT:
return SCROLL_RIGHT;
case M_SCROLL_LEFT:
return SCROLL_LEFT;
case M_SCROLL_UP:
return SCROLL_UP;
case M_SCROLL_DOWN:
return SCROLL_DOWN;
case M_ZOOM_IN:
return ZOOM_IN;
case M_ZOOM_OUT:
return ZOOM_OUT;
default:
return 0;
}
}
int init_uinput_device() {
int fd = uinput_open();
if (fd == -1) {
printf("init_uinput_device : Error while opening fd\n");
return -1;
}
for (int i = 0; i < NUMBER_OF_EVENTS_HANDLED; ++i) {
int res = uinput_enable_event(fd, event_tab[i]);
if (res == -1) {
printf("init_uinput_device : Error while opening event %d, at position %d\n", event_tab[i], i);
return -1;
}
}
struct uinput_setup usetup;
memset(&usetup, 0, sizeof(usetup));
usetup.id.bustype = BUS_VIRTUAL;
usetup.id.vendor = 0x1234; /* sample vendor */
usetup.id.product = 0x5678; /* sample product */
usetup.id.version = 0x1;
strcpy(usetup.name, "ihm3d_device");
int res = uinput_create_device(fd, &usetup);
if (res == -1) {
printf("init_uinput_device : Error while creating device\n");
return -1;
}
return 0;
}
int send_movement(int fd, enum movements mouvement_id) {
if (uinput_emit_event(fd, EV_KEY, get_movement_value(mouvement_id), KEY_PRESSED) == -1) {
return -1;
}
if (uinput_emit_syn(fd) == -1) {
return -1;
}
if (uinput_emit_event(fd, EV_KEY, get_movement_value(mouvement_id), KEY_RELEASED) == -1) {
return -1;
}
if (uinput_emit_syn(fd) == -1) {
return -1;
}
return 0;
}
int close_uinput_device(int fd) {
if (uinput_close(fd) == -1) {
printf("close_uinput_device : Error while closing fd\n");
return -1;
}
return 0;
}
When we tested the init_uinput_device
and close_uinput_device
functions, we get an error when the ioctl(fd, UI_DEV_DESTROY, NULL)
function is called.
The errno returned is the following :
Inappropriate ioctl for device
Does anyone know where the error might be?
We have tested and the event emission part does not produce an error, the only problem is on the destruction of the device. We have also checked, the device is correctly created in /dev/input
We are developing on a Rapsberry Pi4 using arm-linux-gnueabihf-gcc as a compiler
init_uinput_device()
does not return the opened file descriptor fd
, hence the correct descriptor can hardly be passed to close_uinput_device(int fd)
.