Uniform Driver Interface: Difference between revisions

[unchecked revision][unchecked revision]
Content deleted Content added
m Clarified wording
m Bot: Replace deprecated source tag with syntaxhighlight
 
(14 intermediate revisions by 2 users not shown)
Line 30:
* Not at all viable for "casual" projects: requires a significant amount of foreknowledge and prior work.
 
==Core components of UDI Drivers==
==Description==
 
[[Image:Core_spec-8.gif‎|left|frame|alt=Environment|High level view of UDI environments]]
Line 42:
===Modules===
 
A module is essentially a single executable code object. Specifically, drivers can be broken into multiple executables. A large driver that may only need to load certain components and may not need all of its code in memory all the time may be implemented as a multi-module driver. This partitioning of the driver code into modules is up to the driver vendor of course. Most UDI drivers are expected to be single-module drivers, but complex drivers such as graphics card drivers, etc may be best implemented as multi-module drivers. For example, if a graphics driver exports an OpenGL 3D API along with a Direct3D API, it is very likely that both front-ends have a lot of code behind them that would occupy a lot of memory should both be loaded. Most kernels will use ''either'' OpenGL ''or'' Direct3D, so if such a graphics driver was to split its OpenGL and Direct3D implementations into separate modules, this would enable kernels loading that driver to avoid allocating memory for the code and data for the API it isn't using.
 
===Regions===
Line 59:
 
===Metalanguages===
Main article: [[ User:Gravaera/UDI_Metalanguages | Metalanguages ]]
 
Metalanguages define extensions to the core specification for various purposes, and can also be used to define custom IPC protocol APIs between modules/regions. An example of a case where a custom protocol API may be needed is where, for example, a network card driver has a "'''Control'''" region which takes commands from the kernel for power management ("Go to sleep", "prepare to shutdown", etc), and then it has a '''Send'''() region and '''Receive'''() region, which handle its send() and receive() functions respectively.
Metalanguages define the channel operations mentioned in the previous section. They usually have a parent-child relationship, according to the tree representation explained earlier. However, this does not always hold. One good example is the Management Metalanguage. This metalanguage deals with 3 channels:
 
It follows naturally that if the driver receives a "Go to sleep" command from the kernel on its Control region, it would need to send messages to its Send and Receive regions to cause them to cease operation. There is no generic IPC_Send() function defined for IPC across UDI channels -- all IPC must be done according to the protocols APIs defined by a Metalanguage, whether standardized by the UDI spec, or custom-defined. Thankfully, driver writers do not need to define custom protocols for every such case where they want to simply send custom messages between regions: the UDI Core specification defines a "'''Generic I/O Metalanguage'''" IPC protocol API which covers a wide range of generic IPC needs and can be extended with custom messages as desired.
* The parent's management channel
* The child's management channel
* The child's bind channel
 
Apart from APIs/IPC protocols, Metalanguages also cover extensions to the core specification. For example, the already-defined UDI Bus/Bridge Metalanguage can be extended to support new buses as needed; PCI bus drivers, ISA bus drivers, etc do not all need new Metalanguages, because UDI has defined a core UDI Bus/Bridge Metalanguage. This core UDI Bus/Bridge Metalanguage can be extended using Bus/Bridge Metalanguage extensions specific to each bus. This is a case where a Metalanguage is already defined by the UDI Standard, and that metalanguage itself is extended as needed for each bus.
Don't worry if you don't understand the purpose of the management metalanguage yet. Everything will be explained in greater detail later; I was merely trying to point out this special kind of relationship that doesn't only include the parent and the child, it also includes the Management Agent.
 
Entirely new Metalanguages can also be created where necessary; for example, an SCSI Host Bus Adapter is not really a bus, but it is an I/O Microcontroller device that acts as a parent device to SCSI devices (mostly disks). It looks like a bus, but isn't really a bus, and is better handled with an IPC protocol and API of its own. So the UDI specification defines an SCSI Host Bus Adapter Metalanguage API which manages communication (IPC) between SCSI Peripheral Devices (disks) and SCSI Host Bus Adapters. On any given motherboard, a commonly seen arrangement may be as follows in the ASCII art below. The SCSI HBA is not a bus, and the IPC communication between SCSI disks and the SCSI HBA cannot be constrained to follow the same format as communication between a bus and its child devices. This is a case where a new Metalanguage API for communication is a good idea.
===Configuration===
 
As an honourable mention, it would also have been possible to just use the UDI Generic I/O Metalanguage for communication between the SCSI disks and their parent SCSI HBA -- the Generic I/O Metalanguage is equally adequate for that purpose as well.
<syntaxhighlight lang="text">
RootNode
|- PCI-Bus-0
| |- ...
| +- ...
|
|- PCI-Bus-1
| +- SCSI HBA
| |- SCSI-Peripheral-0 (disk)
| +- SCSI-Peripheral-1 (disk)
|
+- PCI-Bus-2
</syntaxhighlight>
 
Metalanguages are essentially UDI IPC Channel protocol definitions or API definitions, and definitions of extensions to the core specification. Hence the name: Meta-''LANGUAGES''.
 
