Determining the device capabilities and features

One of the major advantages of HID is that devices are self-describing. You can tell what sort of capabilites and features the device has in a standardised way. This section describes how to determine what the capabilities and features of a particular HID device are.

The types of features that are supported by the event interface are:

Within each feature type, there can be a wide range of features. For example, the EV_REL feature type distinguishes between X, Y and Z axes and horizontal and vertical wheels. Similarly, the EV_KEY feature type distinguishes between literally hundreds of different keys and buttons.

These capabilities (or features, if you prefer) can be determined through the event interface, using the EVIOCGBIT ioctl. This function allows you to determine the types of features that any particular device supports (for example, whether it has keys or buttons, or not). It further allows you to determine the specific features that are supported (for example, which keys or buttons are present).

The EVIOCGBIT ioctl takes four arguments. If we consider it as

ioctl(fd,
EVIOCGBIT(ev_type, max_bytes), bitfield)
, then the fd argument is an open file descriptor, ev_type is the type of features to return (with 0 as a special case, indicating that the list of all features types that are supported should be returned, rather than the list of particular features for that type), max_bytes shows the upper limit on how many bytes should be returned, and bitfield is a pointer to the memory area where the result should be copied. The return value is the number of bytes actually copied (on success) or a negative error code (on failure).

Lets look at a couple of examples of the EVIOCGBIT ioctl call. The first example shows how to determine the types of features that are present.

NoteAbout this example
 

This example is intentionally a code fragment, and is not complete, nor is it meant to show good programming style.

A complete form of this example (that will compile with gcc -Wall -W) is provided in the second part of this document.

Example 4. EVIOCGBIT event list example

/* this macro is used to tell if "bit" is set in "array"
 * it selects a byte from the array, and does a boolean AND 
 * operation with a byte that only has the relevant bit set. 
 * eg. to check for the 12th bit, we do (array[1] & 1<<4)
 */
