需求

读取二维码 DataMatrix 格式的扫码头是 usb 接口,通过键盘输入的形式输出数据。程序需要读取这些数据。

解决

设备信息

获取设备信息

键盘输入在 linux 当中属于 input 子系统,对应的设备号是 /dev/input/eventX, 如果希望知道自己的设备具体对应那个,可以使用下面这条命令来获取所有的输入设备信息。如果希望了解输入的更多内容,可以查看 https://www.kernel.org/doc/Documentation/input/input.txt

cat /proc/bus/input/devices
I: Bus=0019 Vendor=2454 Product=6575 Version=0010
N: Name="mtk-kpd"
P: Phys=
S: Sysfs=/devices/platform/mtk-kpd/input/input0
U: Uniq=
H: Handlers=event0
B: PROP=0
B: EV=3
B: KEY=1c0000 0 0 0

I: Bus=0019 Vendor=0000 Product=0000 Version=0000
N: Name="ACCDET"
P: Phys=
S: Sysfs=/devices/virtual/input/input1
U: Uniq=
H: Handlers=event1
B: PROP=0
B: EV=3
B: KEY=80 0 78 0 40c0000 0 0 0

I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="hwmdata"
P: Phys=
S: Sysfs=/devices/virtual/input/input2
U: Uniq=
H: Handlers=event2
B: PROP=0
B: EV=5
B: REL=2

分析设备信息

根据上面的输出信息,可以根据 Name 这一行大概找到自己的设备,如果希望知道设备支持的详细内容可以查看 EV 值,根据 /usr/include/linux/input-event-codes.h 文件内容, 可以按照哪一位置位来知道设备支持的输入形式。

#define EV_SYN                  0x00
#define EV_KEY                  0x01
#define EV_REL                  0x02
#define EV_ABS                  0x03
#define EV_MSC                  0x04
#define EV_SW                   0x05
#define EV_LED                  0x11
#define EV_SND                  0x12
#define EV_REP                  0x14
#define EV_FF                   0x15
#define EV_PWR                  0x16
#define EV_FF_STATUS            0x17
#define EV_MAX                  0x1f
#define EV_CNT                  (EV_MAX+1)

常用的对应关系:

键盘 鼠标 触摸
EV_KEY EV_REL EV_ABS
按键类型 相对位置 绝对位置

比如说带按键的触摸屏,那么 EV 值就是 (1 << 1) | (1 << 3). 如果需要在 C/C++, 当中来获取这些数据,可以使用如下代码:

    char          name[64];           /* RATS: Use ok, but could be better */
    char          buf[256] = { 0, };  /* RATS: Use ok */
    unsigned char mask[EV_MAX/8 + 1]; /* RATS: Use ok */

#define test_bit(bit) (mask[(bit)/8] & (1 << ((bit)%8)))

    for (i = 0; i < 32; i++) {
      sprintf(name, "/dev/input/event%d", i);
      if ((fd = open(name, O_RDONLY, 0)) >= 0) {
	  ioctl(fd, EVIOCGVERSION, &version);
	  ioctl(fd, EVIOCGNAME(sizeof(buf)), buf);
	  ioctl(fd, EVIOCGBIT(0, sizeof(mask)), mask);
	  printf("%s\n", name);
	  printf("    evdev version: %d.%d.%d\n",
		 version >> 16, (version >> 8) & 0xff, version & 0xff);
	  printf("    name: %s\n", buf);
	  printf("    features:");
	  for (j = 0; j < EV_MAX; j++) {
	      if (test_bit(j)) {
		  const char *type = "unknown";
		  switch(j) {
		  case EV_KEY: type = "keys/buttons"; break;
		  case EV_REL: type = "relative";     break;
		  case EV_ABS: type = "absolute";     break;
		  case EV_MSC: type = "reserved";     break;
		  case EV_LED: type = "leds";         break;
		  case EV_SND: type = "sound";        break;
		  case EV_REP: type = "repeat";       break;
		  case EV_FF:  type = "feedback";     break;
		  }
		  printf(" %s", type);
	      }
	  }
	  printf("\n");
	  close(fd);
      }
    }

