The UDI Environment is described, by the definition in the real documentation, as a description and its corresponding implementation of all interfaces provided to UDI-compliant drivers which include the system services, implicit scheduling and synchronization, and inter-module communication infrastructure. The environment is the means by which the Uniform Driver Interface provides a software and hardware independent nature to its drivers. The UDI specs provide a complete environment for the development of compliant driver.
The drivers use the services of the UDI environment by service calls. These are implemented as functions which are exported to all UDI driver instances. This makes them common to all UDI drivers. The environment must provide two types of service calls - asynchronous and synchronous.
UDI environments are generally tied to the OS and are more platform-dependent. They can be developed by looking into the UDI specifications. The UDI environment must implement some core services and utility functions. For source code compatibility, the interface must be compacted into two header files - udi.h and udi_physio.h (see Physical I/O Specifications).
The UDI environment provides utility functions & types.
The fundamental types given by UDI are -
The Uniform Driver Interface declares specific-length types for use in arithmetic and logical operations. They have fixed lengths that don't change between different platforms.
typedef uint8_t udi_ubit8_t; typedef uint16_t udi_ubit16_t; typedef uint32_t udi_ubit32_t; typedef int8_t udi_sbit8_t; typedef int16_t udi_sbit16_t; typedef int32_t udi_sbit32_t; typedef bool udi_bool_t;
The driver must use these types while passing arguments and returning variables. They can also use the fast ints instead of the fixed width ones.
These are integral types whose size is defined by the implementation. By keeping the size of these types abstract, the Uniform Driver Interface is able to maintain source portability between different platforms and also allow drivers to adapt to newer architectures. Specifically, for binary portability, their size is defined by the corresponding ABI specification.
This type is used for referring to the size to any buffer or object. It can also store offsets in buffers and memory itself. It is flexible and can represent different ranges in different domains which means that its size can change between different domains. That is why it is a non-transferable variable across domain boundaries.
It must have at least 16 bits in size, for following guidelines.
typedef <INTEGRAL TYPE> udi_size_t;
This is used by the driver to access indices of object stored in an array. It holds values in the range 0 to 255 (is zero-based) for the driver. As indices to environment object are global for the driver, it is transferable across domain boundaries.
I think is type need not have a 1 byte size as the UDI environment may encrypt the index to hide it from the driver (information presented in this statement is not guaranteed).
typedef <INTEGRAL TYPE> udi_index_t;
i) Control Block Index
The index type can be used to refer to a control that was registered through udi_cb_init_t structure. It can be used for allocating control blocks and getting hold of scratch space sizes and configuring other properties of UDI control blocks.
Note that zero is a invalid value currently, and is reserved for future usage.
ii) Metalanguage Index
The index type can also be used for holding metalanguage indices. It can be used for figuring out the metalanguages used by a driver. These indexes as associated with meta declarations in Static Driver Properties. These are of importance while the UDI Build System compiles and bundles the drivers.
iii) Ops Index
It can also be used to point to a channel operations vector by using the udi_ops_init_t function. It can be used to anchor channels and set default channel types by the driver.
Zero is a reserved value here also, and is booked for future usage.
iv) Region Index
A region-index is used to refer to a driver-defined region and is used for manipulating attributes of regions created by or on behalf of the driver instance.
The zero value refers to the primary region of the driver while other values refer to secondary regions.
These types are not for direct access by drivers. They are essentially handles (like in Windows NT, just relating man) which are passed to service calls to refer to actual objects. They are used to protect system data and prevent malicious code from corrupt valuable data. The size of these handles are specified in the ABI bindings, so these handles have the size of long on IA32/64 architectures.
A rather simple implementation will use just the address of the object in the handle. But for better security, determined projects could use a value that is the result of the operation between the ID given to a UDI driver and a index into a handle table. After undoing the effect of that operation, the environment will get the address of the object by accessing a handle table.
Each opaque handle type will have a corresponding null value. It is used for referring to null objects that don't really exist. This value need not be zero and thus the UDI_HANDLE_IS_NULL macro is provided for testing whether the handle carries a null value. null handles are not to be passed to service calls and will be treated as invalid operands. Also, the driver should never expect to receive a null value from a returning service call.
These handles may or may not be transferable depending on the object being referred to.
i) Channels & udi_channel_tFile -
typedef <HANDLE> udi_channel_t;
#define UDI_NULL_CHANNEL <NULL HANDLE VALUE>
udi_channel_t is used to refer to UDI Channels (described on another page) and are transferrable opaque handles. They are used for bi-directional communication between different drivers and with the environment.
ii) Buffer Path Routing Handle & udi_buf_path_tFile -
typedef <HANDLE> udi_buf_path_t;
#define UDI_NULL_BUF_PATH <NULL HANDLE VALUE>
All UDI buffers are associated with buffer path handles. They indicate destinations for incoming data and are explicitly allocated by the driver. They are not transferable between regions.
iii) Origin Handles & udi_origin_handle_tFile -
typedef <HANDLE> udi_origin_handle_t;
#define UDI_NULL_ORIGIN <NULL HANDLE VALUE>
Environments should use origin handles to hold information about origination of user requests. UDI drivers are responsible for copying the origin handle from received control blocks into control blocks generated on behalf of that control block. This is used by the environment to track quota usage, resource distribution, etc. in the system.
The UDI_NULL_ORIGIN may be set for the origin of any control block instead of copying it from another source, but drivers can't allocate or delete origin handles on their own. They are transferable opaque handles.
Self-contained Opaque Types
These are special opaque-like types that can be interpreted by the environment implementation only, but they are stored locally and have value semantics. Normal opaque handles have reference semantics but here the case is different. Assignment will make a copy of the whole object.
These are not transferable between regions.
i) Timestamp Type - This is used to refer to a point in time and is relative to a starting point that is implementation defined. The driver cannot make use of it on its own and it is for determining relative differences in time. The implementation should use the kernel-timestamp maintained by the kernel for scheduling use. It should be able to do so effectively by mapping the time variable in user-space itself.
typedef <OPAQUE TYPE> udi_timestamp_t;
UDI also levers the advantages of object that have a driver-visible part and implementation-dependent part too. The visible part is a C structure and is kept with a pointer by the driver.
These are allocated by the environment because the driver ain't knowing what the size of the object is.
The Uniform Driver Interface provides a _control block_ type to provide a context for asynchronous service calls and channel operations. These are semi-opaque object and are transferable.
They will be discussed on another page.
UDI also provides a buffer type for storing object and protocol data in dynamic memory on the fly. They need to be logically continuous and any machine or OS differences need to hidden. This should not be hard to implement - all you need is a large & small memory allocator.
We'll have another page on UDI buffer 'management'.
Structures with Fixed-binary representation
Driver usually don't need to be concern with the binary representation of bits or endian-ness of the number while operating in software-defined data structures. But hardware-defined structures have a fixed endian-ness which causes architectural dependencies in binary files if not taken care of. That is why UDI also provides functions in the environment for the drivers to store and manipulate hardware-defined types with specified binary representation. Drivers have to follow certain rules while declaring C hardware-defined structs.
Commonly Used Derived Types
These types are not fundamental and are derived from other UDI types, but are not implementation dependent.
This type is used for providing a uniform method of reporting status or error within the I/O system.File -
typedef udi_ubit32_t udi_status_t;
MaskValues & Flags -
#define UDI_STATUS_CODE_MASK 0x0000FFFF #define UDI_STAT_META_SPECIFIC 0x00008000 #define UDI_SPECIFIC_STATUS_MASK 0x00007FFF #define UDI_CORRELATE_OFFSET 0x16 #define UDI_CORRELATE_MASK 0xFFFF0000
#define UDI_OK 0 // request was successfully completed without any errors #define UDI_STAT_NOT_SUPPORTED 1 // this operation is not supported or given parameters are not handled by the environment #define UDI_STAT_NOT_UNDERSTOOD 2 // parameters given exceed a range or don't make sense in this context, used in channel operations #define UDI_STAT_INVALID_STATE 3 // request understand and supported, but parameters don't make sense here; for channel operations #define UDI_STAT_MISTAKEN_IDENTITY 4 // request is implemented and understood, but not applicable for the device or object concerned #define UDI_STAT_ABORTED 5 // operation was successfully aborted on requesting cancellation #define UDI_STAT_TIMEOUT 6 // operation timeout passed due to which it was aborted #define UDI_STAT_BUSY 7 // the device or object concerned is being used by another process or is busy #define UDI_STAT_RESOURCE_UNAVAIL 8 // the system cannot provide anymore resources for the given request #define UDI_STAT_HW_PROBLEM 9 // system has detected a hardware problem due to which the request could not execute #define UDI_STAT_NOT_RESPONDING 10 // device is not present or not responding #define UDI_STAT_DATA_UNDERRUN 11 // data transfer from device is less than required or expected #define UDI_STAT_DATA_OVERRUN 12 // data transfer from device is more than required or expected #define UDI_STAT_DATA_ERROR 13 // data transfer error occurred from device #define UDI_STAT_PARENT_DRV_ERROR 14 // parent device has faced an error due to this request failing #define UDI_STAT_CANNOT_BIND 15 // driver tried to bind with its parent, but was denied; also used by the parent to indicate denying #define UDI_STAT_CANNOT_BIND_EXCL 16 // driver cannot bind exclusively to the parent as another child is already bound #define UDI_STAT_TOO_MANY_PARENTS 17 // this device cannot bind with the parent as the parent already has maximum supported children #define UDI_STAT_INVALID_PARENT 18 // the parent-binding request cannot execute as meta-language of parent is not supported by driver #define UDI_STAT_TERMINATED 19 // request ended abruptly due to termination of the region without notice #define UDI_STAT_ATTR_MISMATCH 20 // the driver has been given a custom attribute that it cannot apply on its device
UDI Status is a 32-bit type composed of two separate 16-bit values as seen in the two different sets of macros given. They are 'status code' field and a 'correlation field'.
When a error must be signaled then the driver must put an proper status code and assign it to the udi_status_t variable. The correlation field must be 0 for new errors. It is to be set for errors that are derived from other errors or which have meta-language specific semantics.
The STATUS_CODE_MASK is used for filtering out the correlation field and getting out the status field. The UDI_STAT_META_SPECIFIC macro is used for telling that the status code used is not of the common status codes given and is meta-language specific. Those are not defined here, not.