Writing Device Drivers

32–bit and 64–bit Data Structure Macros

While the method shown in the previous example works well for many drivers, an alternate scheme is to use the data structure macros 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 10–17 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, these macros do all that is necessary to use the same piece of kernel memory as a buffer for the contents of the native form of the data structure (that is, the LP64 form), and for the ILP32 form of the same structure. This usually means that each structure access is implemented by a conditional expression. When compiled as a 32-bit driver, only one data model is supported and only the native form exists, so no conditional expression is used.

The 64-bit versions of the macros depend on the definition of a shadow version of the data structure that describes the 32-bit interface using 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 take arguments such as:

structname

The structure name (as would appear after the struct keyword) of the native form of the data structure

umodel

A flag word containing 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. They do not provide a way to take separate code paths based on the data model. They should be avoided if the number of fields in the data structure is large or the frequency of references to these fields is high.

Because the macros hide many of the differences between data models in the implementation of the macros, 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, they should be grouped with such declarations in C code.


STRUCT_DECL(structname, handle)

Declares a structure handle called handle for a struct structname data structure, and 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 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

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 (non-pointer type) in the data structure referred to by handle.

typeof fieldname STRUCT_FGETP(handle, fieldname)

Returns the indicated field (pointer type) in the data structure referred to by handle.

STRUCT_FSET(handle, fieldname, val)

Sets the indicated field (non-pointer type) in the data structure referred to by handle to value val. The type of val should match the type of fieldname.

STRUCT_FSETP(handle, fieldname, val)

Sets the indicated field (pointer type) in the data structure referred to by handle to value val.

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

size_t SIZEOF_STRUCT(struct_name, datamodel)

Returns the size of struct_name based on the given data model.

size_t SIZEOF_PTR(datamodel)

Returns the size of a pointer based on the given data model.