XOrg Input Driver Specifications (Incomplete Proposal)
Table of Contents
This page is intended as a place to evolve the design specifications for XInput device drivers in the XOrg server. Most of this document is not based on the existing server code. Instead, this is mostly a proposal of a new and improved design that is more consistent with DIX. Therefore, comments and suggestions from other device driver programmers is important.
The modular driver design introduced in XFree86 4.0 is documented and designed primarily for video display hardware. It is also used for input devices, but the functions needed for video and input devices are not always equivalent, and the design has been independently mis-translated in many input device drivers. As a result, the driver sources inherited from XFree86 are a very inconsistent.
One important consideration left out of the XInput driver design (and the video driver design as well) is propoer sharing of devices with other servers or terminals on the same VT console.
Another feature that has been neglected is the use of the deviceControl() functions intended to be the primary control point for XInput drivers, as documented in the Xi docs and implemented in DIX. It is a well defined, clean design, and should be used. In fact, the same design could be ported to the video drivers as well, to provide a uniform driver design that is (arguably) better than the current design.
Virtual Core Devices
Many issues arise from core devices being different from extension input devices. An excellent solution, implemented in the IRIX server, is to use virtual core devices. In this design, the core keyboard and mouse are virtual devices, and cannot be changed.
Virtual core devices are always available, but produce no independent events. All events come from extension devices. This is an excellent fit to a server that supports multiple core devices and device hot-plugging. With the recent integration of MPX, multiple virtual core devices may exist. They are also referred to as master devices.
The master devices are designed to provide core events in a range that matches the Display resolution. At the same time, they also generate events that are in the device-specific resolution (if applicable). Clients that register for XInput Extension events, will receive events in this native resolution. Clients that open physical devices ("slave devices") directly and register for events do not receive core events. A slave device cannot generate core events.
The server starts up with two master devices, which are always present. This makes it possible for the external hotplug agent to assign all input devices, and provides a clean method for running a server with no input devices. It also provides a chance to clean up all of the core device code, and convert everything to a standard extension device driver.
Any slave device may be "attached" to a master device. In this case, when the slave device generates an event, this event is processed both by the slave device and by the respective master device. This device dependency is referred to as Master-Slave Device Hierarchy, and all slave devices attached to a master device control the master's cursor/keyboard focus. If a slave device is not attached to master device, it is referred to as "floating". It cannot generate core events and clients have to explicitly open the device and register for events. This is discouraged except in special circumstances (i.e. mapping the whole device to the canvas in the GIMP). Internally, floating slave devices control their own cursor sprite but this sprite is not rendered to the screen.
HID Protocol Features
The HID design is a verbose and inflexible, rather than a simple but extensible design. This does not really fit the overall X design concept very well, so it does not make sense to incorporate the actual HID protocol into X. However, it does make sense to use HID within the input drivers. Any HID information sent to the server from drivers should be in the form of Atoms (or strings) rather than HID enums.
One meaningful use for HID is the Device and Control Usage tables. XInput supports a type Atom for Devices, which should be defined to match HID Usage names where possible. These already match names typically used in X:
HID Usage tables would also be useful to define a type Atom for each input control of a device, but the XInput spec needs to be updated in order to support Control types.
Device initialization functions
These come in two different kinds. The functions defined in the driver (the XF86ModuleData and the registered InputDriverRec structures) and the deviceControl(DeviceIntPtr pdev, int action) function. The latter not to be confused with X!ChangeDeviceControl(), it is referred to as deviceProc() in the XInput documentation and is the primary control point to input devices in the sample server DIX code.
The core server design has always included a central deviceControl() function for each input device. Aside from the fact that the currently evolved input driver design is completely mangled, Main.JoeKrahn thinks that the originally documented design is better, and in fact could easly be applied to video devices as well.
Here are the current modular driver functions defined for input drivers:
This is where a driver initializes itself and is part of the module loading process, thus it's per-driver, not per-device. Device instances are initialized later, so this function should never attempt to access any hardware.
The xf86AddInputDriver() function must be called with the InputDriverRec for the driver as one argument to register the driver and making it available to create instances of. The return value must be non-NULL if the driver should successfully be initialized.
Device side: Allocates all the resources for the device the driver needs. Should not access the hardware yet. Must call xf86AllocateInput() to allocate the device-struct that is returned. This is a good place to check/verify parameters and initialize structures for the device. An option with the key "_source" will be in the provided option list if called as a result of a hot-plug request.
Server side: If function returns a pointer to an InputInfoRec but haven't set XI86_CONFIGURED, UnInit() is called if defined. If not, xf86DeleteInput() is called to make some kind of cleanup. For devices that should be hot pluggable the UnInit() function is required if there shouldn't be any resource leaks.
Device side: Additional initialization/resource allocation for the device. Should avoid to access the hardware, but if accessing the device is the only way of getting specific device parameters the device can be opened. The appropriate Init*ClassDeviceStruct() functions should be called here to initialize the input device structure.
Server side: If the function return anything but Success the device can not be used. DEVICE_ON will never be called. It will however be listed as an extension device in X, thus making it visible, but not available.
Device side: Opens the underlying device and should restore the device to a known state, based on the config, or from the state saved at the lst DEVICE_OFF. Should call xf86AddEnabledDevice() to add the FD to the ones monitored by the event loop. It is called when adding an input device or switching to the VT.
Server side: Only if the function return Success the server will call DEVICE_OFF at a VT-switch.
Device side: Called when switching away from the VT. Drivers should save the device state, if applicable, and close the device port. Should call xf86RemoveEnabledDevice() first to stop the event loop from monitoring the FD.
Server side: This function will only be called if the DEVICE_ON returned Success.
Device side: Deallocation of the resources allocated in DEVICE_INIT. This is sent when a device instance is to be removed from the server. Before this is called, any long-term storage of device state information, such as touch-screen calibration, should be retrieved by client code (i.e. a HotPlug agent) and not by the device driver.
Server side: Will be called without prior call to DEVICE_OFF when a remove request is received. Not called if DEVICE_INIT returned anything but Success.
Device side: Deallocates all recources allocated in PreInit(). xf86DeleteInput() should be called to free the device-struct allocated in PreInit().
Server side: If the function is not defined, xf86DeleteInput() will be called instead. But that will most likely result in a memory leak as no device-private structures will be deallocated.
This function is called when the driver is about to be unloaded from memory and should free all resources allocated in ModuleSetupProc(). The argument to this function is the same as returned from ModuleSetupProc(). xf86DeleteInputDriver() must be called to remove the module from the list of available modules.
-- Main.JoeKrahn - 10 Jul 2004
-- Main.MagnusVigerlof - 07 May 2007