读取数据

event 中读取数据,数据是按照 input_event 的格式给出的,定义在 /usr/include/linux/input.h 中。

struct input_event {
#if (__BITS_PER_LONG != 32 || !defined(__USE_TIME_BITS64)) && !defined(__KERNEL__)
      struct timeval time;
#define input_event_sec time.tv_sec
#define input_event_usec time.tv_usec
#else
      __kernel_ulong_t __sec;
#if defined(__sparc__) && defined(__arch64__)
      unsigned int __usec;
      unsigned int __pad;
#else
      __kernel_ulong_t __usec;
#endif
#define input_event_sec  __sec
#define input_event_usec __usec
#endif
      __u16 type;
      __u16 code;
      __s32 value;
};
  • time: 按键时间

  • type: 类型,就是 EV_KEY, EV_REL 之类的。

  • code: 按键的键值,鼠标的键值,鼠标的轨迹类型

    #define KEY_RESERVED            0
    #define KEY_ESC                 1
    #define KEY_1                   2
    #define KEY_2                   3
    #define KEY_3                   4
    #define KEY_4                   5
    #define KEY_5                   6
    #define KEY_6                   7
    #define KEY_7                   8
    #define KEY_8                   9
    #define KEY_9                   10
    #define KEY_0                   11
    #define KEY_MINUS               12
    #define KEY_EQUAL               13
    #define KEY_BACKSPACE           14
    #define KEY_TAB                 15
    #define KEY_Q                   16
    #define KEY_W                   17
    #define KEY_E                   18
    ...
    
    #define BTN_MOUSE               0x110
    #define BTN_LEFT                0x110
    #define BTN_RIGHT               0x111
    #define BTN_MIDDLE              0x112
    #define BTN_SIDE                0x113
    #define BTN_EXTRA               0x114
    #define BTN_FORWARD             0x115
    #define BTN_BACK                0x116
    #define BTN_TASK                0x117
    
    #define BTN_JOYSTICK            0x120
    #define BTN_TRIGGER             0x120
    #define BTN_THUMB               0x121
    #define BTN_THUMB2              0x122
    #define BTN_TOP                 0x123
    #define BTN_TOP2                0x124
    ...
    
    #define REL_X                   0x00
    #define REL_Y                   0x01
    #define REL_Z                   0x02
    #define REL_RX                  0x03
    #define REL_RY                  0x04
    #define REL_RZ                  0x05
    #define REL_HWHEEL              0x06
    #define REL_DIAL                0x07
    #define REL_WHEEL               0x08
    #define REL_MISC                0x09
    
    #define ABS_X                   0x00
    #define ABS_Y                   0x01
    #define ABS_Z                   0x02
    #define ABS_RX                  0x03
    #define ABS_RY                  0x04
    #define ABS_RZ                  0x05
    #define ABS_THROTTLE            0x06
    #define ABS_RUDDER              0x07
    #define ABS_WHEEL               0x08
    #define ABS_GAS                 0x09
    #define ABS_BRAKE               0x0a
    #define ABS_HAT0X               0x10
    #define ABS_HAT0Y               0x11
    #define ABS_HAT1X               0x12
    #define ABS_HAT1Y               0x13
    #define ABS_HAT2X               0x14
    #define ABS_HAT2Y               0x15
    #define ABS_HAT3X               0x16
    #define ABS_HAT3Y               0x17
    #define ABS_PRESSURE            0x18
    #define ABS_DISTANCE            0x19
    #define ABS_TILT_X              0x1a
    
  • value: 事件的值,如果是键盘,那么按下为 1, 松开为 0,如果是鼠标,那么正负表示两个不同方向的值。

打开设备并读取可以使用 while 来读取,深入一点可以使用 select 来读取。

const string kInputDevice = "/dev/input/event11";

int main()
{
    int fd = 0;
    if ((fd = open(kInputDevice.c_str(), O_RDONLY)) < 0) {
      fprintf(stderr, "open error\n");
      return -1;
    }

    struct input_event key;
    while (1) {
      if (read(fd, &key, sizeof(struct input_event)) == sizeof(struct input_event)) {
	  printf("type: %d, code: %d, value: %d\n", key.type, key.code, key.value);
      }
    }

    close(fd);
    return 0;

}

