2015-12-28 7 views
2

Je travaille sur un petit module de noyau. Im essayant d'utiliser IOCTL (dans ioctl_add), mais je reçois ENOTTY quand je l'appelle, qui est vérifié dans le commutateur, sur le bas de la principale. Le code est ci-dessous. Quelqu'un at-il une idée de ce que je fais mal?Module de noyau Linux - L'utilisation de l'IOCTL renvoie 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

Le code de votre programme de test ne vérifie pas la valeur de retour de 'ioctl'. Il est possible que 'errno' soit défini par une opération précédente, mais' ioctl' renvoie lui-même 0: comme beaucoup d'autres fonctions de la bibliothèque C, 'ioctl' n'efface pas errno en cas de succès. Un autre problème possible, que vous utilisez un noyau 64 bits mais un exécutable 32 bits. Dans ce cas, 'compat_ioctl' est utilisé à la place de' unlocked_ioctl'. – Tsyvarev

+0

les deux conseils sont utiles, et compat_ioctl au lieu de unlocked_ioctl a résolu le problème. merci beaucoup –

+0

la fonction: 'void cleanup()' n'est jamais appelée, et même si elle a été appelée, elle essaie d'utiliser la variable 'f', mais la variable n'est pas transmise et n'est pas visible depuis cette fonction. – user3629249

Répondre

2

Lorsque l'application 32 bits est exécuté sur OS 64 bits, il utilise compat_ioctl syscall au lieu de unlocked_ioctl un pour effectuer la commande ioctl. La raison d'un syscall spécial est que la taille de l'argument ioctl peut différer pour les applications 64 bits et 32 ​​bits.

Vous devez donc implémenter le fonctionnement du fichier .compat_ioctl.