2015-12-28 3 views
2

Im работает над небольшим модулем ядра. Я пытаюсь использовать IOCTL (в ioctl_add), но я получаю ENOTTY, когда я его называю, который проверяется в коммутаторе, в нижней части основного. Код ниже. Кто-нибудь понял, что я делаю неправильно?Модуль ядра Linux - возврат использования IOCTL ENOTTY

user.c:

#include <stdio.h> 
#include <stdlib.h> 
#include <stdarg.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/ioctl.h> 
#include <linux/ioctl.h> 
#include <sys/stat.h> 
#include <sys/poll.h> 
#include <fcntl.h> 
#include <string.h> 
#include <errno.h> 

#define IOCTL_TYPE (100) 
#define IOCTL_ADD (_IO(IOCTL_TYPE, 1)) 

void cleanup() 
{ 
    if(f>=0) { 
    close(f); 
    } 
} 

int ioctl_add(int f) 
{ 
    int ret; 
    ret = ioctl(f, IOCTL_ADD); 
    printf("Add \n"); 
    return ret; 
} 

int main(int argc, char * argv[]) 
{ 

     int fd; 
     int *ptr; 
     fd = open(argv[1], O_RDWR); 

     if (fd < 0) { 
       perror("error"); 
     } 
     posix_memalign((void **)&ptr, 4096, 4096); 
     * ptr = atoi(argv[2]); 
     write(fd, ptr, 4096); 

     ioctl_add(fd); 

     printf("data is %d\n", *ptr); 

     close(fd); 

    switch(errno){ 
     case EBADF: 
     printf("errno: EBADF \n"); 
     break; 

     case EFAULT: 
     printf("errno: EFAULT \n"); 
     break; 

     case EINVAL: 
     printf("errno: EINVAL \n"); 
     break; 

     case ENOTTY: 
     printf("errno: ENOTTY \n"); 
     break; 

     default: 
     printf("errno: none \n"); 

     return 0; 
    } 

    return 0; 
} 

module.c:

#include <linux/kernel.h> 
#include <linux/module.h> 
#include <linux/spinlock.h> 
#include <linux/sched.h> 

#include <linux/device.h> 
#include <linux/fs.h> 
#include <linux/cdev.h> 
//#include <linux/mm.h> 
//#include <linux/config.h> 
#include <linux/ioport.h> 
#include <linux/interrupt.h> 
#include <linux/poll.h> 
#include <asm/io.h> 
#include <asm/bitops.h> 

#include <linux/ioctl.h> 
#define IOCTL_TYPE (100) 
#define IOCTL_ADD (_IO(IOCTL_TYPE, 1)) 

#include <linux/mm.h> 
#include <linux/pagemap.h> 


#define DEVICE_NAME "acc_priv" 
MODULE_LICENSE("GPL v2"); 

int ress, tmp; 
struct page *page; 
int *myaddr; 

ssize_t acc_read(struct file *filp, 
      char __user *buf, size_t count,loff_t * off) 
{ 
    printk (KERN_ALERT "Opened\n\r"); 
    return 0; 
} 

ssize_t acc_write(struct file *filp, 
      const char __user *buf, size_t count,loff_t * off) 
{ 

    printk (KERN_ALERT "Write\n\r"); 

    printk(KERN_INFO "%s\n", __FUNCTION__); 
    down_read(&current->mm->mmap_sem); 
    ress = get_user_pages(current, current->mm,(unsigned long)buf,1,1,1,&page,NULL); 
     if (ress) { 
       printk(KERN_INFO "Got mmaped.\n"); 
       myaddr = kmap(page); 
       printk(KERN_INFO "%d\n", *myaddr); 

       tmp = *myaddr; 

       tmp = tmp * 2; 
       printk(KERN_INFO "the result of multiplying: %d\n", tmp); 

       * myaddr = tmp; 
       page_cache_release(page); 
     } 
     up_read(&current->mm->mmap_sem); 
     return (0); 
} 


static int acc_open(struct inode *inode, 
       struct file *file) 
{ 
    printk(KERN_INFO "Opened inode:%p, file:%p\n", inode, file); 
    return 0; 
} 


