IOCTL stands for:
Input Output Control
It is a mechanism in Linux device drivers used for:
- Device-specific operations
- Configuration commands
- Runtime control
- Special communication between user space and kernel space
Unlike normal:
read()
write()
operations, IOCTL allows custom commands to be sent to drivers.
An IOCTL Driver is a Linux character driver that implements:
unlocked_ioctl()to handle custom control commands from user applications.
IOCTL is commonly used when:
- read/write are insufficient
- Driver needs multiple commands
- Device configuration is required
- Special operations must be performed
Normal file operations only support:
| Operation | Purpose |
|---|---|
| read() | Receive data |
| write() | Send data |
But drivers often require:
- Enable/disable device
- Set baud rate
- Configure hardware
- Reset device
- Read status registers
IOCTL solves this problem.
| Device | IOCTL Usage |
|---|---|
| UART Driver | Baud rate configuration |
| Camera Driver | Resolution setup |
| Network Driver | Interface control |
| GPIO Driver | Pin configuration |
| RTC Driver | Set alarm |
| Audio Driver | Volume control |
| Touchscreen Driver | Calibration |
+------------------------------+
| User Space Application |
|------------------------------|
| ioctl(fd, cmd, arg) |
+--------------+---------------+
|
v
+------------------------------+
| VFS Layer |
+--------------+---------------+
|
v
+------------------------------+
| IOCTL Driver |
|------------------------------|
| unlocked_ioctl() |
+--------------+---------------+
|
v
+------------------------------+
| Hardware Device |
+------------------------------+
User Application
↓
ioctl(fd, cmd, arg)
↓
Kernel VFS
↓
Driver unlocked_ioctl()
↓
Hardware Operation
Each ioctl operation has unique command ID.
Identifies driver category.
Example:
#define MY_MAGIC 'A'Defines data flow:
| Macro | Purpose |
|---|---|
| _IO | No data transfer |
| _IOR | Read from driver |
| _IOW | Write to driver |
| _IOWR | Read + Write |
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/ioctl.h>Example:
#define WR_VALUE _IOW('A', 'a', int32_t *)
#define RD_VALUE _IOR('A', 'b', int32_t *)Explanation:
| Field | Meaning |
|---|---|
| 'A' | Magic number |
| 'a' | Command number |
| int32_t * | Data type |
Character device registered.
Example:
/dev/my_ioctlKernel routes request to:
unlocked_ioctl()Performs device-specific action.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/ioctl.h>
#define DEVICE_NAME "my_ioctl"
#define WR_VALUE _IOW('A', 'a', int32_t *)
#define RD_VALUE _IOR('A', 'b', int32_t *)
static int32_t value = 0;
static int major;
static struct class *dev_class;
static struct cdev my_cdev;
static long my_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case WR_VALUE:
if (copy_from_user(&value,
(int32_t *)arg,
sizeof(value)))
return -EFAULT;
printk(KERN_INFO "Value Written = %d\n", value);
break;
case RD_VALUE:
if (copy_to_user((int32_t *)arg,
&value,
sizeof(value)))
return -EFAULT;
printk(KERN_INFO "Value Read = %d\n", value);
break;
default:
printk(KERN_INFO "Invalid Command\n");
return -EINVAL;
}
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = my_ioctl,
};
static int __init ioctl_driver_init(void)
{
dev_t dev;
alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
major = MAJOR(dev);
cdev_init(&my_cdev, &fops);
cdev_add(&my_cdev, dev, 1);
dev_class = class_create("my_class");
device_create(dev_class,
NULL,
dev,
NULL,
DEVICE_NAME);
printk(KERN_INFO "IOCTL Driver Loaded\n");
return 0;
}
static void __exit ioctl_driver_exit(void)
{
dev_t dev = MKDEV(major, 0);
device_destroy(dev_class, dev);
class_destroy(dev_class);
cdev_del(&my_cdev);
unregister_chrdev_region(dev, 1);
printk(KERN_INFO "IOCTL Driver Removed\n");
}
module_init(ioctl_driver_init);
module_exit(ioctl_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple IOCTL Driver");#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#define WR_VALUE _IOW('A', 'a', int32_t *)
#define RD_VALUE _IOR('A', 'b', int32_t *)
int main()
{
int fd;
int value = 100;
fd = open("/dev/my_ioctl", O_RDWR);
ioctl(fd, WR_VALUE, &value);
value = 0;
ioctl(fd, RD_VALUE, &value);
printf("Value = %d\n", value);
close(fd);
return 0;
}obj-m += ioctl_driver.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) cleanmakeOutput:
ioctl_driver.kosudo insmod ioctl_driver.kols /dev/my_ioctlgcc test_ioctl.c -o test./testExpected Output:
Value = 100
dmesg | tailExpected:
Value Written = 100
Value Read = 100
Handles ioctl commands.
Example:
.unlocked_ioctl = my_ioctlCopies data from user space to kernel space.
Copies data from kernel space to user space.
No data transfer.
Example:
#define RESET _IO('A', 1)Driver → User.
User → Driver.
Bidirectional transfer.
| Advantage | Description |
|---|---|
| Flexible | Supports custom commands |
| Runtime Configuration | Device control |
| Efficient | Direct communication |
| Standard Linux API | Widely supported |
| Hardware Control | Advanced operations |
| Disadvantage | Description |
|---|---|
| Complex Design | Multiple commands |
| Security Risks | Invalid user pointers |
| Hard Debugging | Custom interfaces |
| No Standardization | Driver-specific |
| Feature | IOCTL | Read/Write |
|---|---|---|
| Purpose | Control/configuration | Data transfer |
| Complexity | High | Simple |
| Flexibility | Very high | Limited |
| Usage | Special commands | Streaming data |
A Linux mechanism for device-specific control operations.
To perform operations that cannot be handled using read/write.
Kernel callback function that processes ioctl commands.
| Macro | Direction |
|---|---|
| _IOR | Driver → User |
| _IOW | User → Driver |
Kernel cannot directly access user memory safely.
Cause:
- Wrong ioctl command number
Fix:
- Verify macros
Cause:
- Invalid user pointer
Fix:
- Validate pointers
Cause:
- Device permissions issue
Fix:
chmod 666 /dev/my_ioctldmesg | tailstrace ./testls -l /dev/my_ioctlAfter learning basic ioctl drivers, move to:
- Compat ioctl
- 32-bit/64-bit compatibility
- ioctl command validation
- Complex structures
- DMA communication
- Async notifications
Always validate:
copy_from_user()data.
Avoid:
- Buffer overflow
- Kernel corruption
Always handle:
default:case.
Prefer:
- sysfs
- procfs
- read/write
for simple interfaces.
Avoid overly complex interfaces.
Maintain stable API.
Prevent kernel crashes.
IOCTL drivers are widely used on:
- Raspberry Pi 5
- BeagleBone Black
- NVIDIA Jetson Nano
- STM32MP157
| Device | Usage |
|---|---|
| TTY Driver | Terminal configuration |
| Network Driver | Interface settings |
| Camera Driver | Resolution control |
| Audio Driver | Mixer settings |
| RTC Driver | Alarm setup |