获取的数据类似如下, 我按照按下和抬起,做了初步的分隔。实际的值为: C301001001,

// shift + c, 1
type: 4, code: 4, value: 458981
type: 1, code: 54, value: 1
type: 4, code: 4, value: 458758
type: 1, code: 46, value: 1
type: 0, code: 0, value: 0
// shift + c, 0
type: 4, code: 4, value: 458981
type: 1, code: 54, value: 0
type: 4, code: 4, value: 458758
type: 1, code: 46, value: 0
type: 0, code: 0, value: 0

// 3, 1
type: 4, code: 4, value: 458784
type: 1, code: 4, value: 1
type: 0, code: 0, value: 0
// 3, 0
type: 4, code: 4, value: 458784
type: 1, code: 4, value: 0
type: 0, code: 0, value: 0

// 0, 1
type: 4, code: 4, value: 458791
type: 1, code: 11, value: 1
type: 0, code: 0, value: 0
// 0, 0
type: 4, code: 4, value: 458791
type: 1, code: 11, value: 0
type: 0, code: 0, value: 0

// 1, 1
type: 4, code: 4, value: 458782
type: 1, code: 2, value: 1
type: 0, code: 0, value: 0
// 1, 0
type: 4, code: 4, value: 458782
type: 1, code: 2, value: 0
type: 0, code: 0, value: 0

// 0, 1
type: 4, code: 4, value: 458791
type: 1, code: 11, value: 1
type: 0, code: 0, value: 0
// 0, 0
type: 4, code: 4, value: 458791
type: 1, code: 11, value: 0
type: 0, code: 0, value: 0

// 0, 1
type: 4, code: 4, value: 458791
type: 1, code: 11, value: 1
type: 0, code: 0, value: 0
// 0, 0
type: 4, code: 4, value: 458791
type: 1, code: 11, value: 0
type: 0, code: 0, value: 0

// 1, 1
type: 4, code: 4, value: 458782
type: 1, code: 2, value: 1
type: 0, code: 0, value: 0
// 1, 0
type: 4, code: 4, value: 458782
type: 1, code: 2, value: 0
type: 0, code: 0, value: 0

// 0, 1
type: 4, code: 4, value: 458791
type: 1, code: 11, value: 1
type: 0, code: 0, value: 0
// 0, 0
type: 4, code: 4, value: 458791
type: 1, code: 11, value: 0
type: 0, code: 0, value: 0

// 0, 1
type: 4, code: 4, value: 458791
type: 1, code: 11, value: 1
type: 0, code: 0, value: 0
// 0, 0
type: 4, code: 4, value: 458791
type: 1, code: 11, value: 0
type: 0, code: 0, value: 0

// 1, 1
type: 4, code: 4, value: 458782
type: 1, code: 2, value: 1
type: 0, code: 0, value: 0

// 1, 0
type: 4, code: 4, value: 458782
type: 1, code: 2, value: 0
type: 0, code: 0, value: 0

// , , 1
type: 4, code: 4, value: 458806
type: 1, code: 51, value: 1
type: 0, code: 0, value: 0
// , , 0
type: 4, code: 4, value: 458806
type: 1, code: 51, value: 0
type: 0, code: 0, value: 0

如果读取程序更加完善,可以参考如下的示例代码:

