The Input Subsystem (2.4)

References:
B. Hards, "The Linux USB Input Subsystem, Part I", Linux Journal 106, Feb. 2003, 20-25
B. Hards, "Using the Input Subsystem, Part II", Linux Journal 107, Mar. 2003, 22-30
The source code in the drivers/input subdirectory
The include file include/linux/input.h



The Input subsystem is an abstraction layer between input devices (keyboard, mouse, joystick, and so on) and input handlers. The input devices capture inputs from the user actions or from other sources and produce input events. For instance a key press results in an input event. The input events go through the Input core and are dispatched to the interested handlers, which can make them available to user space through the standard Unix file interface, eg, read.2. The Input core provides a many-to-many mapping between input devices and event handlers.
An input event is a data structure with four field (see the file include/linux/input.h),
There are hundreds of events. They are grouped in few event types. For example "keys" events (EV_KEY), relative motion events (EV_REL), led events (EV_LED). "Keys" event are really many. Relative axes and led events are just a few.
There are three important data structures:
The input_dev contains several fields. In particular
The input_handler data structure contains
Finally the input_handle contains
Input devices, input handlers and input handles are connected in a sparse matrix structure, in which the input handles are the nodes of the matrix. The devices are the entries to the columns of the matrix, and the handlers are the entries to the rows. See the figure below.

The Input Core

  • input_event( dev, type, code, value ) reports events. It returns immediately if the device has not set the bit for the type. The it switches on the event type. Each event is handled only if the corresponding bit of the proper device bitset is set. Key events ... For absolute motion events the code entry of the device abs[] array is modified (with some fuzziness) before handling them. Relative motion events are skipped if the value is 0. LED and sound events toggle the device led and snd, respectively, and call the device event() function if it exists defore handling. Repeat events set the code entry of the device rep[] array to value before handling. After these controls and preliminary things it dispatches the event to the list of handles attaches to the device: traversing the list dnext it calls the event() method for each open handle.
  • input_repeat_key( dev ) invokes the input_event() method with the device dev, a "key" event type, the code of the device repaet_key, and value 2
  • input_open_device( handle ) is called when a handle' device is opened. It invokes the open method of the handle's device and increases the handle open counter.
  • input_close_device( handle ) is called when the handle' device is closed. It invokes the close method of the handle's device and decreases the open counter.
  • input_link_handle( handle ) inserts handle in the first position of both the device list (of the handle's dev) and the handler list (of the handle's handler).
  • input_unlink_handle( handle ) remove the handle from both the device list and the handler list
  • input_register_device( dev ) is called by an input device that wants to register with the input core. It initializes the device repeat timer to default values (in particular the timer function is input_repeat_key). Then it assignes the device number (the first free number). Finally it scans the list of input_handler trying to connect each handler with the device: if it succeeds it inserts the newly created handle. It increases input_number.
  • input_unregister_device( dev ) is called by a device that drops from the input core. It deletes the repeat timer. For each handle in the device list of dev it unlinks the handle and disconnect it from the associated handler. Finally it removes the device and decreases input_number.
  • input_register_handler( handler ) is called by an input handler that wants to register with the input core. It adds the handler to the list input_handler and add it to the input_table. Then it adds a handle for each device that can be connected to the handler.
  • input_unregister_handler( handler ) is called by a handler to unregister from the input core. It disconnects the handler from all the devices to which it is connected. Then it removes the handler from the list input_handler and remove it from the input_table.
  • input_open_file( inode, file ). This is the only file operation of the input core driver. [ STILL TO DO ]

Anatomy of a handler

Sample input handlers are contained in the drivers/input subdirectory of the source tree. They provide the user interfaces. Here we consider the event handler evdev.c. Two data structures are used to store event device and event informations:
  • evdev, which contains, besides other things, an input_handle, a wait_queue_head, and a pointer to an evdev_list;
  • evdev_list, with a buffer of input_event's, a fasync_struct pointer, a pointer to its evdev, and the next evdev_list pointer.
The evdev's are stored in a static evdev_table;
The user interface methods (API) are
  • evdev_fasync( fd, file, on ). It gets the evdev_list stored in the file's private_data, and invokes the the fasync_helper with the evdev_list's fasync function [FIXME],
       fasync_helper(fd, file, on, &list->fasync);
  • evdev_open( inode, file ). The inode's minor specifes which evdev to open. A new evdev_list structure is allocated and linked into the evdev's evdev_list, and the file private_data is made to point to the newly created evdev_list,
       list->evdev = evdev_table[i];
       list->next = evdev_table[i]->list;
       evdev_table[i]->list = list;
       file->private_data = list;
    After this bookkeeping, if the evdev was not already open (and if it exists), the input_core input_open_device is invoked with the evdev's handle.
  • evdev_release( inode, file ). This does the opposite operations of the open method: if this was the last list of the evdev, the evdev is closed with the input_core (and possibly unregistered and deallocated if its exist flag is not set). Finally the evdev_list is freed.
  • evdev_write( file, buffer, count ) allows the user process to write events into the evdev buffer. The user buffer must contain one or more struct input_event. count is the number of bytes used in the user buffer.
  • evdev_read( file, buffer, count, ppos ). Reading is the reverse operation: events are copied from the evdev's buffer to the user buffer. There is one catch. If the evdev's buffer is empty the process is put to sleep.
  • evdev_poll( file, wait ).
  • evdev_ioctl( inode, file, cmd, arg ).
Finally the methods related to the input_core are
  • evdev_event: given an input_handle, retrieves the associated evdev (contained in the private field), and its list of evdev_list's. Then for each evdev_list in the list, the event informations (time, type, code and value) are put on the next free slot of the buffer, and the fasync is signalled [FIXME]. Finally the processes on the evdev's wait_queue are waken up.
  • evdev_connect( handler, dev ). Finds the first unused entry in the evdev_table, and allocates a new evdev for it. Initialize its wait_queue, its handle
       evdev->handle.dev = dev;
       evdev->handle.handler = handler;
       evdev->handle.private = evdev;
    and register with the input_core.
  • evdev_disconnect( handle ). The evdev is contained in the handle's private field. If the evdev is open it is closed and the processes on its wait_queue are waken up. Otherwise it is unregistered, dropped from the evdev_table, and deallocated.
  • evdev_init: register with the input_core;
  • evdev_exit: unregister with the input_core.

Working through the code

First, we start with examples of input devices. These are taken from Hard's article, with minor changes to make them run on 2.4 kernels.
  1. example1.c, basic register/unregister
  2. example2.c, key event device
  3. example3.c, motion event device
Try these examples out. Here is the makefile.

Exercise 1

Write a driver that generates led input events. You can begin with examples 2 or 3 above, and replace the driver event type with EV_LED, and set some ledbit. Give it a try before looking at a solution,
example4.c, led event device.

Exercise 2

Write a handler for the led device of the first exercise. As a hint you may look at the joydev.c code in drivers/input subdirectory of Linux. Here is a solution, leddev.c, leddev.h, led event handler. And these are two user programs for it, ledtest.c, ledreset.c.

Marco Corvi - 2003