首先,我们需要定义一个设备结构体来描述所要控制的设备。例如:
struct my_device_data {
int value;
int major;
int minor;
int data;
void* private;
char* name;
struct device* dev;
struct class* cls;
};
然后,我们需要定义一组 ioctl 命令,用于控制设备。例如:
#define MY_DEVICE_GET_VALUE _IOR('q', 1, int *)
#define MY_DEVICE_SET_VALUE _IOW('q', 2, int *)
- _IOR表示将数据从内核空间传递到用户空间,即输入输出读操作。它需要一个指向用户空间缓冲区的指针作为参数,以便内核将数据复制到该缓冲区。
- _IOW表示将数据从用户空间传递到内核空间,即输入输出写操作。它也需要一个指向用户空间缓冲区的指针作为参数,以便内核将数据从该缓冲区中复制出来。
在驱动程序中,我们需要实现一个 ioctl 处理函数来处理这些命令。例如:
static long my_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct my_device_data *data = (struct my_device_data *)file->private_data;
int value;
switch (cmd) {
case MY_DEVICE_GET_VALUE:
if (copy_to_user((int *)arg, &(data->value), sizeof(int)))
return -EFAULT;
break;
case MY_DEVICE_SET_VALUE:
if (copy_from_user(&(data->value), (int *)arg, sizeof(int)))
return -EFAULT;
break;
default:
return -ENOTTY;
}
return 0;
}
在初始化设备文件时,我们需要将该 ioctl 处理函数与文件操作结构体进行关联。例如:
static struct file_operations my_device_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = my_device_ioctl,
};
static int __init my_device_init(void)
{
ddev = kzalloc(sizeof(*ddev), GFP_KERNEL);
if(!ddev)
return -ENOMEM;
ddev->name = DEMO_NAME;
ddev->major = register_chrdev(0, ddev->name, &ddev_fops);
ddev->cls = class_create(THIS_MODULE, DEMO_NAME);
if(IS_ERR(&ddev->cls)){
pr_err("my_device: device class create failed\n");
goto err_class_create;
}
ddev->dev = device_create(ddev->cls, NULL, MKDEV(ddev->major, 0), NULL, DEMO_NAME);
if(IS_ERR(&ddev->dev)){
pr_err("my_device: create device failed@@\n");
goto err_device_create;
}
return 0;
err_device_create:
class_destroy(ddev->cls);
err_class_create:
unregister_chrdev(ddev->major, DEMO_NAME);
kfree(ddev);
return -EFAULT;
return 0;
}
最后,在应用程序中,我们可以使用 ioctl 系统调用来控制驱动程序。例如:
int fd = open("/dev/my_device", O_RDWR);
int value;
/* Set the value to 42 */
value = 42;
ioctl(fd, MY_DEVICE_SET_VALUE, &value);
/* Get the value */
ioctl(fd, MY_DEVICE_GET_VALUE, &value);
printf("The current value is %d\n", value);
close(fd);