clinux-kernelraspberry-piioctluinput

IOCTL error while closing an uinput device


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


Solution

  • 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).