|
|
Endianness Management Utility Functions
22.1 Overview
This chapter defines endianness management utility functions.
22.2 Endianness Management
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.
22.2.1 Rules for C Structure Definitions
As specified in Section 9.8, "Structures Requiring a Fixed Binary Representation", 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 within (offsets from the beginning of) the structure. Bit fields are not allowed (see the warning in Section 9.8 for details).
- Every byte in the structure must be accounted for.
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.
22.2.1.1 Byte-by-byte structure layout
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; \ }22.2.2 Helper Macros
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.
22.2.2.1 Bit-field Macros
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.
NAME UDI_BFMASK,
Bit-field helper macros
UDI_BFGET, UDI_BFSET
#include <udi.h>#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)))ARGUMENTS p is the bit position in the byte of the least significant bit in the bit field. The bit position p is 0 for the least significant bit in the byte, 7 for the most significant bit. 0 £ p £ 7.
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).
22.2.2.2 Multi-Byte Macros
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.
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);
NAME UDI_MBGET, UDI_MBGET_2/3/4
Multi-byte extract helper macros
#include <udi.h>#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))ARGUMENTS N is the number of bytes in the corresponding multi-byte quantity. N must be 2, 3, or 4.
structp is a pointer to a structure that contains N single-byte (and byte-aligned) members whose names are field0, field1, ... fieldn (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. Each of these structure members must be of type udi_ubit8_t.
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. 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 field0, field1, ... fieldn (n=N-1), which together represent an N-byte quantity in the structure. field0 is the least significant byte; fieldn is the most significant.
UDI_MBGET extracts an N-byte unsigned 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.
NAME UDI_MBSET, UDI_MBSET_2/3/4
Multi-byte deposit helper macros
#include <udi.h>#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)ARGUMENTS N is the number of bytes in the corresponding multi-byte quantity.
structp is a pointer to a structure that contains N single-byte (and byte-aligned) members whose names are field0, field1, ... fieldn (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. Each of these structure members must be of type udi_ubit8_t.
val is the value to deposit into the multi-byte quantity in the structure pointed to by structp and corresponding to field. This value must be of an unsigned data type, such as udi_ubit16_t.
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. 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 field0, field1, ... fieldn (n=N-1), which together represent an N-byte quantity in the structure. field0 is the least significant byte; fieldn is the most significant.
UDI_MBSET deposits val into the N-byte unsigned 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.
22.2.3 Endian-Swapping Utilities
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.
Note - Except for use with DMA-able shared control structures (described in the UDI Physical I/O Specification), drivers should use the byte-by-byte structure layout rather than using these utilities, since the relationship between driver and device endianness is not indicated in any UDI-specified fashion, either at compile time or at run time.
NAME UDI_ENDIAN_SWAP_16/32
Byte-swap 16 or 32-bit integers
#include <udi.h>#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) )ARGUMENTS data16 is a 16-bit data value.
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 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 ); udi_ubit32_t UDI_ENDIAN_SWAP_32 ( udi_ubit32_t data32 );NAME udi_endian_swap
Byte-swap multiple data items
#include <udi.h>void udi_endian_swap ( const void *src, void *dst, udi_ubit8_t swap_size, udi_ubit8_t stride, udi_ubit16_t rep_count );ARGUMENTS src points to a memory area across which byte swapping will be performed, as defined below.
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.
RefereNCES UDI_ENDIAN_SWAP_ARRAY
NAME UDI_ENDIAN_SWAP_ARRAY
Byte-swap each element in an array
#include <udi.h>#define \ UDI_ENDIAN_SWAP_ARRAY(src, element_size, count) \ udi_endian_swap(src, src, element_size, \ element_size, count)ARGUMENTS src points to an array of count elements, each of which are element_size bytes in size.
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 );