Using the Input Subsystem, Part II

No matter how many buttons an input device has or how many kinds of events it can generate, you can now work with it from user space.

In last month's article, we saw how the Linux input subsystem worked inside the kernel, ending with a quick mention of the event handlers. Each handler essentially provides a different user-space API, converting input events into the particular format that makes up that API.

One of the key aspects of the input subsystem integration into Linux is the availability of the event interface. This basically exposes the raw events to userspace through a collection of character device nodes—one character device node per logical input device. The event interface is a really powerful technique, because it allows the manipulation of the events in userspace without information loss. For example, legacy mouse interfaces support only two relative axes and up to five buttons. These are normally mapped to the two real axes and three real buttons, with the fourth and fifth buttons logically being mapped to the scroll wheel up and scroll wheel down events.

However, this mapping becomes a problem when trying to use a mouse with a scroll wheel and more than three buttons, because any additional buttons can be mapped only to an existing button. The legacy APIs also impede use of advanced input devices, such as space balls and other devices' with many axes. By contrast, the event API provides full access to the devices capabilities, and it even includes a per-device description of those capabilities and other device characteristics.

This month's article focuses on the various ioctl capabilities of the event interface, in addition to the normal read and write calls.

Finding the Version of the Event Interface

The event interface supports determining the version of the event device code, using the EVIOCGVERSION ioctl function. The argument is an int (32 bits) and is meant to be interpreted as a major version (two high bytes), a minor version (third byte) and a patch level (low byte). The same value is returned from each event device on a machine.

An example of the EVIOCGVERSION is shown in Listing 1. The first argument to the ioctl function is an open file descriptor for the event device node (for example, /dev/input/event0). Notice that you have to pass a pointer to the integer variable, not the variable itself, as the third argument to the ioctl call.

Listing 1. Sample EVIOCGVERSION Function

Finding Information about the Device's Identity

The event interface supports retrieving information associated with the underlying device using the EVIOCGID ioctl. The argument is a pointer to an input_id structure; the input_id structure is defined as shown in Listing 2. The __u16 data type is a Linux-specific, unsigned 16-bit integer. You can safely cast it to a standard uint16_t in your code.

Listing 2. iput_id Structure Definitions

The bus type is the only field that contains accurate data. You should probably consider it to be an opaque, enumerated type, and compare it with the various BUS_x type definitions provided in <linux/input.h>. The vendor, product and version fields are bus type-specific information relating to the identity of the device. Modern devices (typically using PCI or USB) do have information that can be used, but legacy devices (such as serial mice, PS/2 keyboards and game ports on ISA sound cards) do not. These numbers therefore are not meaningful for some values of bus type.

An example of the EVIOCGID ioctl is shown in Listing 3. This example calls the ioctl and then prints out the results. The case logic shows all current bus types. Here is an example of running that code: vendor 045e product 001d version 0111 is on a Universal Serial Bus.

Listing 3. Sample EVIOCGID ioctl

In addition to the type of bus and the vendor, product and version information, some devices can provide strings that make up meaningful names. This can be obtained from the event interface using the EVIOCGNAME ioctl. This ioctl provides a string and returns the length of the string (or a negative error value). If the string is too long to fit into the argument, it will be truncated. An example is provided in Listing 4. If it seems strange that the argument is not &name, remember the name of an array is the same as a pointer to the first element. Therefore, &name would be a pointer to a pointer to the first element, which is not what we want. If you really want to use a dereference, use &(name[0]).

Listing 4. Example Trunctated String

Here is an example of running that event code:

The device on /dev/input/event0 says its name
    is Logitech USB-PS/2 Optical Mouse

Not all devices contain meaningful names, however, so kernel input drivers try to provide something meaningful. For example, USB devices without manufacturer or product strings concatenate the vendor and product ID information.

Although device identity and name information is often useful, it may not be sufficient information to tell which device you have. For example, if you have two joysticks that are the same, you may need to identify them based on which port they use. This is usually known as topology information, and you can get this from the event interface using the EVIOCGPHYS ioctl. Like EVIOCGNAME, this provides a string and returns the length of the string (or a negative error number). An example is shown in Listing 5; running that example will produce something like:

The device on /dev/input/event0 says its path
    is usb-00:01.2-2.1/input0

Listing 5. Using EVIOCGPHYS for Topology Information

