Uniform Driver Interface: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
(Added more realistic configuration file and udi_cb_t definition)
Line 65: Line 65:
Below you can see a sample udiprops.txt:
Below you can see a sample udiprops.txt:


properties_version 0x101
# You will want to state the driver requirements. While "requires udi"
# always needs to be present, the others may be omitted, depending on
# the driver.
message 1 Project UDI
requires udi 0x101 # Unfortunately 0x101 is the only version
message 2 http://www.project-UDI.org/participants.html
requires udi_bridge 0x101 # of the spec that can be implemented
message 3 Pseudo-Driver
message 4 Generic UDI Pseudo-Driver
release 3 1.01
supplier 1
# The ones below should be self-explanatory.
contact 2
name 3
shortname pseudod
##
module example
## Interface dependencies
##
requires udi 0x101
requires udi_gio 0x101
##
## Build instructions.
##
module pseudod
compile_options -DPSEUDO_GIO_META=1
source_files pseudo.c pseudo.h
region 0
region 0
##
# The two statements below are used for building only and may be omitted
## Metalanguage usage
# from binary distributions as they serve no purpose.
##
meta 1 udi_gio # Generic I/O Metalanguage
compile_options -DCMOS_BRIDGE_META=1
source_files udi_example.c
child_bind_ops 1 0 1 # GIO meta, primary region, ops_index 1
meta 1 udi_bridge # Using the Bus Bridge Metalanguage
# Orphan driver; no device line
#
# Initialization, shutdown messages
#
message 1100 pseudod: devmgmt_req %d
message 1500 pseudod: final_cleanup_req


Of course, udiprops.txt can be a lot more complex than this, I only wanted you to see what it looks like. You should check the specification for all compile options, statements and configuration options.
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.


==Data objects==
==Data objects==
Line 102: Line 126:


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.
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>


==Initial state==
==Initial state==

Revision as of 22:18, 9 February 2010

Logo
The official Project UDI logo

UDI stands for "Uniform Driver Interface". It is the specification of a framework and driver API / ABI that enables different operating systems (implementing the UDI framework) to use the same drivers. Conceived by several industry big players, it has fallen somewhat dormant, despite being functional and delivering on its promise.

UDI drivers are binary compatible across all UDI-implementing operating systems running on the same CPU family. They are also source compatible across all UDI-implementing operating systems. This means, a driver only has to be developed once.

While Microsoft Windows gets all the hardware drivers they want, and GNU discourages UDI for philosophical reasons, its advantages for hobbyist OS developers are obvious.

Why UDI?

UDI has several advantages over other existing driver interfaces which motivates developers to choose it above all others:

  • Portability (both cross-OS and cross-platform), which was mentioned in the above section, is perhaps the primary concern for which UDI was developed in the first place. All we can hope for is that enough operating systems will embrace the model so we can actually take advantage of it.
  • Performance is comparable or better than that of legacy drivers. Let's face it, performance is always important. UDI features a non-blocking model, besides the blocking one, a synchronization model for increased MP scalability and much more. UDI drivers have proven themselves over DDI drivers (and others).
  • Compatibility has also been taken into account. UDI environments can be implemented regardless of the OS architecture (monolithic kernel vs. microkernel, POSIX vs. non-POSIX, etc.) with no extra overhead for any exotic design one might think of.
  • Stability is usually overlooked by the design and falls back to the implementation phase. UDI tries to eliminate some categories of potential bugs, such as (but not limited to) resource leaks and deadlocks.
  • Flexibility is another thing UDI has been designed mind with: not only in the way the specification was conceived (i.e., to be extensible), but also in the sense that it permits system programmers to apply techniques such as driver isolation, shadow drivers, etc. if they see fit to do so.

Description

Environment
High level view of UDI environments

The OS layer that deals with UDI drivers is called an UDI environment. The reference implementation (see link below) puts quite a few environments for some of the more popular operating systems at your disposal (Linux, Mach, Darwin, Solaris and FreeBSD) - although some of them might be out of date. This is the layer you want to implement in order to enjoy UDI drivers. One thing environments are liable for is providing service calls. There are two types of service calls recognized by the UDI paradigm: synchronous (which will return immediately to the caller - i.e., to the driver) and asynchronous (which work through a callback mechanism).

Try to imagine the logical topology of an I/O system. It's a tree: you have one central node (perhaps the system board) which has several children (say, buses). Each of these buses may have several controllers attached. Since the tree can be more than 2 layers deep, each node needs to enumerate its children, which in turn will need to enumerate theirs, and so on. UDI drivers for these devices will interact in a tree-like fashion just as the hardware does. Let's take a closer look at drivers themselves!

