linux驱动开发-ioctl

在内核中,ioctl(input/output control)是一个系统调用,用于设备驱动程序和用户空间程序之间的通信。它允许用户空间程序向设备驱动程序发送命令,以执行特定的操作或获取设备的状态信息。ioctl 是一个非常灵活的接口,因为它可以根据设备类型和需求定义不同的命令。

ioctl 的基本结构
ioctl 系统调用的原型如下:

#include <sys/ioctl.h>

int ioctl(int fd, unsigned long request, ...);
  • fd:文件描述符,指向设备驱动程序的打开文件。
  • request:ioctl 命令,由设备驱动程序定义。
  • …:可变参数,用于传递额外的数据

ioctl 请求码

请求码通常由以下几个部分组成:

| 31 - 30 | 29 - 16 | 15 - 8 | 7 - 0 |
| 方向   |  类型   |  序号  |  大小  |

方向(Direction):2 位,用于指示数据传输的方向。

类型(Type):14 位,用于标识设备或驱动程序的类型。

序号(Number):8 位,用于标识具体的操作。

大小(Size):8 位,用于指示数据的大小。


方向(Direction)
方向字段占用 2 位,用于指示数据传输的方向。常见的方向值如下:

_IOC_NONE(0):没有数据传输。

_IOC_WRITE(1):数据从用户空间写入内核空间。

_IOC_READ(2):数据从内核空间读取到用户空间。

_IOC_READ | _IOC_WRITE(3):双向数据传输。

类型(Type)
类型字段占用 14 位,用于标识设备或驱动程序的类型。通常是一个唯一的标识符,例如一个字符常量。

序号(Number)
序号字段占用 8 位,用于标识具体的操作。通常是一个整数。

大小(Size)
大小字段占用 8 位,用于指示数据的大小。通常是数据结构的大小。

ioctl 请求码的宏

在 Linux 内核中,请求码通常使用宏来定义,例如:

#define _IOC(dir, type, nr, size) \
    (((dir) << _IOC_DIRSHIFT) | \
     ((type) << _IOC_TYPESHIFT) | \
     ((nr) << _IOC_NRSHIFT) | \
     ((size) << _IOC_SIZESHIFT))

_IOC_DIRSHIFT、_IOC_TYPESHIFT、_IOC_NRSHIFT、_IOC_SIZESHIFT 是一些预定义的位移常量。

常用的 ioctl 宏

_IO(type, nr):用于定义一个没有数据传输的 ioctl 命令。

_IOR(type, nr, size):用于定义一个从内核读取数据的 ioctl 命令。

_IOW(type, nr, size):用于定义一个向内核写入数据的 ioctl 命令。

_IOWR(type, nr, size):用于定义一个双向数据传输的 ioctl 命令。

_IOC_DIR(request):提取请求码中的方向(Direction)。

_IOC_TYPE(request):提取请求码中的类型(Type)。

_IOC_NR(request):提取请求码中的序号(Number)。

_IOC_SIZE(request):提取请求码中的大小(Size)。

示例

假设我们有一个字符设备驱动程序,我们希望用户空间程序能够通过 ioctl 命令来设置设备的某个参数。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

// 定义设备类型和 ioctl 命令
#define MY_DEVICE_TYPE 'M'
#define MY_IOCTL_SET_PARAM _IOW(MY_DEVICE_TYPE, 0, int)

// 设备打开函数
static int my_device_open(struct inode *inode, struct file *file) {
   
    printk(KERN_INFO "my_device: open\n");
    return 0;
}

// 设备释放函数
static int my_device_release(struct inode *inode, struct file *file) {
   
    printk(KERN_INFO "my_device: release\n");
    return 0;
}

// ioctl 处理函数
static long my_device_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
   
    int param;

    switch (cmd) {
   
        case MY_IOCTL_SET_PARAM:
            // 从用户空间复制参数到内核空间
            // 注意:copy_from_user() 函数返回 0 表示成功,非 0 表示失败
            if (copy_from_user(&param, (int __user *)arg, _IOC_SIZE(int))) {
   
                return -EFAULT;
            }
            printk(KERN_INFO "my_device: set param to %d\n", param);
            break;
        default:
            return -EINVAL; // 无效的命令
    }
    return 0;
}

// 文件操作结构体
static struct file_operations my_device_fops = {
   
    .owner = THIS_MODULE,
    .open = my_device_open,
    .release = my_device_release,
    .unlocked_ioctl = my_device_ioctl,
};

// 设备号和字符设备结构体
static dev_t my_device_dev;
static struct cdev my_device_cdev;

// 模块初始化函数
static int __init my_device_init(void) {
   
    int ret;

    // 分配设备号
    ret = alloc_chrdev_region(&my_device_dev, 0, 1, "my_device");
    if (ret < 0) {
   
        printk(KERN_ERR "my_device: failed to allocate device number\n");
        return ret;
    }

    // 初始化字符设备
    cdev_init(&my_device_cdev, &my_device_fops);
    my_device_cdev.owner = THIS_MODULE;

    // 添加字符设备
    ret = cdev_add(&my_device_cdev, my_device_dev, 1);
    if (ret < 0) {
   
        printk(KERN_ERR "my_device: failed to add character device\n");
        unregister_chrdev_region(my_device_dev, 1);
        return ret;
    }

    printk(KERN_INFO "my_device: module loaded\n");
    return 0;
}

// 模块退出函数
static void __exit my_device_exit(void) {
   
    // 删除字符设备
    cdev_del(&my_device_cdev);
    // 注销设备号
    unregister_chrdev_region(my_device_dev, 1);
    printk(KERN_INFO "my_device: module unloaded\n");
}

// 模块初始化和退出函数的注册
module_init(my_device_init);
module_exit(my_device_exit);

// 模块许可证、作者和描述信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可能只会写BUG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值