在ioctl上获取Linux内核模块的ENOTTY

时间:2017-02-14 20:01:31

标签: linux linux-kernel kernel-module ioctl chardev

我定义了以下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 kernel module - IOCTL usage returns ENOTTY

Linux Kernel Module/IOCTL: inappropriate ioctl for device

但他们的解决方案并不适合我

1 个答案:

答案 0 :(得分:3)

当您的设备驱动程序尚未注册要调用的ioctl函数时,内核将发出

ENOTTY。我担心你的功能没有很好地注册,可能是因为你已经在.unlocked_ioctl结构的struct file_operations字段中注册了它。

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

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

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

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

_IOW是很久以前建立的一个宏(很久以前从linux内核诞生之后)试图解决这个问题,允许你为每个驱动程序选择一个不同的字符(但不依赖于其他对于具有ioctl调用的设备没有与其他设备驱动程序冲突的内核参数,由于上述原因)。这种冲突的可能性很低,但是一旦发生这种情况,你可能会导致错误的机器状态(你已经向错误的设备发出了有效的,工作的ioctl调用)

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

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

另外,查看_IO*宏的定义应该非常具有说明性:)