关闭 uinput 设备时出现 IOCTL 错误

IOCTL error while closing an uinput device

我们正在测试 creates/closes 输入设备的 C 代码,并且 运行 出现了一个奇怪的问题。我们有一个基本的 api 用于与 uinput 库交互:


#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); }

当我们单独测试这些功能时,它们都工作正常。

然后我们写了一个 api 应该稍后用作 python 模块 :

#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;
}

当我们测试init_uinput_deviceclose_uinput_device函数时,调用ioctl(fd, UI_DEV_DESTROY, NULL)函数时出现错误。 返回的错误号如下:

Inappropriate ioctl for device

有谁知道错误可能出在哪里?

我们已经测试过,事件发射部分没有产生错误,唯一的问题是在设备的破坏上。我们还检查过,设备已在 /dev/input

中正确创建

我们正在使用 arm-linux-gnueabihf-gcc 作为编译器在 Rapsberry Pi4 上进行开发

init_uinput_device() 没有 return 打开的文件描述符 fd,因此很难将正确的描述符传递给 close_uinput_device(int fd)