The cmd variable identifies a specific device control operation. If arg contains a user virtual address, ioctl(9E) must call ddi_copyin(9F) or ddi_copyout(9F) to transfer data between the data structure in the application program pointed to by arg and the driver. In Example 10-13, for the case of an XX_GET_STATUS request the contents of xsp->regp->csr are copied to the address in arg. When a request succeeds, ioctl(9E) can store in *rvalp any integer value to be the return value of the ioctl(2) system call that made the request. Negative return values, such as -1, should be avoided, as they usually indicate the system call failed, and many application programs assume that negative values indicate failure.
An application that uses the I/O controls discussed above could look like Example 10-14.
Example 10-14 Using ioctl(9E)
I/O Control Support for 64-Bit Capable Device Drivers
The Solaris kernel runs in 64-bit mode on suitable hardware and supports both 32-bit and 64-bit applications. A 64-bit device driver is required to support I/O control commands from 32-bit and 64-bit user mode programs. The difference between a 32-bit program and a 64-bit program is its C language type model: a 32-bit program is ILP32 and a 64-bit program is LP64. See Appendix C, Making a Device Driver 64-Bit Ready for information on C data type models.
Any data that flows between programs and the kernel and vice versa (for example using ddi_copyin(9F) or ddi_copyout(9F)) will either need to be identical in format regardless of the type model of the kernel and application, or the device driver should be able to handle a model mismatch between it and the application and adjust the data format accordingly.
To determine if there is a model mismatch, the ioctl(9E) mode parameter passes the data model bits to the driver. As Example 10-15 shows, the mode parameter is then passed to ddi_model_convert_from(9F) to determine if any model conversion is necessary.
The data model is passed to the ioctl(9E) routine using a flag subfield of the mode argument. The flag will be set to one of:
DATAMODEL_ILP32
DATAMODEL_LP64
with FNATIVE conditionally defined to match the data model of the kernel implementation. The flag should be extracted from the mode argument using the FMODELS mask. The driver can then determine the data model explicitly to work out how to copy the application data structure.
The DDI function ddi_model_convert_from(9F) is a convenience routine that can assist some drivers with their ioctl() calls. The function takes the data type model of the user application as an argument and returns one of the following values:
DDI_MODEL_ILP32 -- Convert from ILP32 application
DDI_MODEL_NONE -- No conversion needed
DDI_MODEL_NONE is returned if no data conversion is necessary. This is the case when the application and driver have the same data model. DDI_MODEL_ILP32 is returned if the driver is compiled to the LP64 data model and is communicating with a 32-bit application.
In the following example, the driver copies a data structure that contains a user address. Because the data structure changes size from ILP32 to LP64, the 64-bit driver uses a 32-bit version of the structure when communicating with a 32-bit application.
Example 10-15 ioctl(9E) Routine to Support 32-bit and 64-bit Applications