32-bit and 64-bit Data Structure Macros
The method in Example 15-15 works well for many drivers. An alternate scheme is
to use the data structure macros that are provided in <sys/model.h>to move data
between the application and the kernel. These macros make the code less cluttered and
behave identically, from a functional perspective.
Example 15-16 Using Data Structure Macros to Move Data
int
xxioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *cr, int *rval_p)
{
STRUCT_DECL(opdata, op);
if (cmd != OPONE)
return (ENOTTY);
STRUCT_INIT(op, mode);
if (copyin((void *)arg,
STRUCT_BUF(op), STRUCT_SIZE(op)))
return (EFAULT);
if (STRUCT_FGET(op, flag) != XXACTIVE ||
STRUCT_FGET(op, size) > XXSIZE)
return (EINVAL);
xxdowork(device_state, STRUCT_FGET(op, size));
return (0);
}
How Do the Structure Macros Work?
In a 64-bit device driver, structure macros enable the use of the
same piece of kernel memory by data structures of both sizes. The memory
buffer holds the contents of the native form of the data structure, that
is, the LP64 form, and the ILP32 form. Each structure access is implemented
by a conditional expression. When compiled as a 32-bit driver, only one data
model, the native form, is supported. No conditional expression is used.
The 64-bit versions of the macros depend on the definition of a
shadow version of the data structure. The shadow version describes the 32-bit interface with
fixed-width types. The name of the shadow data structure is formed by appending
“32” to the name of the native data structure. For convenience, place the
definition of the shadow structure in the same file as the native structure
to ease future maintenance costs.
The macros can take the following arguments:
- structname
The structure name of the native form of the data structure as entered after the struct keyword.
- umodel
A flag word that contains the user data model, such as FILP32 or FLP64, extracted from the mode parameter of ioctl(9E).
- handle
The name used to refer to a particular instance of a structure that is manipulated by these macros.
- fieldname
The name of the field within the structure.
When to Use Structure Macros
Macros enable you to make in-place references only to the fields of a
data item. Macros do not provide a way to take separate code
paths that are based on the data model. Macros should be avoided if
the number of fields in the data structure is large. Macros should also
be avoided if the frequency of references to these fields is high.
Macros hide many of the differences between data models in the implementation of
the macros. As a result, code written with this interface is generally easier
to read. When compiled as a 32-bit driver, the resulting code is
compact without needing clumsy #ifdefs, but still preserves type checking.
Declaring and Initializing Structure Handles
STRUCT_DECL(9F) and STRUCT_INIT(9F) can be used to declare and initialize a handle and
space for decoding an ioctl on the stack. STRUCT_HANDLE(9F) and STRUCT_SET_HANDLE(9F) declare and
initialize a handle without allocating space on the stack. The latter macros can
be useful if the structure is very large, or is contained in some
other data structure.
Note - Because the STRUCT_DECL(9F) and STRUCT_HANDLE(9F) macros expand to data structure declarations, these macros
should be grouped with such declarations in C code.
The macros for declaring and initializing structures are as follows:
- STRUCT_DECL(structname, handle)
Declares a structure handlethat is called handle for a structname data structure. STRUCT_DECL allocates space for its native form on the stack. The native form is assumed to be larger than or equal to the ILP32 form of the structure.
- STRUCT_INIT(handle, umodel)
Initializes the data model for handle to umodel. This macro must be invoked before any access is made to a structure handle declared with STRUCT_DECL(9F).
- STRUCT_HANDLE(structname, handle)
Declares a structure handle that is called handle. Contrast with STRUCT_DECL(9F).
- STRUCT_SET_HANDLE(handle, umodel, addr)
Initializes the data model for handle to umodel, and sets addr as the buffer used for subsequent manipulation. Invoke this macro before accessing a structure handle declared with STRUCT_DECL(9F).
Operations on Structure Handles
The macros for performing operations on structures are as follows:
- size_t STRUCT_SIZE(handle)
Returns the size of the structure referred to by handle, according to its embedded data model.
- typeof fieldname STRUCT_FGET(handle, fieldname)
Returns the indicated field in the data structure referred to by handle. This field is a non-pointer type.
- typeof fieldname STRUCT_FGETP(handle, fieldname)
Returns the indicated field in the data structure referred to by handle. This field is a pointer type.
- STRUCT_FSET(handle, fieldname, val)
Sets the indicated field in the data structure referred to by handle to value val. The type of val should match the type of fieldname. The field is a non-pointer type.
- STRUCT_FSETP(handle, fieldname, val)
Sets the indicated field in the data structure referred to by handle to value val. The field is a pointer type.
- typeof fieldname *STRUCT_FADDR(handle, fieldname)
Returns the address of the indicated field in the data structure referred to by handle.
- struct structname *STRUCT_BUF(handle)
Returns a pointer to the native structure described by handle.
Other Operations
Some miscellaneous structure macros follow:
- size_t SIZEOF_STRUCT(struct_name, datamodel)
Returns the size of struct_name, which is based on the given data model.
- size_t SIZEOF_PTR(datamodel)
Returns the size of a pointer based on the given data model.