IOCTL(9E) Driver Entry Points IOCTL(9E)

NAME


ioctl - control a character device

SYNOPSIS


#include <sys/cred.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>


int prefixioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *cred_p, int *rval_p);


INTERFACE LEVEL


Architecture independent level 1 (DDI/DKI). This entry point is optional.

ARGUMENTS


dev
Device number.


cmd
Command argument the driver ioctl() routine interprets as the
operation to be performed.


arg
Passes parameters between a user program and the driver. When
used with terminals, the argument is the address of a user
program structure containing driver or hardware settings.
Alternatively, the argument may be a value that has meaning
only to the driver. The interpretation of the argument is
driver dependent and usually depends on the command type; the
kernel does not interpret the argument.


mode
A bit field that contains:

o Information set when the device was opened. The
driver may use it to determine if the device was
opened for reading or writing. The driver can make
this determination by checking the FREAD or FWRITE
flags. See the flag argument description of the
open() routine for further values.

o Information on whether the caller is a 32-bit or
64-bit thread.

o In some circumstances address space information
about the arg argument. See below.


cred_p
Pointer to the user credential structure.


rval_p
Pointer to return value for calling process. The driver may
elect to set the value which is valid only if the ioctl()
succeeds.


DESCRIPTION


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(8) 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 must be prepared to receive
commands that they do not recognize or are in contexts that they do not
expect. In the case where cmd is unknown, it is recommended that the
driver return ENOTTY.


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:

1. Define the I/O control command names and the associated value
in the driver's header and comment the commands.

2. 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.

RETURN VALUES


ioctl() should return 0 on success, or the appropriate error number. The
driver may also set the value returned to the calling process through
rval_p.

EXAMPLES


Example 1 ioctl() entry point


The following is an example of the ioctl() entry point and how to support
32-bit and 64-bit applications with the same device driver.


struct passargs32 {
int len;
caddr32_t addr;
};

struct passargs {
int len;
caddr_t addr;
};

xxioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *credp, int *rvalp) {
struct passargs pa;

#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32:
{
struct passargs32 pa32;

ddi_copyin(arg, &pa32, sizeof (struct passargs32),\
mode);
pa.len = pa32.len;
pa.address = pa32.address;
break;
}
case DDI_MODEL_NONE:
ddi_copyin(arg, &pa, sizeof (struct passargs),\
mode);
break;
}
#else /* _MULTI_DATAMODEL */
ddi_copyin(arg, &pa, sizeof (struct passargs), mode);
#endif /* _MULTI_DATAMODEL */

do_ioctl(&pa);
....
}


SEE ALSO


stty(1), dkio(4I), fbio(4I), termio(4I), ttymon(8), open(9E), put(9E),
srv(9E), copyin(9F), copyout(9F), ddi_copyin(9F), ddi_copyout(9F),
ddi_model_convert_from(9F), cb_ops(9S)


Writing Device Drivers

WARNINGS


Non-STREAMS driver ioctl() routines must make sure that user data is
copied into or out of the kernel address space explicitly using
copyin(9F), copyout(9F), ddi_copyin(9F), or ddi_copyout(9F), as
appropriate.


It is a severe error to simply dereference pointers to the user address
space, even when in user context.


Failure to use the appropriate copying routines can result in panics
under load on some platforms, and reproducible panics on others.

NOTES


STREAMS drivers do not have ioctl() routines. The stream head converts
I/O control commands to M_IOCTL messages, which are handled by the
driver's put(9E) or srv(9E) routine.

May 6, 2020 IOCTL(9E)