UDI drivers are portable across big- and little-endian platforms. Endianness of data must be managed in any structure that requires a specific endianness which is potentially different from the driver's endianness. This includes hardware protocol-defined structures such as SCSI commands (which are big endian) or networking headers (which are generally big endian), shared control structures (DMA-able structures which are shared between the driver and the device, are typically in system memory and in the endianness of the device), or device memory (registers, card RAM, etc.).
UDI physical I/O drivers access device memory using an access handle with which the driver associates its device endianness, allowing the environment to implicitly take care of any needed endian conversions (see the Physical I/O Specification). The other two cases (protocol structures and shared control structures) are directly accessed by the driver using a pointer and therefore require direct involvement of the driver in the managing of the structure endianness.
Protocol structures and shared control structures have different characteristcs with respect to endianness handling in UDI. First, protocol structures have a required endianness that is known at compile time, whereas shared control structures have a required endianness which in general is only known at runtime (due to the impacts of intervening bus bridges). Second, the relationship between driver endianness and protocol structure endianness is not indicated in any UDI-defined manner, whereas a shared control structure has a must_swap flag associated with it when it is allocated.
These characteristics lead to different approaches in the construction of the corresponding C structure definitions, as described below.
As specified in Section 9.8, "Structures Requiring a Fixed Binary Representation," on page 9-15, any C structure definitions used to represent hardware structures must be constructed at least according to the following rules:
These rules should generally be sufficient for the needs of shared control structures, combined with appropriate byte-swapping based on the must_swap flag. However, since protocol structures have no associated must_swap flag or other UDI-defined swapping indication, protocol structures should be handled by the driver using the further rules defined in the byte-by-byte layout method below.
In this method, the C structure definition for a hardware-defined structure is laid out byte-by-byte, splitting up multi-byte integer quantities and combining adjoining integer quantities that share a byte. This means that each field in the structure must be either exactly one byte in size or an array of byte-sized elements.
To help the driver deal with these structures, bit-field, multi-byte, and other helper utilities are provided in Section 22.2.2 below. The multi-byte helper utilities impose a rule on the naming of fields making up a multi-byte quantity in these structures: the fields must all be named with a common name followed by a digit 0..N, where myfield0 is the least significant byte of the multi-byte quantity, myfield1 the next most significant byte, and myfieldN is the most significant byte. See "Multi-Byte Macros" below for details.
For example, a 10-byte SCSI command might look like the following when defined naturally in SCSI's big endian format, using non-portable bit fields for a particular compiler. This structure would only work correctly on a big-endian platform, with ISO C compilers that make appropriate assumptions about bit field order, and on platforms that don't require natural alignment and in which an int is 32 bits.
typedef struct {
udi_ubit32_t c10_opcode:8; /* command code */
udi_ubit32_t c10_lun:3; /* LUN */
udi_ubit32_t c10_dpo:1; /* Disable Page Out */
udi_ubit32_t c10_fua:1; /* Force Unit Access */
udi_ubit32_t c10_rsvd1:2; /* Reserved: must be zero */
udi_ubit32_t c10_reladr:1; /* Addr Relative to prev linked cmd */
udi_ubit32_t c10_lba3:8; /* LBA - high order byte */
udi_ubit32_t c10_lba2:8; /* LBA - next order byte */
udi_ubit16_t c10_lba0; /* LBA - low order two bytes */
udi_ubit8_t c10_rsvd2; /* Reserved: must be zero */
udi_ubit16_t c10_len; /* transfer length */
udi_ubit8_t c10_ctrl; /* control byte */
} udi_scsi_cdb_10_t;
When converted using the byte-by-byte layout rules this becomes:
typedef struct {
udi_ubit8_t c10_opcode; /* Command code */
udi_ubit8_t c10_flags; /* Flags */
udi_ubit8_t c10_lba3; /* LBA - high order byte */
udi_ubit8_t c10_lba2; /* LBA - next order byte */
udi_ubit8_t c10_lba1; /* LBA - next order byte */
udi_ubit8_t c10_lba0; /* LBA - low order byte */
udi_ubit8_t c10_rsvd1; /* Reserved: must be zero */
udi_ubit8_t c10_len1; /* Transfer length - high order byte */
udi_ubit8_t c10_len0; /* Transfer length - low order byte */
udi_ubit8_t c10_ctrl; /* Control byte */
} udi_scsi_cdb_10_t;
which is completely portable, across any platform, big or little endian, with or without natural alignment, with any ISO C compiler, etc. Using the helper macros, the following "build cdb" macro could be defined:
#define UDI_SCSI_BUILD_CDB_10(cdb, opcode, data_len, block_num, \
lun,dpo, fua, reladr) \
{ \
(cdb)->c10_opcode = opcode; \
(cdb)->c10_flags = ((lun)<<5) | (((dpo)<<4) | ((fua)<<3) | \
(reladr)); \
UDI_MBSET(4, cdb, c10_lba, block_num); \
UDI_MBSET(2, cdb, c10_len, data_len); \
(cdb)->c10_ctrl = 0; \
}
These macros are provided for use in the handling of hardware structures built using the byte-by-byte structure layout, which typically is only used with hardware protocol structures. However these macros should be useful in general with any hardware structures that have non-naturally-aligned multi-byte quantities (or in general any multi-bit quantity that isn't naturally aligned on a power-of-two byte boundary) because such quantities will need to be split up to meet the general requirements on the definition of hardware structures as given in Section 9.8.
The bit-field macros, UDI_BFMASK, UDI_BFGET, and UDI_BFSET, are used to perform basic operations on bit-fields within a udi_ubit8_t variable or value. UDI_BFMASK is used to create a bit mask (all 1's in the bit field, zeroes elsewhere); UDI_BFGET extracts a bit field; and UDI_BFGET deposits a value into a bit field.
#define
UDI_
BFMASK
(
p
,
len
) \
(((1U<<(len))-1) << (p))
#define
UDI_
BFGET
(
val
,
p
,
len
) \
(((udi_ubit8_t)(val) >> (p)) & ((1U<<(len))-1))
#define
UDI_
BFSET
(
val
,
p
,
len
,
dst
) \
((dst) = ((dst) & ~UDI_BFMASK(p,len)) | \
(((udi_ubit8_t)(val) << (p)) & \
UDI_BFMASK(p,len)))
len is the size in bits of the bit field. 1 ≤ len ≤ 8-p.
val is a udi_ubit8_t variable or value.
dst is a udi_ubit8_t variable into which a value will be deposited.
DESCRIPTION Bit-fields in these macros refer to sub-divisions of a byte and are defined by a 2-tuple ( p , len ) where p is the bit position in the byte of the least significant bit in the bit field, and len is the size in bits of the bit field. The bit position p is 0 for the least significant bit in the byte, 7 for the most significant bit. Note that 0 ≤ p ≤ 7 and 1 ≤ len ≤ 8-p.
UDI_BFMASK creates a p , len bit mask containing all 1's in the corresponding bit field, zeroes elsewhere.
UDI_BFGET extracts an unsigned p , len bit-field from val .
UDI_BFSET deposits val into the p , len bit-field in dst .
These macros must be called as if they, respectively, had the following functional interfaces:
udi_ubit8_t
UDI_BFMASK
(
udi_ubit8_t
p
,
udi_ubit8_t
len
);
udi_ubit8_t
UDI_BFGET
(
udi_ubit8_t
val
,
udi_ubit8_t
p
,
udi_ubit8_t
len
);
void
UDI_BFSET
(
udi_ubit8_t
val
,
udi_ubit8_t
p
,
udi_ubit8_t
len
,
udi_ubit8_t
dst
);
Note that UDI_BFSET modifies dst , and dst must be an lvalue (assignable on the left side of an assignment statement).
The multi-byte helper macros, UDI_MBGET and UDI_MBSET and their variants, are used to extract and deposit multi-byte quantities from a structure which has been constructed according to the byte-by-byte layout rules given in Section 22.2.1.1 on page 22-2.
These macros have arguments called "structp" and "field". The structp argument is a pointer to a structure that contains N single-byte (and byte-aligned) members whose names are "field"0, "field"1, ... "field"N, which together represent a multi-byte quantity in the structure. "field"0 is the least significant byte; "field"N is the most significant.
For example, a structure that has a 3-byte "sum" field might have field names of sum0 , sum1 , and sum2 , and the complete 24-bit field could be extracted from the structure into a variable, my_sum , using the UDI_MBGET macro as follows:
my_sum = UDI_MBGET(3, &my_struct, sum);
To write a value into this 3-byte field, use the UDI_MBSET macro as follows:
UDI_MBSET(3, &my_struct, sum, my_sum);
#define
UDI_
MBGET
(
N
,
structp
,
field
) \
UDI_MBGET_##N (structp, field)
#define
UDI_
MBGET_2
(
structp
,
field
) \
((structp)->field##0 | ((structp)->field##1<<8))
#define
UDI_
MBGET_3
(
structp
,
field
) \
((structp)->field##0 | ((structp)->field##1<<8) | \
((structp)->field##2<<16))
#define
UDI_
MBGET_4
(
structp
,
field
) \
((structp)->field##0 | ((structp)->field##1<<8) | \
((structp)->field##2<<16)|((structp)->field##3<<24))
structp is a pointer to a structure that contains N single-byte (and byte-aligned) members whose names are field 0, field 1, ... field n (n= N -1), which together represent an N -byte quantity in the structure.
field is the base name of a sequence of members in structp . The name of each member in the sequence is field followed by a decimal number in the range 0.. N -1; this number represents the byte number in a multi-byte quantity, with byte 0 being the least significant byte.
DESCRIPTION These macros are used to extract multi-byte quantities from a structure which has been constructed according to the byte-by-byte layout rules given in Section 22.2.1.1 on page 22-2. The structure is pointed to by the structp argument, and the multi-byte quantities are represented by a sequence of fields in the structure whose names are based on the field argument.
As described above, the structp argument is a pointer to a structure that contains N single-byte (and byte-aligned) members whose names are field 0, field 1, ... field n (n= N -1), which together represent an N -byte quantity in the structure. field 0 is the least significant byte; field n is the most significant.
UDI_MBGET extracts an N -byte quantity from the structure pointed to by structp . UDI_MBGET _2, UDI_MBGET _3, and UDI_MBGET _4, extract 2, 3, and 4-byte quantities, respectively.
These macros don't translate well into functional interfaces, so no corresponding functional interfaces are given.
#define
UDI_
MBSET
(
N
,
structp
,
field
,
val
) \
UDI_MBSET_##N (structp, field, val)
#define
UDI_
MBSET_2
(
structp
,
field
,
val
) \
((structp)->field##0 = (val) & 0xff, \
(structp)->field##1 = ((val) >> 8) & 0xff)
#define
UDI_
MBSET_3
(
structp
,
field
,
val
) \
((structp)->field##0 = (val) & 0xff, \
(structp)->field##1 = ((val) >> 8) & 0xff, \
(structp)->field##2 = ((val) >> 16) & 0xff)
#define
UDI_
MBSET_4
(
structp
,
field
,
val
) \
((structp)->field##0 = (val) & 0xff, \
(structp)->field##1 = ((val) >> 8) & 0xff, \
(structp)->field##2 = ((val) >> 16) & 0xff, \
(structp)->field##3 = ((val) >> 24) & 0xff)
structp is a pointer to a structure that contains N single-byte (and byte-aligned) members whose names are field 0, field 1, ... field n (n= N -1), which together represent an N -byte quantity in the structure.
field is the base name of a sequence of members in structp . The name of each member in the sequence is field followed by a decimal number in the range 0.. N -1; this number represents the byte number in a multi-byte quantity, with byte 0 being the least significant byte.
val is the value to deposit into the multi-byte quantity in the structure pointed to by structp and corresponding to field .
DESCRIPTION These macros are used to deposit a value into a multi-byte quantity in a structure which has been constructed according to the byte-by-byte layout rules given in Section 22.2.1.1 on page 22-2. The structure is pointed to by the structp argument, and the multi-byte quantities are represented by a sequence of fields in the structure whose names are based on the field argument.
As described above, the structp argument is a pointer to a structure that contains N single-byte (and byte-aligned) members whose names are field 0, field 1, ... field n (n= N -1), which together represent an N -byte quantity in the structure. field 0 is the least significant byte; field n is the most significant.
UDI_MBSET deposits val into the an N -byte quantity represented by field in the structure pointed to by structp . UDI_MBSET _2, UDI_MBSET _3, and UDI_MBSET _4, deposit into 2, 3, and 4-byte quantities, respectively.
These macros don't translate well into functional interfaces, so no corresponding functional interfaces are given.
UDI provides two basic macros for endian translation of a single 16-bit or 32-bit integer: UDI_ENDIAN_SWAP16 and UDI_ENDIAN_SWAP32, respectively. The utility function udi_endian_swap provides for the endian conversion of greater than 32-bit integers; this function has a rep_count and stride parameter, allowing for multiple byte-swaps of a given size in a single call. This can be used to byte-swap each element in an array (as shown by the UDI_ENDIAN_SWAP_ARRAY macro), or to perform more complex sequences of byte-swaps across sub-elements of an array of structures.
#define
UDI_ENDIAN_SWAP_16
(
data16
) \
( (((data16) & 0x00ff) << 8) | \
(((data16) >> 8) & 0x00ff) )
#define
UDI_ENDIAN_SWAP_32
(
data32
) \
( (((data32) & 0x000000ff) << 24) | \
(((data32) & 0x0000ff00) << 8) | \
(((data32) >> 8) & 0x0000ff00) | \
(((data32) >> 24) & 0x000000ff) )
data32 is a 32-bit data value.
DESCRIPTION UDI_ENDIAN_SWAP_16 byte-swaps a single 16-bit data value; UDI_ENDIAN_SWAP_32 byte-swaps a single 32-bit data value. These two macros provide basic endian translation of an individual data item. See udi_endian_swap on page 22-14 for information on an endian utility that provides for larger than 32-bit endian translation and that allows for multiple byte-swaps in a single call.
These macros must be called as if they, respectively, had the following functional interface:
udi_ubit16_t
UDI_ENDIAN_SWAP_16
(
udi_ubit16_t
data16
);
void
udi_endian_swap
(
const void *
src
,
void *
dst
,
udi_ubit8_t
swap_size
,
udi_ubit8_t
stride
,
udi_ubit16_t
rep_count
);
dst points to a memory area into which the results of the byte-swap will be placed. This memory area must be at least the size of the memory area pointed to by src . src and dst may be the same, but if not, the memory areas must be non-overlapping.
swap_size is the size, in bytes, of each element to be byte-swapped. swap_size must be a power-of-two which is less than or equal to stride .
stride is the number of bytes by which to increment src between repetitions.
rep_count is the number of swap_size byte-swaps to perform.
DESCRIPTION The udi_endian_swap function generates rep_count byte-swapping copies from src to dst , of swap_size bytes each. Between each repetition src is incremented by stride to obtain the next element to be swapped. Only the memory actually byte-swapped into dst will be changed as a result of this call.
If swap_size is 1, and src is equal to dst , this function acts as a no-op. If swap_size is 1, and src is not equal to dst , this function performs a copy.
EXAMPLES If the driver has an array of structures that need to be endian translated, it can call udi_endian_swap once for each type of multi-byte field in the structure, setting src to point to the address of the field in the zeroth element of the array (e.g. &array[0]->my_field ), swap_size to the size of the field, stride to the size of the structure, and rep_count to the number of elements in the array.
#define \
UDI_
ENDIAN_SWAP_ARRAY
(
src
,
element_size
,
count
) \
udi_endian_swap(src, src, element_size, \
element_size, count)
element_size is the size, in bytes, of each element to be byte-swapped. element_size must be a power-of-two.
count the number of elements in the src array to be byte-swapped.
DESCRIPTION UDI_ENDIAN_SWAP_ARRAY byte-swaps count elements in the array pointed to by src . The results are written in place in the src array.
The macro UDI_ENDIAN_SWAP_ARRAY must be called as if it had the following functional interface, as can be derived from the above macro definition and the definition of udi_endian_swap :
void
UDI_ENDIAN_SWAP_ARRAY
(
void
*
src
,
udi_ubit8_t
element_size
,
udi_ubit16_t
count
);