3 votes

Problème avec ioctl() dans un module noyau simple

J'essaie de construire un module de noyau simple. Voici le contenu des fichiers impliqués dans ce module :

module.c :

    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <linux/kernel.h>
    #include "header.h"

    static int device_open(struct inode *inode, struct file *file)
    {
    printk("\n Open \n");
    return 0;
    }

    static int device_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long args)
    {
    switch(cmd)
    {
    case IOCTL_CMD:
    printk(KERN_ALERT "\n %s \n", (char *)args);
    break;
    }
    return 0;
    }

    static int device_release(struct inode *inode, struct file *file)
    {
    printk("\n Release \n");
    return 0;
    }

    static struct class *my_class;

    static struct file_operations fops={
    .open = device_open,
    .release = device_release,
    .compat_ioctl = device_ioctl
    };

    static int hello_init(void)
    {
    major_no = register_chrdev(0, DEVICE_NAME, &fops);
    printk("\n Major_no : %d", major_no);

    my_class = class_create(THIS_MODULE, DEVICE_NAME);
    device_create(my_class, NULL, MKDEV(major_no,0), NULL, DEVICE_NAME);
    printk("\n Device Initialized in kernel ....!!!");
    return 0;
    }

    static void hello_exit(void)
    {
    printk("\n Device is Released or closed \n");
    device_destroy(my_class,MKDEV(major_no,0));
    class_unregister(my_class);
    class_destroy(my_class);
    unregister_chrdev(major_no, DEVICE_NAME);
    printk("\n===============================================================\n");
    }

    module_init(hello_init);
    module_exit(hello_exit);

    MODULE_LICENSE("GPL");

appln.c

    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>

    #include "header.h"

    int main()
    {
    int fd;
    char * msg = "yahoooo";
    fd = open(DEVICE_PATH, O_RDWR);
    ioctl(fd, IOCTL_CMD, msg);
    printf("ioctl executed\n");
    close(fd);
    return 0;
    }

header.h :

    #include <linux/ioctl.h>
    #include <linux/kdev_t.h> /* for MKDEV */

    #define DEVICE_NAME "my_dev"
    #define DEVICE_PATH "/dev/my_dev"
    #define WRITE 0
    static int major_no;
    #define MAGIC_NO '4'
    /* 
     * Set the message of the device driver 
     */
    #define IOCTL_CMD _IOR(MAGIC_NO, 0, char *)

Mon module se charge parfaitement (je peux voir le mesg dans la fonction hello_init()). Mais quand j'exécute le programme appln.c, même quand il fait l'appel ioctl(), je ne vois aucun résultat. Quelqu'un peut-il me dire pourquoi le module ignore mon appel ioctl ?

Merci,

3voto

Brian Childress Points 437

Quelques trucs :

  • Vous devez utiliser "unlocked_ioctl" et non "compat_ioctl".

  • L'interface de la fonction "device_ioctl" est erronée (cf. include/linux/fs.h ), il devrait l'être :

        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
  • Le fichier appln.c ne vérifie pas les codes d'erreur ( Ouvrir le site , ioctl ).

Après avoir corrigé cela, le code fonctionnera bien.

1voto

Exemple minimal exécutable

Testé dans un environnement QEMU + Buildroot entièrement reproductible, ce qui pourrait aider d'autres personnes à obtenir leurs propres résultats. ioctl de travail. GitHub en amont : module noyau | en-tête partagé | userland .

La partie la plus ennuyeuse a été de comprendre que certains identifiants bas sont détournés : https://stackoverflow.com/questions/10071296/ioctl-is-not-called-if-cmd-2 vous devez utiliser _IOx macros.

Module du noyau :

#include <asm/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/printk.h> /* printk */

#include "ioctl.h"

MODULE_LICENSE("GPL");

static struct dentry *dir;

static long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long argp)
{
    void __user *arg_user;
    union {
        int i;
        lkmc_ioctl_struct s;
    } arg_kernel;

    arg_user = (void __user *)argp;
    pr_info("cmd = %x\n", cmd);
    switch (cmd) {
        case LKMC_IOCTL_INC:
            if (copy_from_user(&arg_kernel.i, arg_user, sizeof(arg_kernel.i))) {
                return -EFAULT;
            }
            pr_info("0 arg = %d\n", arg_kernel.i);
            arg_kernel.i += 1;
            if (copy_to_user(arg_user, &arg_kernel.i, sizeof(arg_kernel.i))) {
                return -EFAULT;
            }
        break;
        case LKMC_IOCTL_INC_DEC:
            if (copy_from_user(&arg_kernel.s, arg_user, sizeof(arg_kernel.s))) {
                return -EFAULT;
            }
            pr_info("1 arg = %d %d\n", arg_kernel.s.i, arg_kernel.s.j);
            arg_kernel.s.i += 1;
            arg_kernel.s.j -= 1;
            if (copy_to_user(arg_user, &arg_kernel.s, sizeof(arg_kernel.s))) {
                return -EFAULT;
            }
        break;
        default:
            return -EINVAL;
        break;
    }
    return 0;
}

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = unlocked_ioctl
};

static int myinit(void)
{
    dir = debugfs_create_dir("lkmc_ioctl", 0);
    /* ioctl permissions are not automatically restricted by rwx as for read / write,
     * but we could of course implement that ourselves:
     * https://stackoverflow.com/questions/29891803/user-permission-check-on-ioctl-command */
    debugfs_create_file("f", 0, dir, NULL, &fops);
    return 0;
}

static void myexit(void)
{
    debugfs_remove_recursive(dir);
}

module_init(myinit)
module_exit(myexit)

En-tête partagé :

#ifndef IOCTL_H
#define IOCTL_H

#include <linux/ioctl.h>

typedef struct {
    int i;
    int j;
} lkmc_ioctl_struct;
#define LKMC_IOCTL_MAGIC 0x33
#define LKMC_IOCTL_INC     _IOWR(LKMC_IOCTL_MAGIC, 0, int)
#define LKMC_IOCTL_INC_DEC _IOWR(LKMC_IOCTL_MAGIC, 1, lkmc_ioctl_struct)

#endif

Userland :

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "../ioctl.h"

int main(int argc, char **argv)
{
    int fd, arg_int, ret;
    lkmc_ioctl_struct arg_struct;

    if (argc < 2) {
        puts("Usage: ./prog <ioctl-file>");
        return EXIT_FAILURE;
    }
    fd = open(argv[1], O_RDONLY);
    if (fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }
    /* 0 */
    {
        arg_int = 1;
        ret = ioctl(fd, LKMC_IOCTL_INC, &arg_int);
        if (ret == -1) {
            perror("ioctl");
            return EXIT_FAILURE;
        }
        printf("arg = %d\n", arg_int);
        printf("ret = %d\n", ret);
        printf("errno = %d\n", errno);
    }
    puts("");
    /* 1 */
    {
        arg_struct.i = 1;
        arg_struct.j = 1;
        ret = ioctl(fd, LKMC_IOCTL_INC_DEC, &arg_struct);
        if (ret == -1) {
            perror("ioctl");
            return EXIT_FAILURE;
        }
        printf("arg = %d %d\n", arg_struct.i, arg_struct.j);
        printf("ret = %d\n", ret);
        printf("errno = %d\n", errno);
    }
    close(fd);
    return EXIT_SUCCESS;
}

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X