Developer: Maxim Zhuravlev
Enhanced NewBus project aims to implement kernel module piping framework to connect heterogenious devices.
Device is implemented by a stack of drivers. Requests to a device are received by a hyper driver, which pushes the request through the stack. This feature is to simplify code reuse. The hyper driver implies heterogenious devices interconnection support.
IO subsystem implements basic IO requests' processing operations, like creation and delivery.
These devices are not hardware layout driven. These devices are consumers and coordinators of hardware device's data. The most common example is a console device. It's a meta device which coordinates display, keyboard and optionally other devices. The devices are added to NewBus domain as NewBus has a code base for tree support, and it is easy to extend it to generic graph support.
The layout lets to create more modular complex logical device drivers and to abstract work with underlying devices. It also lets to split a device in two devices: first provides just raw data and some instances of 2nd device connected to the first, so that different configurations of a *single* device are supported.
Stack of drivers per device
Each device is implemented by a stack of drivers. This approach simplifies code reuse. There are 5 levels of driver stacking:
lowest -- here raw data receiving driver is to be placed (the allowed number of such drivers is set by now to 1). A device must have at least this driver in stack;
middle -- here non-lowest drivers should be placed by default;
upper -- ex. cashing drivers may be placed here;
topmost -- here virtualizing, muxing, etc. drivers may be placed.
Non-lowest drivers are refered to as filter drivers. These may be some keymap support implementing driver(in case of keyboard), some device virtualizing driver, etc.
As for compatibility with old NewBus drivers, the semantics for those drivers is kept.
Generic device IO subsystem
The subsystem is designed to deliver IO requests (iors) to devices in NewBus graph. By design, the delivery is asynchronous to provide an opportunity to apply some additional processing, such as IO prioritization. A (lowest level) driver may set subsystem to avoid the processing for iors to be directed to a device, controlled by the driver. Iors are being dispatched by a tunable number of working threads.
Each hyper-driver implements two methods:
- IOR_DO ();
- IOR_DONE ().
Depending on the arguments the hyper-driver should call appropriate functions of stacked drivers.
The subsystem provides the following API for drivers:
- ior_create ();
- ior_destroy ();
- ior_pause ();
- ior_resume ();
- ior_complete ();
ior_pause () and ior_resume () are functions to be called for pausing (to be called only within IOR_DO () or IOR_DONE ()) and resuming ior processing by IO subsystem (ex. if a driver can't process the ior immediately). ior_complete (...,status) is the function to be called by a device to abort the further ior processing with a status describing a reason.
From IO subsystem point of view all iors are processed by a device in parallel, i.e. no serialization is provided. All serialization and workload control is to be implemented by a pair <hyper-driver, driver>.
NewBus hosts hardware devices. Hardware layout lets a device(strictly saying, a driver) to name a device or a bus it's being probed on. As for logical devices the approach is't flexible enough. So a console shouldn't really care, whether it has all keyboards connected to it through a demuxing device or a single keyboard connected directly. In some scenario a logical device may require a specific number of devices for ex. monopolization purposes.
Also one would prefer to say *I need a mouse*, rather than *I need a usb mouse, or a ps/2 mouse, or ...*. I.e. logical device requires functional driven intellectual autoconfiguration.
So a device should declare it's requirements (*I need all keyboards, all mice, one display, ...*) and capabilities (*I'm a console device*). Given such well-formalized pairs <requirements, capabilities> one can build a device graph.
Given all this requirements should specify:
- devices layout, supporting graph topology
- device type
- device settings
Ex: [<demux (dmx1); monopolize>, [<keyboard (kbd1); monopolize, keymap=..., ...>]]
[ & ] denotes a subgraph. A node may include more than a single device type.
< & > denotes the device type and appropriate settings.
( & ) specifies a label for the graph node.
Here monopolize means *I need an exclusive access to demuxing device, hosting keyboards, I don't know how to share them safely*. Actually the device doesn't need to know, whether the monopolization is provided by a virtualizing driver or the requested devices are really binded to it. Further more, here a single logical device *keyboard* may be implemented by two devices *keyboard* + *keyboard_raw*. Where *keyboard* device implement *software* settings of keyboard and *keyboard_raw* -- *hardware* settings. This may result in iterative device layout building. This should be a black box for requesting device.
The example requirement defines, that keyboard devices should be attached to demuxing device. The demuxing device than probes and attachs keyboards using its one rules, possibly modified by settings.
The autoconfiguration results may be suboptimal, and root may override them. The (modified) results should be saved in a configuration file. The file should include the requirements with exceptions specified (ex. don't attach ukbd0 to a logical device) rather than current devices layout.
The project aims to create some basic primitives for kernel module piping frameworks.
Devices are notified, when a child or a parent is suspended, resumed, detached, etc.