Drivers are split into one or more modules. Once they run, they do so as driver instances: each device gets one logical instance. The reason I used the word "logical" is that it doesn't actually matter to the driver how the environment implements instances; if there are n SCSI devices of the same type installed on a system, there might only be one copy of the executable code in memory, yet n individual driver states (i.e., variables, open channels, etc.).

Regions

Driver instances are divided into regions, the unit of concurrent execution in UDI. UDI regions location- and instance-independent, meaning that they can be moved from one place to another without affecting any of the other regions because they share no common state. This is particularly interesting in multiprocessor systems (esp. NUMA) because an environment may separate regions due to performance and resource constraints. They are concurrent in the sense that there can only be one thread running in a region at any given time. If there's still code running in region context while an asynchronous service call returns, the callback procedure is put on a queue. This helps avoid all sorts of locking mechanisms and isn't really a performance bottleneck since there can be more than once region per instance and more than once instance per driver running at the same time. Since regions don't share any state it's safe to say that running them in parallel won't cause any race conditions. It's worth mentioning that, because of the separate states, the tasks performed by regions are mutually-exclusive (for instance a network driver might have one region that handles sending packets and another receiving). This is exactly why there is no performance bottleneck.

Channels

The only way for regions to communicate is through channels. Channels are a bi-directional communication mechanism. Each of the two channel endpoints provide an ops vector, which is a set of entry points. The channel operations along with the associated functionality is defined by metalanguages. Metalanguages are separately defined for each class of drivers, but we'll get to that soon.

All channel operation invocations have the following form:

void meta_op(meta_cbtype_cb_t *cb, ...);

Where "meta" is a prefix specific to the metalanguage (e.g., udi or usbdi), "op" is the channel operation and "cbtype" is the control block type (read more on this below). Channel operations take zero or more parameters, depending on which operation we're talking about. The target channel is specified by the value of cb->gcb.channel.

This will result in the environment running an operation in the other region. The convention is for the operation to have the following declaration:

static void ddd_meta_op(meta_cbtype_cb_t *cb, ...);

The same observations as above apply, plus "ddd" being a driver-specific prefix.

Metalanguages

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:

  • The parent's management channel
  • The child's management channel
  • The child's bind channel

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.

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.

The udiprops.txt file doesn't only allow for static configuration options, but is also used in the building process for UDI drivers since they do not use makefiles - not that it would be technically unfeasible. The UDI specification defines tools for building, packaging and installing UDI drivers for simplicity's sake since, unlike POSIX tools, they don't require operating systems to have any extra functionality (e.g., a VFS). Luckly, these tools are available in the reference implementation, all you need to do is build them.

Below you can see a sample udiprops.txt:

  properties_version 0x101
  
  message 1 Project UDI
  message 2 http://www.project-UDI.org/participants.html
  message 3 Pseudo-Driver
  message 4 Generic UDI Pseudo-Driver
  release  3 1.01
  
  supplier	1
  contact	2
  name		3
  shortname	pseudod
  
  ##
  ## Interface dependencies
  ##
  requires udi	 	0x101
  requires udi_gio 	0x101
  
  ##
  ## Build instructions.
  ##
  
  module pseudod
  compile_options -DPSEUDO_GIO_META=1
  source_files pseudo.c pseudo.h
  region 0
  
  ##
  ## Metalanguage usage
  ##
  
  meta 1 udi_gio			# Generic I/O Metalanguage
  
  child_bind_ops 1 0 1		# GIO meta, primary region, ops_index 1
  
  # Orphan driver; no device line
  
  #
  # Initialization, shutdown messages
  #
  message 1100  pseudod: devmgmt_req %d
  message 1500  pseudod: final_cleanup_req

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.

Data objects

There are several types of data we need to look at. First, there's modlue-global data - which resides in the .bss 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.

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.

However, none of these are actually data objects. The difference between data types and data objects is that 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 move 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.

typedef struct {
        udi_channel_t channel;
        void *context;
        void *scratch;
        void *initiator_context;
        udi_origin_t origin;
} udi_cb_t;

Initial state

This page is a stub.
You can help the wiki by accurately adding more contents to it.

Driver failures

This page is a stub.
You can help the wiki by accurately adding more contents to it.

Metalanguages

This page is a stub.
You can help the wiki by accurately adding more contents to it.

See also

External Links