==Driver configuration==
There's a special configuration method for static properties of UDI drivers using a file called udiprops.txt. This file is distributed independently in each driver package for source code distributions and linked into a special section (called .udiprops) for binary distributions.
 
Line 122 ⟶ 139:
Of course, udiprops.txt can be a lot more complex than this, I only wanted you to see what one looks like. You should check the specification for all compile options, statements and configuration options.
 
==DataProgramming objectsModel==
All UDI function calls are asynchronous in nature; this means that they implicitly do not block. A ''compliant'' UDI driver will always be implicitly non-blocking. Whether or not the ''host kernel'' supports non-blocking programming models is up to that kernel, and for any particular kernel, it may be necessary to use locking, mutexes and blocking. Naturally, for a kernel that fully supports a non-blocking, asynchronous model, UDI will simply scale seamlessly.
 
UDI drivers, because of their asynchronous nature, behave like servers to a large extent and they have very good throughput, owing to the fact that the driver itself will only block if the host kernel imposes a limitation on it. For a host kernel which does not have scaling limitations, UDI drivers will innately also scale without limitations -- the throughput of a ''compliant'' UDI driver is dependent solely on the limitations of the host kernel.
There are several types of data we need to look at. First, there's modlue-global data - which resides in the .rodata section. The reason why this data is global is that it's read-only and thus won't cause any race conditions - remember that although only one thread of execution can be active per region, several regions of the same driver instance may run in parallel. Secondly, there's region-local and region-global data. Last but not least, function-local variables.
 
UDI drivers do not implicitly assume the use of locking, blocking, or any specific threading or synchronization model. They fit perfectly into any kind of host environment. As such, the UDI specification does not define any locking operations. It is completely possible for a host kernel to run UDI drivers locklessly.
Region-local data is data private to a region. Being private makes it okay to move the region to a different location or domain without affecting any of the neighbouring regions. Region-global is data that is not particular to a channel or operation.
 
Data objects get allocated via an UDI allocation interface. Let's take a look at the existing types of data objects.
 
===Control blocks===
 
Control blocks are a semi-opaque (i.e., the driver doesn't see the whole data object) data type that are used in metalanguage operations. Once a block is sent via a channel operation it may not be referenced until the channel operation completes. The same is true for asynchronous service calls - the control block will only become available once the callback returns. The way you need to think about this is that a block is owned by only one region at a time and you transfer it from one place to another.
 
There are several types of control blocks, the generic block type being udi_cb_t. All other types of control blocks are supersets of the generic control block. Also, there is the notion of control block groups - control blocks are categorized into groups of control blocks of the same size. Control blocks within the same group can thus be used interchangeably using casts.
 
Each control block may own a scratch space which is driver-specific and must be preserved across asynchronous and service calls. The driver can change the size for its control blocks' scratch spaces and if any of these are zero in size, their pointers must not be dereferenced.
 
<source lang="c">
typedef struct {
udi_channel_t channel;
void *context;
void *scratch;
void *initiator_context;
udi_origin_t origin;
} udi_cb_t;
</source>
 
===Handles===
 
Handles are opaque objects, meaning that the driver does not know their internal representation in the UDI environment. You can implement this as simple (void *)s, internally casting them as pointers into the correct context, or have abstract types.
 
==Initial state==
 
Drivers are relocatable objects files, they have no entry points. UDI drivers have only one global variable, udi_init_info that describes the primary module (and perhaps secondary modules), its primary region (and perhaps the secondary regions that it and the other modules have) and what control blocks it requests. udi_init_info is of type udi_init_t:
 
<source lang="c">
typedef const struct { // Don't forget that global variables are read-only
udi_primary_init_t *primary_init_info;
udi_secondary_init_t *secondary_init_list;
udi_ops_init_t *ops_init_list;
udi_cb_init_t *cb_init_list;
udi_gcb_init_t *gcb_init_list;
udi_cb_select_t *cb_select_list;
} udi_init_info;
</source>
 
==Driver failures==
 
When illegal behavior is detected by the environment, the misbehaving region will usually be region-killed and all neighouring regions will be notified. All channels to that regions will be closed and all resources owned by that region will be freed.
 
==Metalanguages==
 
{{stub}}
 
==See also==
Line 179 ⟶ 154:
* Combuster's effort on creating a [[User:Combuster/UDI_Graphics|graphics metalanguage]]
* Love4Boobies' page for several other [[User:Love4boobies|UDI drafts]]
 
==Existing Implementations==
* [http://projectudi.sf.net/ Reference implementation] - Mostly targeted at linux
* [http://github.com/thepowersgang/acess2 Acess2] - Mostly complete implementation (with network support)
* [http://www.d-rift.nl/combuster/mos3/ MOS3]
 
==External Links==
Line 185 ⟶ 165:
* [http://projectudi.sf.net/ Reference implementation]
* [http://www.ties.org/deven/udi.html Deven Corzine's editorial]
* [http://osr600docuw714doc.scoxinuos.com/en/UDI_dwg/CONTENTSdwg_code_top.html UDI Driver Writer's Guide]