To understand what this string is showing, you need to break it down into parts. The usb part means this is a physical topology from the USB system. The 00:01.2 is the PCI bus information for the USB host controller (in this case, bus 0, slot 1, function 2). The 2.1 shows the path from the root hub to the device. In this case, the upstream hub is plugged in to the second port on the root hub, and that device is plugged in to the first port on the upstream hub. input0 means this is the first event device on the device. Most devices have only one, but multimedia keyboards may present the normal keyboard on one interface and the multimedia function keys on a second interface. This topology example is shown in Figure 1.

Figure 1. Keyboard Topology

This setup doesn't help if you swap the cables on two identical devices. The only thing that can help in this case is if the device has some form of unique identifier, such as a serial number. You can get this information using the EVIOCGUNIQ ioctl. An example is shown in Listing 6. Most devices don't have such an identifier, and you will get an empty string from this ioctl.

Listing 6. Finding a Unique Identifier



Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Full example source.

waeva I dont care's picture

I am making a full source code basic tutorial for copy pasting should be done in a few days I'll post the link. It will also include handling a joystick.
A forward note in the 2.6 version of kernel you need root permissions to open event# files. So you need to run under sudo or make a dev rule.

Best Way to Detect Hotplug from App

E Franz's picture

To Solusrex: I have the same issue. I see at
that the kernel should call /sbin/hotplug which calls device-specific scripts to handle the add or remove event. I'm going to try to instrument the associated script to somehow send a message to my app to notify it. You might try the same.
E Franz

Seriously, how hard is it to give useful code examples?

Bryan Ischo's picture

WHY would you not include the entire code fragment in each example? WHY are you obfuscating some parameter types? Example: you describe getting the unique identifier of a device using the EVIOCGUNIQ ioctl. But you reference a magical variable called 'uniq' the type of which you do not define anywhere. Exactly how is hiding this information supposed to be useful? Who edited this? Why are you trying to make my life more difficult? WTF?!?!

What is the best way to

Solusrex's picture

What is the best way to detect from an app that a USB input device was plugged/unplugged beside polling it using open() function?

Hello, you can write a small

Uwe's picture


you can write a small app that is called from a udev rule.
(You have to add a small rule for this)

Inside of the app you can broadcast a small message on a local socket.

Every app that listens on this socket, receive this notification.


Retrieve Scan Infor

sfog's picture

Hi all...

I need to retrieve all data (position x,y) from the driver when i try to draw something on the pad tablet....

can anyone tell me how to get the ABS when i got event scan ??

bad code

Anonymous's picture

The author clearly does not understand the beauty of copy and paste. Each listing of code just will never compile because the variables are not defined. People have to pull their hair to figure it out.

HELP: Retrieving Input from a Device

Fatih Isler's picture

Hi All,

In one of my project, I need to retrieve events from keyboard in Linux environment. For doing that in my code firstly I opened /dev/input/event0 file and it was successfully opened. However, when I was trying to read something from that file. The read() function stuck and my program can't execute any further steps. Does anybody know why this kind of problem that I encounter and how can I solve this problem. Here is my code:

P.S: In order to execute program you need to be super user and then need to execute code. Otherwise, program will can give "File can not open" error.

#define FILE_PATH "/dev/input/event0"

int main()
	printf("Starting KeyEvent Module\n");

	//int file, version;
	size_t file;
	const char *str = FILE_PATH;

	printf("File Path: %s\n", str);

	if((file = open(str, O_RDWR)) < 0)
		printf("ERROR:File can not open\n");

	struct input_event event[64];

        size_t reader;
        reader = read(file, &event, sizeof(struct input_event) * 64);

        printf("DO NOT COME HERE...\n");

	return 0;

change this : reader =

Anonymous's picture

change this :
reader = read(file, &event, sizeof(struct input_event) * 64);
reader = read(file, event, sizeof(struct input_event) * 64);

You need one more flag

FireHL's picture

change this:

if((file = open(str, O_RDWR)) < 0)


if((file = open(str, O_RDWR|O_NDELAY)) < 0)

and check manual by 'man 2 open'

nice to see, but how many users have figured how to get a joysti

jago25_98's picture

I for one can figure out how /etc/devfsd.conf needs to be edited to setup /dev/joysticks, or /dev/js* with nothing under /dev/input being created either.

linux/Doc*/devices says digital joysticks are char-15,128 while the help doc under makeconfig states char 13:0+ - /dev/input/jsX

neither of this help me much.

If there was documentation somewhere in the world about how to setup joysticks we could hopefully have more people using this code :)