linux驱动--ioctl接口

内核中对底层设备操作完全可以通过read、write接口来实现,在linux 2.2之前都是没有ioctl接口的,2.4以后才引入ioctl接口。
典故(据说),以前在操作软盘时,需要弹出光盘时命令为eject,可以通过write写这个字符串来传输这个指令,但是此时,如果要往软盘中写入”eject”字符串时就出现了歧义的问题。虽然可以通过附加一些代码绕过这个问题,但是不是严谨的开发者的习惯。所以出现了iotcl接口。

int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
/*
inode与filp两个指针对应于应用程序传递的文件描述符fd,这和传递open方法的参数一样。
cmd 由用户空间直接不经修改的传递给驱动程序
arg 可选。
*/

这个里面主要的就是这个cmd,驱动开发人员定义好这个cmd,在接口内部就是根据cmd做的一个分支处理。
在Linux核心中是这样定义一个命令码的:
这里写图片描述

魔数:只是一个数字,位宽为8位。在驱动中直接定义数字,大多是一字符的形式定义的,不过这个也是在8位位宽的数字范围内。具体可以参考内核源码中的文档ioctl-number.txt.

序号:就是这个type中的序号,比如有些字符在内核中已经用了,但是对应的序号并没有用完。自己定义时可以继续跟着在后面定义。

方向:数据传送的方向,如果这个特殊的命令涉及数据传送. 可能的值是 _IOC_NONE(没有数据传输), _IOC_READ, _IOC_WRITE, 和 _IOC_READ|_IOC_WRITE (数据在2个方向被传送). 数据传送是从应用程序的观点来看待的; _IOC_READ 意思是从设备读, 因此设备必须写到用户空间. 注意这个成员是一个位掩码, 因此 _IOC_READ 和 _IOC_WRITE 可使用一个逻辑 AND 操作来抽取。

数据尺寸:就是传输数据的大小。

最终,这个命令其实就是一个特殊的数字,在整个内核中保证和其他驱动所使用的命令不相同就行了。所以,可以自己随便写一个特殊的数字。但是为了保持规范,就有了这个标准。
ioctl命令详解

这里常用的几个

//nr为序号,datatype为数据类型,如int
_IO(type, nr ) //没有参数的命令
_IOR(type, nr, datatype) //从驱动中读数据
_IOW(type, nr, datatype) //写数据到驱动
_IOWR(type,nr, datatype) //双向传送宏

在ioctl函数实现中可以用以下一下两个宏函数来协助进行参数判断
_IOC_TYPE(cmd)用来判断cmd的类型,就是跟定义的魔术进行比对,看是否属于次设备的ioctl的命令类型。
_IOC_NR(cmd)用来判断命令的序号。防止出现定义的序号外的命令。
_IOC_DIR(cmd)用来判断命令的读写方向。
_IOC_SIZE(cmd)用来判断命令中参数的大小。

在具体实现中,用于传输数据的接口函数有四个,这几个接口里面都已经对参数的可读写操作性做了检测,所以不用再调用access_ok来检测了。

copy_from_user
copy_to_user
get_user
put_user

需要检测的接口

__get_user
__put_user

检测函数access_ok。

示例:
命令定义:

#define FREG_IOCTL_MAGIC    'f'
#define FREG_IOCTL_S_INDEX  0x20
#define FREG_IOCTL_NUM      2

/* 定义IOCTL的命令 */
#define FREG_IOCTL_READ     _IOR(FREG_IOCTL_MAGIC, FREG_IOCTL_S_INDEX, int)
#define FREG_IOCTL_WRITE    _IOW(FREG_IOCTL_MAGIC, FREG_IOCTL_S_INDEX + 1, int)

驱动中ioctl接口实现:

static long freg_ioctl(struct file* filp, unsigned int cmd, unsigned long val)
{
    long    ret = 0;
    struct fake_reg_dev* dev = filp->private_data;

    if(down_interruptible(&(dev->sem))) {
        return -ERESTARTSYS;
    }

    /* 检测命令的有效性 */
    if (_IOC_TYPE(cmd) != FREG_IOCTL_MAGIC) {
        printk(KERN_ERR"[%s]ioctl cmd err.\r\n", __func__);
        ret = -EINVAL;
        goto out;
    }
    if (_IOC_NR(cmd) < FREG_IOCTL_S_INDEX || _IOC_NR(cmd) >= (FREG_IOCTL_S_INDEX + FREG_IOCTL_NUM)) {
        printk(KERN_ERR"[%s] ioctl cmd index err.\r\n", __func__);
        ret = -EINVAL;
        goto out;
    }

    switch (cmd) {
        case FREG_IOCTL_WRITE:
            printk(KERN_INFO"ioctl_write.\r\n");
            if (copy_from_user(&(dev->val), (void*)&val, _IOC_SIZE(cmd))) {
                printk(KERN_ERR"[%s]copy from user err.\r\n", __func__);
                ret = -EFAULT;
                goto out;
            }
            break;
        case FREG_IOCTL_READ:
            printk(KERN_INFO"ioctl_read.\r\n");
            if (copy_to_user((void*)&val, &(dev->val), _IOC_SIZE(cmd))) {
                printk(KERN_ERR"[%s]copy from user err.\r\n", __func__);
                ret = -EFAULT;
                goto out;
            }
            break;
        default:
            ret = -EINVAL;
            break;
    }

out:
    up(&(dev->sem));
    return ret;
}

应用程序中使用:

fd = open("/dev/freg", O_RDWR);
ioctl(fd, FREG_IOCTL_READ, &val);
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux PCIe驱动中,ioctl程序是用来实现设备与应用程序之间的通信的一种机制。通过ioctl程序,应用程序可以向设备发送控制命令或者获取设备的状态信息。ioctl程序通常会定义一些特定的命令码,应用程序通过调用ioctl函数并传递相应的命令码和参数来与设备进行交互。 在编写Linux PCIe驱动中的ioctl程序时,一般需要完成以下几个步骤: 1. 在驱动程序中定义ioctl命令码,可以使用宏定义或者枚举类型来表示不同的命令。 2. 在驱动程序的file_operations结构体中注册ioctl函数的处理函数,该处理函数会根据传入的命令码执行相应的操作。 3. 在ioctl处理函数中,根据命令码和参数进行相应的处理,可以通过设备的寄存器访问接口或者其他适当的方式与设备进行通信。 4. 在应用程序中,通过调用ioctl函数并传递相应的命令码和参数来与设备进行通信。 需要注意的是,ioctl程序的具体实现会根据设备的特性和需求而有所不同。因此,在编写ioctl程序时,需要根据具体的设备和应用场景进行相应的设计和实现。 引用\[1\]中的代码示例展示了在Linux PCIe驱动中注册和注销设备驱动程序的过程,而引用\[3\]中提到了设备驱动程序在Linux中的作用和特点。这些引用内容可以帮助理解Linux PCIe驱动中的ioctl程序的作用和实现方式。 #### 引用[.reference_title] - *1* *2* [linux PCIE驱动开发源代码](https://blog.csdn.net/MQWYY3/article/details/112692417)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [linux PCIE驱动开发](https://blog.csdn.net/weixin_39890452/article/details/111797670)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值