How to Use Ioctl() to Manipulate My Kernel Module

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

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.

IOCTL from kernel space

You can use ioctl from the kernel space too.

Because ioctl command RNDADDENTROPY is file-specific, its processing should be implemented in the .unlocked_ioctl operation for /dev/random file (and it is actually implemented this way, see function random_ioctl).

For file-specific ioctl commands you may call .unlocked_ioctl file's operation directly:

// Open file
struct file* f = filp_open("/dev/random", O_WRONLY, 0);
// Replace user space with kernel space
mm_segment_t old_fs = get_fs();
set_fs(KERNEL_DS);

f->f_op->unlocked_ioctl(f, RNDADDENTROPY, entropy);

// Restore space
set_fs(old_fs);

// Close file
filp_close(f, 0);

Using ioctl communication between Kernel mode and user mode

In newer kernels, the preferred way is to use .unlocked_ioctl or .compat_ioctl fields. The plain .ioctl was removed from struct file_operations. This discussion may clarify what happened and how to deal with that.

How to use ioctl() from kernel space in Linux?

You can try to call sys_ioctl.

It's exported if the kernel is compiled with CONFIG_COMPAT.

Or, if you have the device driver's struct file_operations, you can call its ioctl handler directly.

However, the ioctl handle would expect pointer parameters to be in the address space of the process currently running, not in the kernel address space. copy_from_user would be used to read them. If you give pointers to the kernel address space, copy_from_user will fail. I don't see how you would get around this.

Edit:

If you will call ioctl handler between below code than copy_from_user will not ever fail.

 mm_segment_t fs;

fs = get_fs(); /* save previous value */
set_fs (get_ds()); /* use kernel limit */

/* system calls can be invoked */

set_fs(fs); /* restore before returning to user space */

Working with GPIOs in kernel module IOCTLs

If you read the linux source at the warning, it tells you:

* This function should be called from contexts where we cannot sleep, and will
* complain if the GPIO chip functions potentially sleep.
WARN_ON(desc->gdev->chip->can_sleep);

You should be calling gpio_get_value_cansleep

What options do we have for communication between a user program and a Linux Kernel Module?

Your option 3) is really a sub-option of option 1) - ioctl() is one way of interacting with a device file (read() and write() being the usual ways).

Two other ways worth considering are:

  • The sysfs filesystem;
  • Netlink sockets.

IOCTL Linux device driver

An ioctl, which means "input-output control" is a kind of device-specific system call. There are only a few system calls in Linux (300-400), which are not enough to express all the unique functions devices may have. So a driver can define an ioctl which allows a userspace application to send it orders. However, ioctls are not very flexible and tend to get a bit cluttered (dozens of "magic numbers" which just work... or not), and can also be insecure, as you pass a buffer into the kernel - bad handling can break things easily.

An alternative is the sysfs interface, where you set up a file under /sys/ and read/write that to get information from and to the driver. An example of how to set this up:

static ssize_t mydrvr_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", DRIVER_RELEASE);
}

static DEVICE_ATTR(version, S_IRUGO, mydrvr_version_show, NULL);

And during driver setup:

device_create_file(dev, &dev_attr_version);

You would then have a file for your device in /sys/, for example, /sys/block/myblk/version for a block driver.

Another method for heavier use is netlink, which is an IPC (inter-process communication) method to talk to your driver over a BSD socket interface. This is used, for example, by the WiFi drivers. You then communicate with it from userspace using the libnl or libnl3 libraries.



Related Topics



Leave a reply



Submit