long acc_ioctl(struct file *filp, 
     unsigned int cmd,unsigned long arg) 
{ 
     if(cmd == IOCTL_ADD) 
     printk(KERN_INFO "Do specified job \n"); 

     return 0; 
{ 

int acc_release(struct inode *inode, 
      struct file *file) 
{ 

    printk (KERN_INFO "device_release(%p,%p)\n", inode, file); 

    return 0; 
} 

struct file_operations Fops = { 
    .owner=THIS_MODULE, 
    .read=acc_read, 
    .write=acc_write, 
    .open=acc_open, 
    .unlocked_ioctl=acc_ioctl, 
    .release=acc_release, 
}; 

dev_t my_dev=0; 
struct cdev * my_cdev = NULL; 
static struct class *class_acc_priv = NULL; 


void clean_up(void) 
{ 

    if(my_dev && class_acc_priv) { 
    device_destroy(class_acc_priv,my_dev); 
    } 
    if(my_cdev) { 
    cdev_del(my_cdev); 
    my_cdev=NULL; 
    } 
    if(my_dev) { 
    unregister_chrdev_region(my_dev, 1); 
    } 
    if(class_acc_priv) { 
    class_destroy(class_acc_priv); 
    class_acc_priv=NULL; 
    } 
} 


int init_acc_priv(void) 
{ 
    int res=0; 
    res=alloc_chrdev_region(&my_dev, 0, 1, DEVICE_NAME); 
    if(res) { 
    printk (KERN_ALERT "Alocation of the device number for %s failed\n", 
      DEVICE_NAME); 
    return res; 
    }; 

    class_acc_priv = class_create(THIS_MODULE, "acc_class"); 
    if (IS_ERR(class_acc_priv)) { 
    printk(KERN_ERR "Error creating rs_class.\n"); 
    res=PTR_ERR(class_acc_priv); 
    goto err1; 
    } 

    my_cdev = cdev_alloc(); 
    my_cdev->ops = &Fops; 
    my_cdev->owner = THIS_MODULE; 
    res=cdev_add(my_cdev, my_dev, 1); 
    if(res) { 
    printk (KERN_ALERT "Registration of the device number for %s failed\n", 
      DEVICE_NAME); 
    res=-EFAULT; 
    goto err1; 
    }; 

    device_create(class_acc_priv,NULL,my_dev,NULL,"acc_priv%d",MINOR(my_dev)); 
    printk (KERN_ALERT "%s The major device number is %d.\n", 
     "Registeration is a success.", 
     MAJOR(my_dev)); 
    return res; 
err1: 
    clean_up(); 
    return res; 
} 
module_init(init_acc_priv); 


void cleanup_acc_priv(void) 
{ 
    clean_up(); 
} 
module_exit(cleanup_acc_priv); 
+0

Код в вашей тестовой программе не проверяет возвращаемое значение 'ioctl'. Возможно, что 'errno' задается некоторой предыдущей операцией, но' ioctl' сам возвращает 0: как и многие другие функции библиотеки C, 'ioctl' не очищает errno от успеха. Другая возможная проблема заключается в том, что вы используете 64-битное ядро, но 32-разрядный исполняемый файл. В этом случае вместо 'unlocked_ioctl' используется' compat_ioctl'. – Tsyvarev

+0

обе подсказки полезны, и compat_ioctl вместо unlocked_ioctl решил проблему. Большое спасибо –

+0

Функция: 'void cleanup()' никогда не вызывается, и даже если она была вызвана, она пытается использовать переменную 'f', но переменная не передается и не видна из этой функции. – user3629249

ответ

2

Когда приложение 32bit запускается на 64-битной ОС, он использует compat_ioctl системный вызов вместо unlocked_ioctl один для выполнения ioctl команды. Причина специального syscall заключается в том, что размер аргумента ioctl может отличаться для 64-битных и 32-битных приложений.

Таким образом, вам необходимо реализовать файл .compat_ioctl.

Смежные вопросы