开发者

How do ioctls know which function to call in linux?

So when I call an ioctl on a device, with an ioctl number开发者_开发技巧, how does it know which function to call?


The ioctl(2) enters via the fs/ioctl.c function:

SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
{
    struct file *filp;
    int error = -EBADF;
    int fput_needed;

    filp = fget_light(fd, &fput_needed);
    if (!filp)
            goto out;

    error = security_file_ioctl(filp, cmd, arg);
    if (error)
            goto out_fput;

    error = do_vfs_ioctl(filp, fd, cmd, arg);
 out_fput:
    fput_light(filp, fput_needed);
 out:
    return error;
}

Note that there is already a filedescriptor fd associated. The kernel then calls fget_light() to look up a filp (roughly, file pointer, but don't confuse this with the standard IO FILE * file pointer). The call into security_file_ioctl() checks whether the loaded security module will allow the ioctl (whether by name, as in AppArmor and TOMOYO, or by labels, as in SMACK and SELinux), as well as whether or not the user has the correct capability (capabilities(7)) to make the call. If the call is allowed, then do_vfs_ioctl() is called to either handle common ioctls itself:

    switch (cmd) {
    case FIOCLEX:
            set_close_on_exec(fd, 1);
            break;
    /* ... */

If none of those common cases are correct, then the kernel calls a helper routine:

static long vfs_ioctl(struct file *filp, unsigned int cmd,
                  unsigned long arg)
{
    int error = -ENOTTY;

    if (!filp->f_op || !filp->f_op->unlocked_ioctl)
            goto out;

    error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
    if (error == -ENOIOCTLCMD)
            error = -EINVAL;
 out:
    return error;
}

Drivers supply their own .unlocked_ioctl function pointer, like this pipe implementation in fs/pipe.c:

const struct file_operations rdwr_pipefifo_fops = {
    .llseek         = no_llseek,
    .read           = do_sync_read,
    .aio_read       = pipe_read,
    .write          = do_sync_write,
    .aio_write      = pipe_write,
    .poll           = pipe_poll,
    .unlocked_ioctl = pipe_ioctl,
    .open           = pipe_rdwr_open,
    .release        = pipe_rdwr_release,
    .fasync         = pipe_rdwr_fasync,
};


There's a map in the kernel. You can register your own ioctl codes if you write a driver.

Edit: I wrote an ATA over Ethernet driver once and implemented a custom ioctl for tuning the driver at runtime.


A simplified explanation:

The file descriptor you pass to ioctl points to the inode structure that represents the device you are going to ioctl.

The inode structure contains the device number dev_t i_rdev, which is used as an index to find the device driver's file_operations structure. In this structure, there is a pointer to the ioctl function defined by the device driver.

You can read Linux Device Drivers, 3rd Edition for a more detailed explanation. It may be a bit outdated, but a good read nevertheless.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