#define test_bit(bit, array)    (array[bit/8] & (1<<(bit%8)))
…
  uint8_t evtype_bitmask[EV_MAX/8 + 1];

  ioctl(fd, EVIOCGBIT(0, sizeof(evtype_bitmask)), evtype_bitmask) < 0)

  printf("Supported event types:\n");
  for (yalv = 0; yalv < EV_MAX; yalv++) {
      if (test_bit(yalv, evtype_bitmask)) {
	  /* this means that the bit is set in the event types list */
	  printf("  Event type 0x%02x ", yalv);
	  switch ( yalv)
	      {
	      case EV_KEY :
		  printf(" (Keys or Buttons)\n");
		  break;
	      case EV_ABS :
		  printf(" (Absolute Axes)\n");
		  break;
	      case EV_LED :
		  printf(" (LEDs)\n");
		  break;
	      case EV_REP :
		  printf(" (Repeat)\n");
		  break;
	      default:
		  printf(" (Unknown event type: 0x%04hx)\n", yalv);
	      }		 

The result of running the complete form of this code with a USB joystick on my system are:

[root@localhost evdev-example4]# ./example4a /dev/input/event0
Supported event types:
  Event type 0x01  (Keys or Buttons)
  Event type 0x03  (Absolute Axes)

The same code with a USB keyboard shows:

[root@localhost evdev-example4]# ./example4a /dev/input/event1
Supported event types:
  Event type 0x01  (Keys or Buttons)
  Event type 0x11  (LEDs)
  Event type 0x14  (Repeat)

The keyboard happens to have a second interface (for the "multimedia" keys). This second interface shows:

[root@localhost evdev-example4]# ./example4a /dev/input/event2
Supported event types:
  Event type 0x01  (Keys or Buttons)

Note that the previous example of EVIOCGBIT ioctl showed how to determine which function types are supported. Let's consider an example that shows how to determine the particular features that are available within each feature type. The example that follows looks at what LEDs are supported by a particular device within the EV_LED feature type.

NoteAbout this example
 

This example is intentionally a code fragment, and is not complete, nor is it meant to show good programming style.

A complete form of this example (that will compile with gcc -Wall -W) is provided in the second part of this document.

Example 5. EVIOCGBIT LED example

  uint8_t led_bitmask[LED_MAX/8 + 1];
…
  memset(led_bitmask, 0, sizeof(led_bitmask));
  ioctl(fd, EVIOCGBIT(EV_LED, sizeof(led_bitmask)), led_bitmask) < 0)

  printf("Supported LEDs:\n");
  for (yalv = 0; yalv < LED_MAX; yalv++) {
      if (test_bit(yalv, led_bitmask)) {
	  /* this means that the bit is set in the event types list */
	  printf("  LED 0x%02x ", yalv);
	  switch ( yalv)
	      {
	      case LED_NUML :
		  printf(" (Num Lock)\n");
		  break;
	      case LED_CAPSL :
		  printf(" (Caps Lock)\n");
		  break;
	      case LED_SCROLLL :
		  printf(" (Scroll Lock)\n");
		  break;
	      case LED_COMPOSE :
		  printf(" (Compose)\n");
		  break;
	      case LED_KANA :
		  printf(" (Kana)\n");
		  break;
	      case LED_SLEEP :
		  printf(" (Sleep)\n");
		  break;
	      case LED_SUSPEND :
		  printf(" (Suspend)\n");
		  break;
	      case LED_MUTE :
		  printf(" (Mute)\n");
		  break;
	      case LED_MISC :
		  printf(" (Miscellaneous)\n");
		  break;
	      default:
		  printf(" (Unknown LED type: 0x%04hx)\n", yalv);
	      }
        }

Note that the ioctl is basically the same, except that the second argument has changed from 0 to EV_LED, and that the length of the bitfield is now determined by LED_MAX, rather than EV_MAX. Naturally, the meaning of the bits in the bitfield has changed.

Running the complete form of this program on a typical keyboard will produce:

[root@localhost evdev-example4]# ./example4b /dev/input/event1
Supported LEDs:
  LED type 0x00  (Num Lock)
  LED type 0x01  (Caps Lock)
  LED type 0x02  (Scroll Lock)

The LED example can be directly extended to the other function types. Additional examples, showing EV_KEY, EV_REL and EV_ABS are included in the second part of this document, together with complete forms of the examples in this section.

There is one more bit of information that you need for some devices - the range of values that a particular device can report. Obviously a key can only report whether it is up or down, but devices with absolute axes (such as a joystick or tablet) can report over a basically arbitrary range of values.

We can determine the range of a particular absolute axis using the EVIOCGABS ioctl. You have to specify an index, which is the particular axis (for example, ABS_X or ABS_THROTTLE) you want to determine the characteristics of.

This ioctl provides the information back in a input_absinfo structure. This structure is defined as

struct input_absinfo {
        uint32_t min_value;
        uint32_t max_value;
        uint32_t fuzz;
        uint32_t flat;
};

The min_value is the minimum value that this particular axis can return, while the max_value is the maximum value that it can return. The fuzz element is the range of values that can be considered the same (due to mechanical sensor tolerances, or some other reason), and is zero for most devices. The flat is the range of values about the mid-point in the axes that are indicate a zero response (typically, this is the "dead zone" around the null position of a joystick).

Lets look at an example of the EVIOCGABS ioctl.

NoteAbout this example
 

This example is intentionally a code fragment, and is not complete, nor is it meant to show good programming style.

A complete form of this example (that will compile with gcc -Wall -W) is provided in the second part of this document.

Example 6. EVIOCGABS example

  uint8_t abs_bitmask[ABS_MAX/8 + 1];
  struct input_absinfo abs_features;
…
  memset(abs_bitmask, 0, sizeof(abs_bitmask));
  if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) < 0) {
      perror("evdev ioctl");
  }
  for (yalv = 0; yalv < ABS_MAX; yalv++) {
      if (test_bit(yalv, abs_bitmask)) {
	  /* this means that the bit is set in the axes list */
	  if(ioctl(fd, EVIOCGABS(yalv), &abs_features)) {
	      perror("evdev EVIOCGABS ioctl");
	  }
	  printf("(min: %d, max: %d, flatness: %d, fuzz: %d)\n",
		 abs_features.min_value,
		 abs_features.max_value,
		 abs_features.flat,
		 abs_features.fuzz);
      }
  }