The Input Subsystem
href="The Input Subsystem2_file/style.css" rel=stylesheet>
The Input Subsystem
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 clear=all>
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. 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),
struct timeval time of the event;
unsigned short type, the type of the event;
unsigned short code, the code of the event (a sort of
subtype);
unsigned int value, a value number assigend to the event.
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:
input_dev, which represents an input device;
input_handler, which represents an input handler;
input_handle, which represents a connection between an input
device and an input handler.
The input_dev contains several fields. In particular
private, a pointer to private data;
name, a string identifying the device;
idbus, bus identifier;
idvendor, idproduct, and idversion
(in 2.5 these, together with idbus, are packed in an
id structure);
several bitmasks: evbit, keybit, etc.;
a timer;
several method pointers: open, close,
event, upload_effect and erase_effect;
handle, a pointer to an input_handle;
next, a pointer to the next input_dev since the input devices
are linked in a list.
The input_handler data structure contains
the pointer to the private data;
method pointers: event, connect and
disconnect, as well as a pointer to file_operations;
handle, a pointer to an input_handle;
next, for the linked list of input_handler's.
Finally the input_handle contains
the pointer to the private data;
an integer open;
dev, a pointer to an input_dev;
handler, a pointer to an input_handler;
two pointers, dnext and hnext, for the linked
lists of input_handle's attached to a input_dev, and to a input_handler,
respectively.
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.
align=center>
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 ) It 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.
href="http://www.geocities.com/marco_corvi/games/lkpe/input/example1.c">example1.c,
basic register/unregister
href="http://www.geocities.com/marco_corvi/games/lkpe/input/example2.c">example2.c,
key event device
href="http://www.geocities.com/marco_corvi/games/lkpe/input/example3.c">example3.c,
motion event device Try these examples out. Here is the href="http://www.geocities.com/marco_corvi/games/lkpe/input/makefile">makefile.
Now the first exercise. 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, href="http://www.geocities.com/marco_corvi/games/lkpe/input/example4.c">example4.c,
led event device.
Second exercise. Now 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, href="http://www.geocities.com/marco_corvi/games/lkpe/input/leddev.c">leddev.c,
led event handler. And these are two user programs for it, href="http://www.geocities.com/marco_corvi/games/lkpe/input/ledtest.c">ledtest.c,
href="http://www.geocities.com/marco_corvi/games/lkpe/input/ledreset.c">ledreset.c.
Marco Corvi - 2003
language=JavaScript>var PUpage="76001084"; var PUprop="geocities";
src="The Input Subsystem2_file/ygIELib9.js">
var yviContents='http://us.toto.geo.yahoo.com/toto?s=76001084&l=NE&b=1&t=1057747069';yviR='us';yfiEA(0);
geovisit();
width=1 border=0> src="The Input Subsystem2_file/serv.gif" width=1>