3

我定义了以下 chardev:

。H

#define MAJOR_NUM 245
#define MINOR_NUM 0
#define IOCTL_MY_DEV1 _IOW(MAJOR_NUM, 0, unsigned long)
#define IOCTL_MY_DEV2 _IOW(MAJOR_NUM, 1, unsigned long)
#define IOCTL_MY_DEV3 _IOW(MAJOR_NUM, 2, unsigned long)

模块.c

static long device_ioctl(
                  struct file*   file,
                  unsigned int   ioctl_num,
                  unsigned long  ioctl_param)
{
    ...
}

static int device_open(struct inode* inode, struct file* file)
{
    ...
}

static int device_release(struct inode* inode, struct file* file)
{
    ...
}

struct file_operations Fops = {
    .open=device_open,
    .unlocked_ioctl= device_ioctl,
    .release=device_release
};

static int __init my_dev_init(void)
{
    register_chrdev(MAJOR_NUM, "MY_DEV", &Fops);
    ...
}
module_init(my_dev_init);

我的用户代码

ioctl(fd, IOCTL_MY_DEV1, 1);

总是失败并出现相同的错误:ENOTTY

设备的 ioctl 不合适

我见过类似的问题:即

Linux 内核模块 - IOCTL 使用返回 ENOTTY

Linux 内核模块/IOCTL:设备的 ioctl 不合适

但是他们的解决方案对我不起作用

4

1 回答 1

3

ENOTTY当您的设备驱动程序没有注册要调用的 ioctl 函数时,由内核发出。恐怕你的功能没有很好的注册,可能是因为你在结构.unlocked_ioctl领域注册了它struct file_operations

如果在函数的锁定版本中注册它,可能会得到不同的结果。最可能的原因是 inode 被锁定以进行 ioctl 调用(应该是这样,以避免同时readwrite对同一设备进行操作的竞争条件)

抱歉,我无法访问 linux 源代码树以获取要使用的字段的正确名称,但可以肯定的是,您可以自己找到它。

笔记

我观察到您使用了 macro _IOW,使用主编号作为唯一标识符。这可能不是你想要的。_IOW尝试确保 ioctl 调用获得唯一标识符的第一个参数。没有获取此类标识符的通用方法,因为这是您在应用程序代码和内核代码之间创建的接口契约。因此,使用主要数字是不好的做法,原因有两个:

  • 几个设备(至少在 linux 中)可以共享相同的主设备号(linux 内核中的次要分配允许这样做),这使得设备的 ioctl 之间发生冲突成为可能。
  • 如果您更改主编号(您配置了已分配该编号的内核),您必须重新编译所有用户级软件以应对新设备 ioctl id(如果您这样做,所有这些都会更改)

_IOW是一个很久以前(很久以前从 linux 内核的诞生)构建的宏,它试图通过允许您为每个驱动程序选择不同的字符(但不依赖于其他内核参数,原因如上所述)来解决这个问题) 对于具有 ioctl 调用不与另一个设备驱动程序冲突的设备。这种冲突的可能性很低,但是当它发生时,您可能会导致错误的机器状态(您已向错误的设备发出有效的、有效的 ioctl 调用)

古代 unix(和早期的 linux)内核使用不同的字符来构建这些调用,例如,tty驱动程序用作宏'T'的参数,使用的scsi 磁盘等。_IO*'S'

我建议您选择一个随机数(没有出现在 linux 内核列表的其他位置),然后在所有设备中使用它(您编写的驱动程序可能比内核中的驱动程序少)并为每个 ioctl 选择不同的 ioctl id称呼。以这种方式使用已注册的 ioctl 维护本地 ioctl 文件比尝试猜测始终有效的值要好得多。

另外,看一下_IO*宏的定义应该很能说明问题:)

于 2017-02-16T07:48:44.133 回答