if (argc > 1) {
    sprintf(name, "/dev/input/event%d", atoi(argv[1]));
    if ((fd = open(name, O_RDWR, 0)) >= 0) {
      printf("%s: open, fd = %d\n", name, fd);
      for (i = 0; i < LED_MAX; i++) {
	  event.time.tv_sec  = time(0);
	  event.time.tv_usec = 0;
	  event.type         = EV_LED;
	  event.code         = i;
	  event.value        = 0;
	  write(fd, &event, sizeof(event));
      }

      while ((rc = read(fd, &event, sizeof(event))) > 0) {
	  printf("%-24.24s.%06lu type 0x%04x; code 0x%04x;"
		 " value 0x%08x; ",
		 ctime(&event.time.tv_sec),
		 event.time.tv_usec,
		 event.type, event.code, event.value);
	  switch (event.type) {
	  case EV_KEY:
	      if (event.code > BTN_MISC) {
		  printf("Button %d %s",
			 event.code & 0xff,
			 event.value ? "press" : "release");
	      } else {
		  printf("Key %d (0x%x) %s",
			 event.code & 0xff,
			 event.code & 0xff,
			 event.value ? "press" : "release");
	      }
	      break;
	  case EV_REL:
	      switch (event.code) {
	      case REL_X:      tmp = "X";       break;
	      case REL_Y:      tmp = "Y";       break;
	      case REL_HWHEEL: tmp = "HWHEEL";  break;
	      case REL_DIAL:   tmp = "DIAL";    break;
	      case REL_WHEEL:  tmp = "WHEEL";   break;
	      case REL_MISC:   tmp = "MISC";    break;
	      default:         tmp = "UNKNOWN"; break;
	      }
	      printf("Relative %s %d", tmp, event.value);
	      break;
	  case EV_ABS:
	      switch (event.code) {
	      case ABS_X:        tmp = "X";        break;
	      case ABS_Y:        tmp = "Y";        break;
	      case ABS_Z:        tmp = "Z";        break;
	      case ABS_RX:       tmp = "RX";       break;
	      case ABS_RY:       tmp = "RY";       break;
	      case ABS_RZ:       tmp = "RZ";       break;
	      case ABS_THROTTLE: tmp = "THROTTLE"; break;
	      case ABS_RUDDER:   tmp = "RUDDER";   break;
	      case ABS_WHEEL:    tmp = "WHEEL";    break;
	      case ABS_GAS:      tmp = "GAS";      break;
	      case ABS_BRAKE:    tmp = "BRAKE";    break;
	      case ABS_HAT0X:    tmp = "HAT0X";    break;
	      case ABS_HAT0Y:    tmp = "HAT0Y";    break;
	      case ABS_HAT1X:    tmp = "HAT1X";    break;
	      case ABS_HAT1Y:    tmp = "HAT1Y";    break;
	      case ABS_HAT2X:    tmp = "HAT2X";    break;
	      case ABS_HAT2Y:    tmp = "HAT2Y";    break;
	      case ABS_HAT3X:    tmp = "HAT3X";    break;
	      case ABS_HAT3Y:    tmp = "HAT3Y";    break;
	      case ABS_PRESSURE: tmp = "PRESSURE"; break;
	      case ABS_DISTANCE: tmp = "DISTANCE"; break;
	      case ABS_TILT_X:   tmp = "TILT_X";   break;
	      case ABS_TILT_Y:   tmp = "TILT_Y";   break;
	      case ABS_MISC:     tmp = "MISC";     break;
	      default:           tmp = "UNKNOWN";  break;
	      }
	      printf("Absolute %s %d", tmp, event.value);
	      break;
	  case EV_MSC: printf("Misc"); break;
	  case EV_LED: printf("Led");  break;
	  case EV_SND: printf("Snd");  break;
	  case EV_REP: printf("Rep");  break;
	  case EV_FF:  printf("FF");   break;
	      break;
	  }
	  printf("\n");
      }
      printf("rc = %d, (%s)\n", rc, strerror(errno));
      close(fd);
    }
}

参考

请问怎么知道/dev/input/eventX对应的是什么设备?

Linux Input drivers v1.0

raw_mouse raw_keyboard

keyboard

How can I translate Linux keycodes from /dev/input/event* to ASCII in Perl?

如何读取Linux键值,输入子系统,key,dev/input/event,dev/event,C语言键盘

linux下获取按键响应事件

linux下接扫描枪问题

Linux下键盘键值对应input event下的code值表

再谈谈linux input 子系统事件类型

{笔记分享} {遥控器}Android红外及蓝牙遥控器适配流程

input_event(input_dev, EV_MSC, MSC_SCAN, code)具体意义

Linux下读取USB扫描枪数据

struct input_event详解

Linux input固定设备/dev/input/eventX设备号

USB扫码枪获取数据流的实现方式