How do I use ioctl() to manipulate my kernel module?

后端 未结 3 1124
别那么骄傲
别那么骄傲 2020-12-08 03:45

So I\'m trying to write a kernel module that uses the linux/timer.h file. I got it to work inside just the module, and now I am trying to get it to work from a user program.

3条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-12-08 04:10

    The example code you need can be found in drivers/watchdog/softdog.c (from Linux 2.6.33 at the time this was written), which illustrates proper file operations as well as how to permit userland to fill a structure with ioctl().

    It's actually a great, working tutorial for anyone who needs to write trivial character device drivers.

    I dissected softdog's ioctl interface when answering my own question, which may be helpful to you.

    Here's the gist of it (though far from exhaustive) ...

    In softdog_ioctl() you see a simple initialization of struct watchdog_info that advertises functionality, version and device information:

        static const struct watchdog_info ident = {
                .options =              WDIOF_SETTIMEOUT |
                                        WDIOF_KEEPALIVEPING |
                                        WDIOF_MAGICCLOSE,
                .firmware_version =     0,
                .identity =             "Software Watchdog",
        };
    

    We then look at a simple case where the user just wants to obtain these capabilities:

        switch (cmd) {
        case WDIOC_GETSUPPORT:
                return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
    

    ... which of course, will fill the corresponding userspace watchdog_info with the initialized values above. If copy_to_user() fails, -EFAULT is returned which causes the corresponding userspace ioctl() call to return -1 with a meaningful errno being set.

    Note, the magic requests are actually defined in linux/watchdog.h , so that the kernel and userspace share them:

    #define WDIOC_GETSUPPORT        _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
    #define WDIOC_GETSTATUS         _IOR(WATCHDOG_IOCTL_BASE, 1, int)
    #define WDIOC_GETBOOTSTATUS     _IOR(WATCHDOG_IOCTL_BASE, 2, int)
    #define WDIOC_GETTEMP           _IOR(WATCHDOG_IOCTL_BASE, 3, int)
    #define WDIOC_SETOPTIONS        _IOR(WATCHDOG_IOCTL_BASE, 4, int)
    #define WDIOC_KEEPALIVE         _IOR(WATCHDOG_IOCTL_BASE, 5, int)
    #define WDIOC_SETTIMEOUT        _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
    #define WDIOC_GETTIMEOUT        _IOR(WATCHDOG_IOCTL_BASE, 7, int)
    #define WDIOC_SETPRETIMEOUT     _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
    #define WDIOC_GETPRETIMEOUT     _IOR(WATCHDOG_IOCTL_BASE, 9, int)
    #define WDIOC_GETTIMELEFT       _IOR(WATCHDOG_IOCTL_BASE, 10, int)
    

    WDIOC obviously signifying "Watchdog ioctl"

    You can easily take that a step further, having your driver do something and place the result of that something in the structure and copy it to userspace. For instance, if struct watchdog_info also had a member __u32 result_code. Note, __u32 is just the kernel's version of uint32_t.

    With ioctl(), the user passes the address of an object, be it a structure, integer, whatever to the kernel expecting the kernel to write its reply in an identical object and copy the results to the address that was provided.

    The second thing you are going to need to do is make sure your device knows what to do when someone opens, reads from it, writes to it, or uses a hook like ioctl(), which you can easily see by studying softdog.

    Of interest is:

    static const struct file_operations softdog_fops = {
            .owner          = THIS_MODULE,
            .llseek         = no_llseek,
            .write          = softdog_write,
            .unlocked_ioctl = softdog_ioctl,
            .open           = softdog_open,
            .release        = softdog_release,
    };
    

    Where you see the unlocked_ioctl handler going to ... you guessed it, softdog_ioctl().

    I think you might be juxtaposing a layer of complexity that really doesn't exist when dealing with ioctl(), it really is that simple. For that same reason, most kernel developers frown on new ioctl interfaces being added unless they are absolutely necessary. Its just too easy to lose track of the type that ioctl() is going to fill vs the magic you use to do it, which is the primary reason that copy_to_user() fails often resulting in the kernel rotting with hordes of userspace processes stuck in disk sleep.

    For a timer, I agree, ioctl() is the shortest path to sanity.

提交回复
热议问题