NAME
ioctl
, _IO
,
_IOR
, _IOW
,
_IOWR
—
how to implement a new ioctl call to
access device drivers
SYNOPSIS
#include
<sys/ioccom.h>
_IO
(g,
t);
_IOR
(g,
n,
t);
_IOW
(g,
n,
t);
_IOWR
(g,
n,
t);
DESCRIPTION
Whenever an ioctl(2) call is made, the kernel dispatches it to the device driver which can then interpret the request number and data in a specialized manner. Ioctls are defined as:#define MYDEVIOCTL fun(g, n, t)
where the different symbols correspond to:
MYDEVIOCTL
- The name which will later be given in the
ioctl(2) system call as second argument, e.g.,
ioctl(fd, MYDEVIOCTL, ...)
fun
()- A macro which can be one of:
_IO
()- The call is a simple message to the kernel by itself. It does not copy anything into the kernel, nor does it want anything back.
_IOR
()- The call only reads parameters from the kernel and does not pass any to it.
_IOW
()- The call only writes parameters to the kernel, but does not want anything back.
_IOWR
()- The call writes data to the kernel and wants information back.
We always consider reading or writing to the kernel, from the user perspective.
- g
- This integer describes to which subsystem the ioctl applies. Here are some
examples:
- '8'
- aac(4)
- 'a'
- nata(4)
- 'B'
- bpf(4)
- 'C'
- cam(4)
- 'C'
- ciss(4)
- 'd'
- disklabel(5)
- 'd'
- diskslice
- 'd'
- drm(4)
- 'f'
- generic file-descriptor
- 'F'
- frame buffer
- 'h'
- HAMMER(5)
- 'i'
- iic(4)
- 'i'
- carp(4)
- 'i'
- gre(4)
- 'k'
- keyboard(4) and syscons(4)
- 'm'
- mem(4)
- 'm'
- /dev/midi
- 'm'
- mtio(4)
- 'M'
- sound(4) mixer
- 'n'
- smb(4)
- 'p'
- /dev/dsp and /dev/audio
- 'p'
- pci(4)
- 'p'
- ppbus(4)
- 'p'
- procfs(5)
- 'q'
- /dev/sequencer
- 'r'
- random number generator
- 't'
- tty(4)
- 't'
- tap(4)
- 't'
- tun(4)
- 't'
- SLIP ttys
- 'T'
- snp(4)
- n
- This number uniquely identifies the ioctl within the group. That said, two subsystems may share the same g, but there may be only one n for a given g. This is an unsigned 8 bit number.
- t
- This specifies the type of the passed parameter. This one gets internally transformed to the size of the parameter, so for example, if you want to pass a structure, then you have to specify that structure and not a pointer to it or sizeof(struct MYDEV).
In order for the new ioctl to be visible to the system, it is
installed in either
<sys/ioctl.h>
or one of the
files that are reached from
<sys/ioctl.h>
.
RETURN VALUES
A distinction must be made at this point. All
*_ioctl
() routines from
within
kernel should return either 0 for success or a defined error code,
as described in
<sys/errno.h>
. At the libc
level though a conversion takes place, so that eventually
ioctl(2) returns either 0 for success or -1 for failure, in which
case the errno variable is set accordingly.
The use of magic numbers such as -1, to indicate that a given
ioctl code was not handled, is strongly discouraged. The value -1 is bound
to the ERESTART
pseudo-error, which is returned
inside kernel to modify return to process.
EXAMPLES
Let's suppose that we want to pass an integer value to the kernel. From the user point of view, this is like writing to the kernel. So we define the ioctl as:
#define MYDEVIOCTL _IOW('i', 25, int)
Within the *_ioctl
() routine of the
driver, it can be then accessed like:
int mydev_ioctl(struct dev_ioctl_args *ap) { int error; int *a; switch (ap->a_cmd) { case MYDEVIOCTL: a = (int *)ap->data; kprintf("Value passed from userspace: %d\n", *a); return (0); /* Success */ break; /* Handle other ioctls here */ default: /* Inappropriate ioctl for device */ error = ENOTTY; break; } return (error); }
In userspace:
int a = 101; if (ioctl(fd, MYDEVIOCTL, &a) == -1) { /* Handle failure */ }