|
|
Fundamental Types
9.1 Overview
This chapter defines the C language type declaration conventions used by UDI. Other language bindings could be created for UDI; some of the syntax would differ, but the principles and the UDI-defined names listed here would be the same. In particular, UDI interfaces may be accessed from assembly language code, as long as the shape of data structures and calling conventions are made to match the C language conventions for the target platform.
For the most part, UDI avoids the use of standard C data types, since the sizes used for these data types are generally not specified by ISO C. Instead, UDI defines a set of specific-length types that are guaranteed to be specific sizes and a set of abstract types that are sized appropriately for a given class of environment implementations. UDI also defines opaque types that are used to refer to objects that may not be directly manipulated by drivers, and semi-opaque types that have visible parts and opaque parts. Header files provided by each environment implementation contain appropriate definitions of each of the above sets of data types.
All UDI interfaces and declarations are based (directly or indirectly) on the UDI-defined fundamental types listed in this chapter. The only standard ISO C types included as UDI fundamental types are char, void, and the varargs types listed in Section 9.2.4.
UDI drivers must use the UDI-defined types for data objects and interfaces specified by UDI. It is also recommended that UDI drivers use UDI-defined types even for driver-internal variables and structures, to avoid platform-dependent size assumptions. It still may be useful, however, to use the int type for a driver-internal variable that needs the most efficient size that isn't particularly large (clearly a very vague definition); if used, it should not be assumed that the size of an int is bigger than 16 bits, but it is reasonable to assume that an int is at least 16 bits since this is guaranteed by the ISO C standard. The ISO C standard also guarantees that the size of a long is at least 32 bits. For maximum portability, only quantities that fit into 32 bits should be stored in long variables.
While recommended for all drivers, drivers distributed as source code are particularly required to avoid non-portable use of ISO C data types, as described above.
UDI drivers may use floating point arithmetic or data types only in very restricted circumstances. The driver must indicate in its region attributes that floating point will be used (see Section 30.6.8, "Region Declaration"). When this attribute is present, the environment will, if possible, load the region into a domain that can support floating-point operations; otherwise, this driver will be rejected. Not all environments support the use of floating point in UDI drivers. Some environments may only support floating point in user-space domains. In all cases, use of floating-point types is limited to code within a region; there are no UDI service calls or channel operations that support floating-point types.
Note - Separate ABI specifications (see Chapter 2, "Document Organization" and "Section 6: MEI Services") define binary bindings for the UDI interfaces, including such things as the sizes of data types, calling conventions, and object file formats. The UDI Core Specification and other non-ABI UDI specifications support the capability of binary portability, but themselves provide source portability.
9.2 Usage of Standard ISO C Data Types and Macros
The following standard ISO C types and macros are used by UDI, and are available to UDI drivers and libraries by including the UDI-defined header file, <udi.h>. UDI drivers and libraries must not include <stddef.h> or other ISO C header files.
9.2.1 ISO C char Type
UDI supports the standard ISO C char type to refer to an 8-bit byte value.
Pointers to char (char *) are used to represent text strings, as in ISO C. Strings are null-terminated and may use Unicode characters encoded as a UTF-8 byte stream. ASCII as a subset of this encoding (that is, characters that are included in the ASCII set are encoded as separate successive 8-bit bytes using the zero-extended 7-bit ASCII encodings, and no combination of characters outside this set result in encodings that include bytes with the high bit clear). All specific string constants specified by UDI shall contain only ASCII characters.
9.2.2 ISO C void Type
UDI supports the standard ISO C void type.
There are two uses in UDI for the void type. The first is as the "return value" of a function that has no value to return, or to indicate a null argument list. This is standard ISO C usage and is very common in UDI.
The other use of the void type is as a pointer (void *) to an unformatted block of memory in the driver's virtual address space. Such pointers, called generic pointers, may be cast to (or from) any other pointer type, but may not be dereferenced directly.
9.2.2.1 Null Pointers
The special symbol, NULL, is an implementation-defined null pointer constant, as defined by ISO C. It is guaranteed to compare equal to zero and unequal to any valid pointer to any statically or dynamically allocated memory object. Some UDI service calls attach special meaning to a pointer value of NULL, as called out in the documentation of those functions. Where not otherwise mentioned, NULL is treated as any other illegal value: it must not be passed to any UDI service call nor should it ever be expected to be returned by UDI services.
9.2.3 ISO C sizeof and offsetof operators
UDI drivers and libraries may use sizeof and offsetof, as defined by ISO C. When used with UDI-conformant data structures, the values resulting from these operators shall be compatible with the UDI-defined udi_size_t type (see Section 9.5, "Abstract Types"). That is, these values can be passed as parameters or assigned to variables of type udi_size_t without loss of information.
9.2.4 Varargs Types
UDI supports the standard ISO C variable argument list types.
The varags types supported in UDI are provided by the <udi.h> include file:
In addition to supporting the above varargs types, UDI environments shall provide the following macros and functions to manipulate these argument list variables:
- va_start is called to initialize a variable of type va_list to the beginning of the variable argument list.
- va_arg will return the next argument in the list pointed to by a va_list variable.
- va_end is used to terminate processing of a variable argument list by a va_list variable.
For additional information on using variable argument lists the ISO C documentation should be consulted; UDI deviates from that document only in the name of the header file used to obtain the type and macro declarations. UDI drivers must not #include <stdarg.h> directly; any definitions needed for varags support will be provided by #including <udi.h>.
Warning - ISO C va_arg has unspecified behavior when used with integral types smaller than int, and many compilers will disallow this. Since UDI data types are defined as fixed sizes (e.g. udi_ubit32_t), a portable UDI driver cannot know whether or not some of these sizes are smaller than sizeof(int). Therefore, instead of using va_arg directly with UDI data types, UDI drivers must use the UDI_VA_ARG macro (defined on page 9-30). The ISO C va_arg may still be used for standard types whose size is equal to or larger than the size of int (notably int and pointers), although for orthogonality the UDI_VA_ARG may be used for those types as well.
9.3 Notation for Implementation-Dependent Types and Constants
Wherever possible, UDI-defined types and interfaces are represented in the text of the specification by their actual declarations, in standard ISO C syntax, as they would appear in UDI header files. In cases where the details are implementation-specific (usually because of platform differences in sizes of integral data types) a placeholder designator is used in place of the missing detail. In actual header files the placeholder would be replaced with the appropriate valid C syntax.
Placeholder designators are shown with angle brackets, and will be one of the following:
"<INTEGRAL>" is used with specific-length types and abstract types, "<OPAQUE>" is used with self-contained opaque types, and "<HANDLE>" is used with handle types, as described below.
Mnemonic constants (C preprocessor macros) are defined using the #define syntax. Mnemonic constants defined in UDI specifications are defined with specific values, with the exception of null handle constants, such as UDI_NULL_CHANNEL. Since the underlying handle type for null handle constants are implementation-dependent, the constant expression used to create a null handle constant is also implementation-dependent. "<NULL_HANDLE>" is used to represent such a constant expression.
9.4 Specific-Length Types
UDI specific-length types are defined to provide basic integer types, both signed and unsigned, which are guaranteed to be of the specified size and the specified range of valid values. These are all integral types, to which arithmetic and logical operations may be applied.
Implementations of the UDI environment will provide typedefs for the following types that will maintain the size and semantic definitions given below:
typedef <INTEGRAL> udi_sbit8_t signed 8-bit: -27..27-1 typedef <INTEGRAL> udi_sbit16_t signed 16-bit: -215..215-1 typedef <INTEGRAL> udi_sbit32_t signed 32-bit: -231..231-1 typedef <INTEGRAL> udi_ubit8_t unsigned 8-bit: 0..28-1 typedef <INTEGRAL> udi_ubit16_t unsigned 16-bit: 0..216-1 typedef <INTEGRAL> udi_ubit32_t unsigned 32-bit: 0..232-1 typedef udi_ubit8_t udi_boolean_t 0=False; 1..28-1=TrueNote - There are by design no 64-bit specific-length types. UDI is designed to work with compilers that do not support 64-bit integral types. In the few rare cases where 64-bit quantities are needed (such as for physical addresses) they are represented either as a pair of 32-bit values or as a self-contained opaque type (see Section 9.6.2 below).
The following constants are defined for use with udi_boolean_t:
#define FALSE 0 #define TRUE 1These are intended for use in assignment statements. It is not safe to compare a boolean value against the constant TRUE since, for example, 57 is also a valid true value and 57 does not equal 1. Boolean variables should instead be tested by direct application of the if statement in ISO C:
if (boolean_variable) /* then true */ if (!boolean_variable) /* then false */This is guaranteed to work since any non-zero value of the tested expression causes if to take the "then" branch.
Similarly, boolean variables can be tested in conditional expressions; e.g.,
x = (boolean_variable || (some_other_expression)) ? a_value : b_value; x = (!boolean_variable && !(some_other_expression)) ? b_value : a_value;without comparing the boolean variable against TRUE or FALSE.
Care must also be taken when assigning values to udi_boolean_t variables. For example, the following assignment statement could cause trouble:
boolean_variable = (flags & FLAG);If the value of FLAG were 0x100 or greater, a true value (FLAG set in flags word) would be truncated in order to fit into the 8-bit boolean_variable. The value would then incorrectly become FALSE. To avoid this problem you must either know that FLAG would never be 0x100 or greater, or use one of the following constructs:
boolean_variable = !(!(flags & FLAG)); boolean_variable = ((flags & FLAG) != 0);9.5 Abstract Types
UDI abstract types are integral types whose size is implementation-dependent. Each environment implementation chooses a size for each of these types that is appropriate for the way in which it can be used on a given platform. By keeping the sizes abstract, UDI can efficiently adapt to the needs of different platforms, and can evolve over time as needs change.
UDI abstract types are all integral types, to which arithmetic and logical operations may be applied.
Note - As ABIs are defined for binary portability, the sizes of abstract types will become part of each ABI definition. All implementations supporting the same ABI will have to use the same sizes. If a size were to change at some point, that effectively produces a new ABI, and all affected modules would require recompilation to use the new ABI.
9.5.1 Size Type
A driver refers to the number of bytes needed in, being read from, or written to, a buffer by using a size type. The size type is also used for buffer offsets, device memory offsets, and memory object sizes (zero offset refers to the first byte position). This type will be used in many places and it may need to vary across different classes of platforms depending on platform needs and constraints. Therefore, UDI refers to size with the following type:
typedef <INTEGRAL> udi_size_t buffer sizeBecause of architectural minimums on some of the defined size limits (e.g., see udi_limits_t) udi_size_t is guaranteed to be at least 16 bits in size. Since the udi_size_t type can represent different ranges of values in different domains, udi_size_t variables are not transferable between regions.
9.5.2 Index Type
A driver refers to a ("relatively small") zero-based index value via the udi_index_t type (i.e., zero corresponds to the first element). The udi_index_t type is guaranteed to be able to hold values from 0 to 255, inclusive; only values in this range shall be used.
typedef <INTEGRAL> udi_index_t zero-based index typeWhen index values are used to refer to environment objects, as in the sub-sections below, the values are global to an entire driver and all of its instances, even if a driver consists of multiple modules. udi_index_t variables are transferable between regions.
9.5.2.1 Control Block Index
A udi_index_t variable may be used to hold a control block index. A control block index is used to identify a control block group registered via a udi_cb_init_t structure (see udi_cb_init_t) so it can be subsequently used to allocate control blocks or select scratch sizes and other control block properties.
Zero is reserved for future use as a special control block index value. It is illegal to use the value zero anywhere a control block index is expected.
9.5.2.2 Metalanguage Index
A udi_index_t variable may be used to hold a metalanguage index. A metalanguage index is used to identify one of possibly several metalanguages used by a driver. Metalanguages are associated with metalanguage indexes value via "meta" declarations in the driver's Static Driver Properties (see Chapter 30). A metalanguage index of zero indicates the Management Metalanguage. Metalanguage index values are used with the tracing and logging services to associate certain types of events with specific metalanguages.
9.5.2.3 Ops Index
A udi_index_t variable may be used to hold an ops index. An ops index is used to identify a channel operations vector registered via a udi_ops_init_t function (see udi_ops_init_t) so it can be subsequently used to anchor channels or set default channel types.
Zero is reserved as a special ops index value that indicates that no ops are specified. This is used to spawn unanchored channels (see udi_channel_spawn), or to terminate a list of structures containing ops index values.
9.5.2.4 Region Index
A udi_index_t variable may be used to hold a region index. A region index is used to identify a driver-defined region type, so that region attributes can be associated with regions created by or on behalf of a driver instance. Region index zero always refers to the primary region of a driver instance. Secondary regions must use non-zero values for region index.
9.6 Opaque Types
UDI defines opaque types for objects whose contents are implementation-specific but whose semantics are strictly specified. Opaque objects are not directly visible to drivers, but are instead managed entirely by the environment. Drivers may only use opaque values by passing them from one environment interface to another.
Opaque types must not have arithmetic or logical operations applied to them and they must not be dereferenced. The only type of operation which may be applied to an opaque type is assignment (which includes argument passing and function return values). It is not even legal to directly compare two opaque values for equality.1
There are two sub-categories of opaque types: handles and self-contained opaque types.
Note - To facilitate binary portability across the same instruction set architecture, UDI environment implementations are likely to use an ABI-specified size for each opaque type, even though that may be larger than needed by some environments. (This refers to the size of the opaque type itself, not the sizes of any objects that might be referenced by opaque handles.)
9.6.1 Opaque Handles
Since opaque objects cannot be accessed directly, most are referenced indirectly via opaque handles. Opaque handles have "reference" semantics like C language pointers, but the actual type used to implement handles is implementation-specific. Only the environment knows how to directly interpret an opaque handle or the object to which it refers.
If a handle is assigned to two different variables and the object is modified (via an environment routine) using one variable, the other variable still refers to the same, modified object. The objects themselves are owned and managed by the environment.
Each handle type has a corresponding "null" value, for which a unique UDI_NULL_XXX mnemonic constant is defined. This null value is different from any values for handles that reference actual objects, and is reserved for special circumstances when it is necessary to indicate "no object." Drivers must not compare handle values for equality, but the UDI_HANDLE_IS_NULL macro can be used to determine if a handle variable currently holds a null value. (Pointers, on the other hand, may be compared against NULL directly.)
A handle whose contents have been zeroed is considered equivalent to the corresponding UDI_NULL_XXX null handle value. The zeroing may be a result of the initial allocation of the handle variable (as initial region data, or by using udi_mem_alloc without the UDI_MEM_NOZERO flag), or may be done explicitly by the driver, using udi_memset.
Service calls that free opaque objects through their handles act as no-ops when passed null handles. Where not otherwise mentioned, null handles are treated as any other illegal value: they must not be passed to any UDI service call nor should they ever be expected to be returned by UDI services.
Some opaque handle types are transferable, others are not. An object of a transferable opaque handle type may be passed from one region to another via a channel operation. Non-transferable opaque objects are local to the region in which they were allocated and may not be passed between regions.
Many UDI objects are manipulated via handles.
NAME udi_channel_t
UDI inter-module communications handle
#include <udi.h>typedef <HANDLE> udi_channel_t; /* NULL channel handle constant */#define UDI_NULL_CHANNEL <NULL_HANDLE>DESCRIPTION UDI Drivers communicate with other drivers and with certain environment modules (e.g. the Management Agent) via bi-directional communication channels established during configuration. Channels are point-to-point and have two ends. The object which keeps track of a particular end of a communication channel between two modules is called the channel object, which is referred to by a channel handle.
Channel handles are transferable between regions if and only if they refer to loose ends. (See "Channels".)
Warnings Drivers must not compare handle values for equality, but the UDI_HANDLE_IS_NULL macro can be used to determine if a handle variable currently holds a null value.
REFERENCES udi_channel_event_ind, udi_channel_anchor, udi_channel_close, UDI_HANDLE_IS_NULL
NAME udi_buf_path_t
Buffer path routing handle
#include <udi.h>typedef <HANDLE> udi_buf_path_t; /* NULL buffer path handle constant */#define UDI_NULL_BUF_PATH <NULL_HANDLE>Description When a driver allocates a UDI buffer, it associates it with a buffer path. Buffer paths indicate intended destinations for data buffers, typically associated with the allocating driver's parent. Drivers refer to buffer paths via opaque buffer path handles.
Path handles are explicitly allocated (via udi_buf_path_alloc), or provided to a driver instance via a UDI_CHANNEL_BOUND channel event indication; the driver indicates how many path handles are needed on a per-parent basis and the environment provides that number of handles each time a parent is bound to the driver.
Buffer path handles are not transferable between regions.
Warnings Drivers must not compare handle values for equality, but the UDI_HANDLE_IS_NULL macro can be used to determine if a handle variable currently holds a null value.
references UDI_HANDLE_IS_NULL, udi_channel_event_ind, udi_buf_copy, udi_buf_write
NAME udi_origin_t
Request origination handle
#include <udi.h>typedef <HANDLE> udi_origin_t; /* NULL origin handle constant */#define UDI_NULL_ORIGIN <NULL_HANDLE>Description Environments may use the origin handle to maintain information about the origination of a user request. Each driver is responsible for copying the origin handle from received control blocks into any control blocks generated on behalf of that received control block. This origin handle may be used by the environment to maintain tracking, quota, or other information for the original request from its point of origin. The origin handle is an opaque handle.
The driver may set the UDI_NULL_ORIGIN value for a control block's origin field instead of copying an origin handle from another control block, but the driver cannot not create or allocate origin handles itself.
Origin handles are transferable between regions.
Warnings Drivers must not compare handle values for equality, but the UDI_HANDLE_IS_NULL macro can be used to determine if a handle variable currently holds a null value.
references UDI_HANDLE_IS_NULL, udi_cb_t
9.6.2 Self-Contained Opaque Types
A self-contained opaque type holds data that can be interpreted only by the environment. Unlike opaque handles, these types have "value" semantics rather than "reference" semantics. That is, assignment makes a copy of the entire object. If a self-contained opaque value is assigned to two different variables and one is modified (via an environment routine), the other will retain the original value.
This means that allocation calls are not needed for self-contained opaque types; drivers simply declare variables of this type and assign values to them.
Self-contained opaque types are not transferable between regions.
9.6.2.1 Timestamp Type
The timestamp type refers to a point in time, relative to an arbitrary starting point, in implementation-specific units. The timestamp type has the following type definition:
typedef <OPAQUE> udi_timestamp_t;As with abstract types, the size of the udi_timestamp_t type is expected to vary according to the needs of different environments.
Detailed usage of this type is described under udi_time_current.
9.7 Semi-Opaque Types
UDI defines semi-opaque types for objects that have driver-visible fields, but also have implementation-specific contents that are not visible to drivers. The driver-visible part of a semi-opaque object is defined as a C structure; drivers refer to the object using a pointer to this structure.
Semi-opaque objects must only be allocated by the environment, since the driver doesn't know how big the whole object is. This is typically done by calling an environment-provided service call such as udi_cb_alloc to allocate the object.
9.7.1 Control Blocks
UDI defines a control block type to provide context for asynchronous service calls and channel operations. UDI control blocks are semi-opaque objects and are transferable between regions.
See Chapter 11, "Control Block Management" for more details on control blocks.
9.7.1.1 Buffers
UDI defines a buffer type, which contains a variable-length collection of application or protocol data. UDI buffer data consists of a byte string that is logically contiguous, but which may be both physically and virtually segmented. In many cases, the actual storage will be of one or more structure types in the embedding system. UDI hides these machine- and OS-dependencies within the buffer object.
UDI buffers are semi-opaque objects and are transferable between regions.
See Chapter 13, "Buffer Management" for more details on UDI buffers.
9.8 Structures Requiring a Fixed Binary Representation
While drivers must specify the structure layout of certain driver-defined structures which are passed between regions (as indicated in the previous subsection), drivers need not concern themselves with the actual binary layout of such structures, or in general with the binary layout of UDI-defined structures or other software-defined structures. However, hardware-defined structures, defined by the device, bus, or hardware protocol, generally require a fixed binary representation. UDI drivers, which are portable across a range of platforms and operating environments, must carefully follow certain rules to create these structures in a manner that will guarantee correct layout in all environments. Such structures are required to be laid out in the appropriate endianness, with fixed alignment of multi-byte fields handled in a platform-independent manner, and that each byte in the structure be accounted for.
Any C structure definitions used to represent hardware structures must be constructed at least according to the following rules:
- Must use only UDI specific-length types on naturally aligned boundaries (offsets) within the structure. Bit-fields in the C language are not portable and must not be used (see the warning below).
- Every byte in the structure must be accounted for.
These rules must be restricted somewhat for protocol-defined structures, as defined in the section on "Endianness Management". Refer to that section for additional details on the construction of hardware-defined structures.
Warning - Bit-fields in the C language are not portable and must never be used in the definition of hardware-defined structures or in interfaces between independent software components. This is because C is ambiguous about the ordering of bits in a bit-field, allowing compiler implementations to order the bits differently even within a given endianness. Therefore, bit-fields cannot be relied upon to reliably specify a placement of bits in a portable manner.
9.9 Common Derived Types
The types listed in this section are not, strictly speaking, fundamental types; they are derived from other UDI-defined types and are not in any way implementation-dependent. However, they are common to many areas of the UDI specification and so are described here.
9.9.1 UDI Status
To provide a uniform means of reporting status or error conditions within the I/O system. When an error has occurred, provide a means of tracing dependent errors to root causes.
NAME udi_status_t
UDI status code
#include <udi.h>typedef udi_ubit32_t udi_status_t; /* Mask Values and Flags for udi_status_t */#define UDI_STATUS_CODE_MASK 0x0000FFFF #define UDI_STAT_META_SPECIFIC 0x00008000 #define UDI_SPECIFIC_STATUS_MASK 0x00007FFF #define UDI_CORRELATE_OFFSET 16 #define UDI_CORRELATE_MASK 0xFFFF0000/* Common Status Values */#define UDI_OK 0 #define UDI_STAT_NOT_SUPPORTED 1 #define UDI_STAT_NOT_UNDERSTOOD 2 #define UDI_STAT_INVALID_STATE 3 #define UDI_STAT_MISTAKEN_IDENTITY 4 #define UDI_STAT_ABORTED 5 #define UDI_STAT_TIMEOUT 6 #define UDI_STAT_BUSY 7 #define UDI_STAT_RESOURCE_UNAVAIL 8 #define UDI_STAT_HW_PROBLEM 9 #define UDI_STAT_NOT_RESPONDING 10 #define UDI_STAT_DATA_UNDERRUN 11 #define UDI_STAT_DATA_OVERRUN 12 #define UDI_STAT_DATA_ERROR 13 #define UDI_STAT_PARENT_DRV_ERROR 14 #define UDI_STAT_CANNOT_BIND 15 #define UDI_STAT_CANNOT_BIND_EXCL 16 #define UDI_STAT_TOO_MANY_PARENTS 17 #define UDI_STAT_BAD_PARENT_TYPE 18 #define UDI_STAT_TERMINATED 19 #define UDI_STAT_ATTR_MISMATCH 20description UDI status values are 32-bit integers that are logically subdivided into a 16-bit status code field, and a 16-bit correlation field. Modules within the UDI environment must report status using this format. ("Modules" in the context of this section refers to drivers and environment services.) A module reports successful completion by setting the status value to UDI_OK.
To separate and distinguish between common status codes and metalanguage-specific status codes the status code, in the low-order 16-bits, is further sub-divided into a 1-bit "metalanguage-specific status flag" (0 = common status, 1 = metalanguage-specific status)-designated by UDI_STAT_META_SPECIFIC, and a 15-bit "specific status code"-designated by UDI_SPECIFIC_STATUS_MASK.
However, drivers do not generally need to be aware of this additional subdivision because the status code values are defined to include the flag bit, and drivers can just assign the UDI-defined status identifier into the udi_status_t (taking into account the correlation field, as described below). Metalanguage designers must make sure that UDI_STAT_META_SPECIFIC is or'ed into each of their metalanguage-specific status mnemonic constants.
When an error must be signalled, the reporting module selects an appropriate status code value (either one of the common ones shown below, or a call-specific or metalanguage-specific code appropriate to the context in which the error is encountered) and assigns it into a udi_status_t parameter. This status value shall contain zero in the correlation field in order to indicate that this is a new error, rather than a derivative error. The udi_status_t value is used in a call to udi_log_write (see page 17-7), which will record all the data pertinent to the error in a logging file and assign a correlation value to the error. This correlation value will be placed in the 16 most-significant bits of the udi_status_t on return. This combined value will be passed by the driver to other entities that are affected by the error. When this error in turn results in a derivative error worth logging (e.g., lost link connection results in a file access error) the next reporting module will replace the 16 least-significant bits of the udi_status_t with a new appropriate value but will maintain the correlation field contents. When called with a derivative status, udi_log_write will record that same correlation value, together with all the data pertinent to the new error. In this way, the individual entries in the log file can be threaded together by the correlation value to trace back to the original error for the root cause.
To check for a specific status code value, the driver writer can mask off the correlation field and compare the remaining value:
if ((status & UDI_STATUS_CODE_MASK) == UDI_STAT_XXX) handle_error();9.9.1.1 Common Status Codes
The UDI environment defines several status codes for use in reporting various common problems and conditions within UDI drivers and metalanguages. It is important to note that any driver internal errors will have indeterminate results but very likely will result in the driver instance being killed without ever being re-entered, therefore there are no corresponding error codes defined for related conditions (e.g. invalid argument errors). For related information see Section 4.10.
UDI status error codes are defined with mnemonic constants as shown in the following table.
The status codes here may be supplemented by various Physical I/O status codes or metalanguage-specific status codes as defined in the corresponding UDI specification books.
9.9.2 Data Layout Specifier
The data layout specifier type is used to describe the layout of control blocks and other driver data structures that may be transferred between regions by using channel operations. Data layout specifiers are primarily used by metalanguage libraries to describe the layout of all fixed structures passed via channel operations. Drivers may in some cases need to declare layout specifiers themselves, to use with udi_cb_init_t or udi_cb_alloc_dynamic; this allows a driver to register the layout of inline memory structures that aren't strictly typed by the metalanguage.
NAME udi_layout_t
Data layout specifier
#include <udi.h>typedef const udi_ubit8_t udi_layout_t; /* Specific-Length Layout Type Codes */#define UDI_DL_UBIT8_T 1 #define UDI_DL_SBIT8_T 2 #define UDI_DL_UBIT16_T 3 #define UDI_DL_SBIT16_T 4 #define UDI_DL_UBIT32_T 5 #define UDI_DL_SBIT32_T 6 #define UDI_DL_BOOLEAN_T 7 #define UDI_DL_STATUS_T 8/* Abstract Element Layout Type Codes */#define UDI_DL_INDEX_T 20/* Opaque Handle Element Layout Type Codes */#define UDI_DL_CHANNEL_T 30 #define UDI_DL_ORIGIN_T 32/* Indirect Element Layout Type Codes */#define UDI_DL_BUF 40 #define UDI_DL_CB 41 #define UDI_DL_INLINE_UNTYPED 42 #define UDI_DL_INLINE_DRIVER_TYPED 43 #define UDI_DL_MOVABLE_UNTYPED 44/* Nested Element Layout Type Codes */#define UDI_DL_INLINE_TYPED 50 #define UDI_DL_MOVABLE_TYPED 51 #define UDI_DL_ARRAY 52 #define UDI_DL_END 0description A data layout specifier consists of an array of one or more udi_layout_t layout elements. Each element contains a type code indicating one of the UDI data types that can be passed into a channel operation, either as a field in the control block or as an additional parameter. Each successive element of the array represents successive offsets within the described structure, with padding automatically inserted for alignment purposes as if the specified data types had appeared in a C struct declaration.
Since channel operations are based on strongly typed function calls, the environment usually has sufficient information to handle data transformations such as endian conversions when channel operations cross between domains of differing data formats. However, there are some cases where one or more parameters to a channel operation call are not strongly typed, but are simply void * pointers to chunks of memory. Such pointers must point either to movable memory (allocated by udi_mem_alloc with the UDI_MEM_MOVABLE flag set) or to inline memory permanently associated with a control block when it was allocated.
If such untyped memory is also unstructured-that is, it is to be treated as an array of bytes-then no data transformations need be performed. If the memory is structured, however, drivers must inform the environment of that structure, since it cannot be determined a priori from the channel operation definition. In that case, the driver supplies a data layout specifier as a parameter to the "cb_init" function with which the operation is associated.
Layout element type values for udi_layout_t are defined with mnemonic constants as shown in the following table:
Table 9-4 Abstract Element Type Codes Element Type Code Value Corresponding Data Type UDI_DL_INDEX_T udi_index_t For all nested element types, the layout elements following the nested type, up until the matching UDI_DL_END, describe the structure of that element. For driver-type inline structures, indicated by UDI_DL_INLINE_DRIVER_TYPED, the driver-provided layout array is logically inserted as a nested element.
Since nested objects might be variable length arrays of structures, the layout elements for a nested object may need to be repeated to cover the whole nested object, as if the nested layout were describing the structure of one element of such an array. Partial repeats are not allowed; the object must be covered by zero or more complete repeats of the nested layout.
The udi_layout_t array must end with a UDI_DL_END element; the first UDI_DL_END not used to match a nested element type is interpreted as the end of the array.
Note - The various INLINE element types and UDI_DL_CB must not be used as part of the layout description for inline memory contents, since nested inline structures are not supported. They are only legal for control block visible layouts. At most one UDI_DL_CB element may be used within a single layout array.
The UDI_DL_BUF type is used for a pointer to a udi_buf_t buffer. It is followed by three unsigned bytes providing further details. These are used to describe related control block fields that affect the way the buffer and its data are handled during domain crossings and during abrupt driver termination ("region kill"). The meaning of each of these bytes is listed below.
byte 0 designates the control block field, if any, that holds a flag or type code that can be used to distinguish between buffers flowing in the "forward" direction (i.e. carrying significant data-typically a "write" request or a "read" acknowledgement) and those flowing in the "reverse" direction (i.e. carrying data that does not need to be preserved-typically a "read" request or a "write" acknowledgement). This field is referred to as the preserve flag for purposes of this layout element specifier. The value in byte 0 is used as a zero-based index into the layout specifier for the control block to which this UDI_DL_BUF applies; this selects the layout element that corresponds to the preserve flag.
byte 1 supplies a mask value to apply to the low order byte of the preserve flag value.
byte 2 supplies a match value to compare with the masked preserve flag value. If they compare equal, the environment must preserve any previously preserved data content and tags in the buffer, up to the buffer's current buf_size, when the data is transferred via channel operation to the new region. If the masked preserve flag does not equal the match value, then all of the buffer's contents in the new region are unspecified, and buffer tags are removed, but the buf_size value is unchanged.
If the above criteria indicate that buffer data is to be preserved, then environments that might use "region kill" must track this buffer and return it to the sending region (by failing the request with a status of UDI_STAT_TERMINATED) if the receiving region is abruptly terminated while still holding this buffer.
9.10 Implementation-Dependent Macros
UDI defines a number of implementation-dependent macros. That is, macros whose parameters and semantics are defined generically, but whose implementation is environment-specfic (often associated with a particular ABI). This section lists implementation-dependent macros related to fundamental types.
NAME UDI_HANDLE_IS_NULL
Determine whether a handle value is null
#include <udi.h>#define UDI_HANDLE_IS_NULL(handle, handle_type)ARGUMENTS handle is the handle value to check.
handle_type is the type specification for that handle.
DESCRIPTION This macro is used to check if an opaque handle value is null (i.e. all zeroes). This is the only way in which a handle value can be compared against any other value.
RETURN VALUES This macro returns a udi_boolean_t value that is TRUE if the handle value is a null handle value.
NAME UDI_HANDLE_ID
Get identification value for specified handle
#include <udi.h>#define UDI_HANDLE_ID(handle, handle_type)ARGUMENTS handle is the handle for which an ID value is to be obtained.
handle_type is the type specification for that handle.
DESCRIPTION For tracing, logging, and debugging purposes it is often useful to be able to identify and differentiate between handles that are passed to the driver. Handles themselves are opaque structures that the driver has no information about. To obtain an ID value that can be used in tracing, logging, or debugging output, the UDI_HANDLE_ID macro should be used.
The ID value will be unique with respect to all other handle IDs for the same handle_type in the same region. Subsequent uses of UDI_HANDLE_ID for the same handle value will produce the same ID value.
This macro is useful in conjunction with udi_snprintf.
RETURN VALUES This macro returns a (void *) value that can be formatted using the %p format code for udi_snprintf calls.
EXAMPLES udi_snprintf("Got channel handle %p\n",
UDI_HANDLE_ID(chan, udi_channel_t));REFERENCES udi_snprintf, udi_trace_write, udi_log_write
NAME UDI_VA_ARG
Varargs macro for UDI data types
#include <udi.h>#define UDI_VA_ARG(pvar, type, va_code)#define UDI_VA_UBIT8_T #define UDI_VA_SBIT8_T #define UDI_VA_UBIT16_T #define UDI_VA_SBIT16_T #define UDI_VA_UBIT32_T #define UDI_VA_SBIT32_T #define UDI_VA_BOOLEAN_T #define UDI_VA_INDEX_T #define UDI_VA_SIZE_T #define UDI_VA_STATUS_T #define UDI_VA_CHANNEL_T #define UDI_VA_ORIGIN_T #define UDI_VA_POINTERARGUMENTS pvar is a pointer into the argument list, as for ISO C va_arg().
type is one of the UDI data types from the table below.
va_code is a code corresponding to a UDI data type or class of UDI data types, from the table below. This must be from the row of the table that includes the type, type.
DESCRIPTION This macro acts as a wrapper around the ISO C va_arg() macro, allowing it to be used portably with UDI data types. The supported data types and their corresponding va_code values are listed in the following table:
1In most cases, testing for equality should not be needed; drivers store opaque values in their own structures and pass them to environment routines later. In cases where an equality check is useful, an environment routine is provided.