Linux Kernel Module/IOCTL: inappropriate ioctl for device
Solution 1
Okay. So. Here's the solution.
In Linux kernel 2.6.x the declaration for _ioctl calls changed from
static long wait_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
To:
static long wait_ioctl(struct file *, unsigned int, unsigned long);
The fix is thus:
...
static long wait_ioctl(struct file *, unsigned int, unsigned long);
...
static long wait_ioctl(struct file *file, unsigned int cmd, unsigned long sub_cmd){
if(_IOC_TYPE(cmd) != WAIT_DEVICE_MAGIC){
printk(KERN_INFO "[wait device] invalid magic number: %u:%u:%u", _IOC_TYPE(cmd), cmd, WAIT_DEVICE_MAGIC);
return -ENOTTY;
}
....
Solution 2
.compat_ioctl
Also make sure to implement this file_operation
if you are making 32-bit calls to a 64-bit kernel.
The symptom is that your ioctl
handler is never run.
Related videos on Youtube
Tyler
Updated on June 04, 2022Comments
-
Tyler almost 2 years
I am in the process of writing a Linux Kernel Module (LKM) serving as a pseudo-driver - I am unable to figure out how to make IOCTL calls between the LKM (wait.c) and the user-level program (user.c).
The magic number for the device driver is
0xBF
- the LKM does not communicate with a physical block/char device, it is simply an exercise. From what I can tell, the IOCTL call toKERN_IOCTL_CREATE_EVENT
is not formatted properly & the magic number is incorrect.The IOCTL call that I am attempting to use is:
#include <sys/ioctl.h> #define KERN_IOCTL_CREATE_EVENT _IOWR(WAIT_DEVICE_MAGIC, 1, int) int main(){ int ret; int fd; fd = open("/dev/wait", 0); if(fd < 0){ return -1; } ret = ioctl(fd, KERN_IOCTL_CREATE_EVENT, 0);
Error:
[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
The user-mode application can open/close a file descriptor pointing to the device:
/dev/wait
but thecase
/switch
statement isn't accepting the IOCTL call. Any suggestions?Here is the output of
# uname -a
Linux vagrant-ubuntu-trusty-64 3.13.11.11+ #1 SMP Mon Dec 1 20:50:23 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
wait.c
#include <linux/miscdevice.h> #include <linux/moduleparam.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/string.h> #include <asm/uaccess.h> #include <linux/sched.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/init.h> #include <linux/wait.h> #include <linux/fs.h> #include "wait.h" MODULE_LICENSE("GPL"); MODULE_AUTHOR("Tyler Fisher <[email protected]>"); MODULE_DESCRIPTION("In-kernel wait queue"); static unsigned long event_table_size = 50; module_param(event_table_size, ulong, (S_IRUSR | S_IRGRP | S_IROTH)); MODULE_PARM_DESC(event_table_size, "Size of event table (i.e. how many processes can be blocking)"); /* IOCTL function headers */ static int wait_open(struct inode *, struct file *); static int wait_close(struct inode *, struct file *); static long wait_ioctl(struct inode *, struct file *, unsigned int, unsigned long); /* other function headers */ static long event_open(int event_id); /* file operations */ static struct file_operations wait_fops = { .owner = THIS_MODULE, .open = wait_open, .release = wait_close, .llseek = noop_llseek, .unlocked_ioctl = wait_ioctl }; /* device handler */ static struct miscdevice wait_misc_device = { .minor = MISC_DYNAMIC_MINOR, .name = WAIT_DEVICE_NAME, .fops = &wait_fops }; /* open wait device */ static int wait_open(struct inode *inode, struct file *file){ dev_t node = iminor(inode); if(MINOR(node) != WAIT_DEVICE_MINOR){ return -ENODEV; } return 0; } static int wait_close(struct inode *inode, struct file *file){ return 0; } static long wait_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long sub_cmd){ switch(cmd){ case KERN_IOCTL_CREATE_EVENT: printk(KERN_INFO "[wait device]: create event %lu\n", sub_cmd); return event_open(sub_cmd); default: return -ENOENT; } } static long event_open(int id){ return 0; } static long __init wait_init(void){ if(misc_register(&wait_misc_device) < 0){ printk(KERN_ERR "[wait device] failed to register device\n"); return -1; } printk(KERN_INFO "[wait device] has been registered\n"); return 0; } static void __exit wait_exit(void){ misc_deregister(&wait_misc_device); printk(KERN_INFO "[wait device] has been unregistered\n"); } module_init(wait_init); module_exit(wait_exit);
wait.h
#include <linux/ioctl.h> #define WAIT_DEVICE_NAME "wait" #define WAIT_DEVICE_MAGIC 0xBF #define WAIT_DEVICE_MAJOR 200 #define WAIT_DEVICE_MINOR 0 #define KERN_IOCTL_CREATE_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x01, int) #define MAX_WAITING 5
The test program for the IOCTL calls:
user.c
#include <sys/ioctl.h> #include <fcntl.h> #include <stdio.h> #define WAIT_DEVICE_MAGIC 0xBF #define KERN_IOCTL_CREATE_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x01, int) #define KERN_IOCTL_DESTROY_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x02, int) #define KERN_IOCTL_LOCK_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x03, int) #define KERN_IOCTL_UNLOCK_EVENT _IOWR(WAIT_DEVICE_MAGIC, 0x04, int) int main(){ int fd; if(fd = open("/dev/wait", O_RDWR) < 0){ perror("failed to open /dev/wait"); return -1; } /* test IOCTL: event creation */ if(ioctl(fd, KERN_IOCTL_CREATE_EVENT, 0) < 0){ perror("[fail]: KERN_IOCTL_CREATE_EVENT"); return -1; } return 0; }
Makefile
obj-m += wait.o CFLAGS_wait.o += -DDEBUG all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
In order to test out the LKM - clear dmesg, compile & execute user.c w/GCC:
# dmesg -c > /dev/null 2>&1 # make # rmmod wait.ko # insmod wait.ko # gcc user.c -o user && ./user
The amount of debugging errors is embarassing. I feel bad for sharing this - and realize that this may cause the issue to be closed/downvoted into oblivion.
# sh test.sh [+] cleared dmesg make -C /lib/modules/3.13.11.11+/build M=/home/vagrant/PROG40000-kernel-synchronization modules make[1]: Entering directory `/home/vagrant/ubuntu-trusty' CC [M] /home/vagrant/PROG40000-kernel-synchronization/wait.o /home/vagrant/PROG40000-kernel-synchronization/wait.c:61:1: warning: initialization from incompatible pointer type [enabled by default] }; ^ /home/vagrant/PROG40000-kernel-synchronization/wait.c:61:1: warning: (near initialization for ‘wait_fops.unlocked_ioctl’) [enabled by default] In file included from include/linux/moduleparam.h:4:0, from /home/vagrant/PROG40000-kernel-synchronization/wait.c:11: /home/vagrant/PROG40000-kernel-synchronization/wait.c: In function ‘__inittest’: include/linux/init.h:297:4: warning: return from incompatible pointer type [enabled by default] { return initfn; } \ ^ /home/vagrant/PROG40000-kernel-synchronization/wait.c:167:1: note: in expansion of macro ‘module_init’ module_init(wait_init); ^ Building modules, stage 2. MODPOST 1 modules CC /home/vagrant/PROG40000-kernel-synchronization/wait.mod.o LD [M] /home/vagrant/PROG40000-kernel-synchronization/wait.ko make[1]: Leaving directory `/home/vagrant/ubuntu-trusty' [--dmesg--] [13112.810008] [wait device] has been unregistered [13112.819049] [wait device] has been registered [-/dmesg--] [+] compiled user-mode program ----- [fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device [fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device [+] executed user-mode program ----- [--dmesg--] [13112.810008] [wait device] has been unregistered [13112.819049] [wait device] has been registered [13112.893049] SOMEONE DARE READ FROM ME!? [13112.893057] [wait device] invalid magic number: 0:0:191 [13112.893535] [wait device] invalid magic number: 0:0:191 [-/dmesg--]
-
askb over 9 yearsYou may need to install your updated kernel headers. Without this the user space program is unable to locate the correct ioctl, for some reason I had a similar issue on Ubuntu and the only thing that worked was installing the updated kernel headers.
-
Tyler over 9 yearsI updated the kernel using sudo make modules_install install -- and have the linux-headers-generic package installed -- current kernel version is 3.13.11.11+
-