ioctl() provides character-access drivers with an alternate entry point that can be used for almost any operation other than a simple transfer of characters in and out of buffers.
Most often, ioctl() is used to control device hardware parameters and establish the protocol used by the driver in processing data.
The kernel determines that this is a character device, and looks up the entry point routines in cb_ops(9S). The kernel then packages the user request and arguments as integers and passes them to the driver's ioctl() routine. The kernel itself
does no processing of the passed command, so it is up to the user program and the driver to agree on what the arguments mean.
I/O control commands are used to implement the terminal settings passed from ttymon(1M) and stty(1), to format disk
devices, to implement a trace driver for debugging, and to clean up character queues. Since the kernel does not interpret the command type that defines the operation, a driver is free to define its own
commands.
Drivers that use an ioctl() routine typically have a command to ``read'' the current ioctl() settings, and at least one other that sets new settings. Drivers
can use the mode argument to determine if the device unit was opened for reading or writing, if necessary, by checking the FREAD or FWRITE setting.
If the third argument, arg, is a pointer to a user buffer, the driver can call the copyin(9F) and copyout(9F)
functions to transfer data between kernel and user space.
Other kernel subsystems may need to call into the drivers ioctl() routine. Drivers that intend to allow their ioctl() routine to be used in this way should publish
the ddi-kernel-ioctl property on the associated devinfo node(s).
When the ddi-kernel-ioctl property is present, the mode argument is used to pass address space information about arg through to the
driver. If the driver expects arg to contain a buffer address, and the FKIOCTL flag is set in mode, then the driver
should assume that it is being handed a kernel buffer address. Otherwise, arg may be the address of a buffer from a user program. The driver can use ddi_copyin(9F) and ddi_copyout(9F) perform the correct type of copy operation for either kernel or user address spaces. See the example on ddi_copyout(9F).
Drivers have to interact with 32-bit and 64-bit applications. If a device driver shares data structures with the application (for example, through exported kernel memory) and the driver gets recompiled
for a 64-bit kernel but the application remains 32-bit, binary layout of any data structures will be incompatible if they contain longs or pointers. The driver needs to know whether there is a model mismatch
between the current thread and the kernel and take necessary action. The mode argument has additional bits set to determine the C Language Type Model which the current thread expects. mode has FILP32 set if the current thread expects 32-bit ( ILP32) semantics, or FLP64
if the current thread expects 64-bit ( LP64) semantics. mode is used in combination with ddi_model_convert_from(9F) and the FMODELS mask to determine whether there is a data
model mismatch between the current thread and the device driver (see the example below). The device driver might have to adjust the shape of data structures before exporting them to a user thread which
supports a different data model.
To implement I/O control commands for a driver the following two steps are required:
- Define the I/O control command names and the associated value in the driver's header and comment the commands.
- Code the ioctl() routine in the driver that defines the functionality for each I/O control command name that is in the header.
The ioctl() routine is coded with instructions on the proper action to take for each command. It is commonly a switch statement, with each case
definition corresponding to an ioctl() name to identify the action that should be taken. However, the command passed to the driver by the user process is an integer value associated
with the command name in the header.
|