Question

Je suis en train d'écrire un module de noyau qui utilise le fichier linux / timer.h. Je suis arrivé à travailler à l'intérieur tout le module, et maintenant je suis en train de le faire fonctionner à partir d'un programme utilisateur.

Voici mon module de noyau:

//Necessary Includes For Device Drivers.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/timer.h>
#include <linux/ioctl.h>

#define DEVICE_NAME "mytimer"
#define DEVICE_FILE_NAME "mytimer"
#define MAJOR_NUM 61
#define MINOR_NUM 0

MODULE_LICENSE("Dual BSD/GPL");

static struct timer_list my_timer;

struct file_operations FileOps = 
{
    //No File Operations for this timer.
};

//Function to perform when timer expires.
void TimerExpire(int data)
{
    printk("Timer Data: %d\n", data);
}

//Function to set up timers.
void TimerSetup(void)
{
    setup_timer(&my_timer, TimerExpire, 5678);
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(5000));
}

//Module Init and Exit Functions.
int init_module(void)
{
    int initResult = register_chrdev(MAJOR_NUM, "mytimer", &FileOps);

    if (initResult < 0)
    {
        printk("Cannot obtain major number %d\n", MAJOR_NUM);

        return initResult;
    }

printk("Loading MyTimer Kernel Module...\n");


return 0;
}
void cleanup_module(void)
{
    unregister_chrdev(MAJOR_NUM, "mytimer");
    printk("Unloading MyTimer Kernel Module...\n");
}

Plus précisément, je veux que mon programme utilisateur pour appeler la fonction TimerSetup (). Je sais que je vais devoir utiliser ioctl () mais je ne suis pas sûr de savoir comment spécifier dans mon fichier module TimerSetup () doit être appelable via ioctl ().

En outre, ma deuxième question: j'ai pu insmod mon module et aussi mknod dans / dev / myTimer avec le nombre important correct. Mais quand j'ai essayé d'ouvrir () pour que je puisse obtenir le descripteur de fichier, il revenait sans cesse -1, ce qui je suppose est faux. Je me suis assuré les autorisations étaient très bien (en fait, je l'ai fait 777 juste pour être sûr) ... Il ne fonctionne toujours pas ... Y at-il quelque chose que je suis absent?

Voici le programme utilisateur au cas où:

#include <stdio.h>

int main(int argc, char* argv[])
{
    int fd = open("/dev/mytimer", "r");
    printf("fd: %d\n", fd);

    return 0;
}
Était-ce utile?

La solution

Le code exemple, vous avez besoin peut être trouvé dans drivers/watchdog/softdog.c (de Linux 2.6.33 au moment cela a été écrit), qui illustre les opérations de fichiers appropriés ainsi que la façon de permettre userland pour remplir une structure avec ioctl ().

Il est en fait un grand, tutoriel de travail pour tous ceux qui ont besoin d'écrire des pilotes de périphériques de caractère trivial.

J'ai disséqué l'interface ioctl de softdog quand répondre à ma propre question , ce qui peut être utile vous.

Voici l'essentiel de celui-ci (bien loin d'être exhaustive) ...

Dans softdog_ioctl() vous voyez une initialisation simple watchdog_info struct qui permet de diffuser des informations fonctionnalités, la version et le dispositif:

    static const struct watchdog_info ident = {
            .options =              WDIOF_SETTIMEOUT |
                                    WDIOF_KEEPALIVEPING |
                                    WDIOF_MAGICCLOSE,
            .firmware_version =     0,
            .identity =             "Software Watchdog",
    };

Nous examinons ensuite un simple cas où l'utilisateur veut juste obtenir ces capacités:

    switch (cmd) {
    case WDIOC_GETSUPPORT:
            return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;

... qui bien sûr, remplira le correspondant watchdog_info userspace avec les valeurs initialisées ci-dessus. Si copy_to_user () échoue, -EFAULT est retourné qui provoque l'appel à retourner -1 avec un errno significatif étant réglé ioctl () de l'espace utilisateur correspondant.

Note, les demandes magiques sont réellement définis dans linux / watchdog.h, de sorte que le noyau et l'espace utilisateur de les partager:

#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 signifiant évidemment "chien de garde ioctl"

Vous pouvez facilement prendre un peu plus loin, d'avoir votre pilote faire quelque chose et placez le résultat de ce quelque chose dans la structure et le copier dans l'espace utilisateur. Par exemple, si watchdog_info struct avait aussi un __u32 result_code membre. Remarque, __u32 est juste la version de uint32_t du noyau.

Avec ioctl (), l'utilisateur passe l'adresse d'un objet, que ce soit une structure, nombre entier, quel que soit le noyau attendant le noyau d'écrire sa réponse dans un objet identique et copier les résultats à l'adresse fournie.

La deuxième chose que vous allez avoir besoin de faire est assurez-vous que votre appareil sait ce qu'il faut faire quand quelqu'un ouvre, lit de lui, écrit, ou utilise un crochet comme ioctl (), que vous pouvez facilement voir en étudiant softdog.

Il est intéressant:

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,
};

Si vous voyez le gestionnaire de unlocked_ioctl va ... vous l'aurez deviné, softdog_ioctl ().

Je pense que vous pourriez être juxtaposer une couche de complexité qui n'existe pas vraiment lorsqu'ils traitent avec ioctl (), il est vraiment simple. Pour cette même raison, la plupart des développeurs du noyau désapprouvent nouvelles interfaces ioctl ajoutés à moins qu'ils ne sont absolument nécessaires. Son trop facile de perdre du type ioctl () va remplir vs la magie que vous utilisez pour le faire, ce qui est la principale raison pour laquelle copy_to_user () ne se traduit souvent dans le noyau en décomposition avec des hordes de processus userspace coincés dans le sommeil de disque.

Pour une minuterie, je suis d'accord, ioctl () est le plus court chemin à la santé mentale.

Autres conseils

Il vous manque un pointeur de fonction .open dans votre structure file_operations pour spécifier la fonction à appeler lorsqu'un processus tente d'ouvrir le fichier de périphérique. Vous devez spécifier un pointeur de fonction .ioctl pour votre fonction ioctl ainsi.

Essayez de lire à travers Le Guide de programmation du module du noyau Linux , en particulier les chapitres 4 (Dispositif de caractères Files) et 7 (Parler aux fichiers de périphérique).

Chapitre 4 présente la structure de file_operations, qui contient des pointeurs aux fonctions définies par le module / pilote qui exécutent diverses opérations telles que open ou ioctl.

Chapitre 7 fournit des informations sur la communication avec un module / conduire via ioctls.

pilotes de périphériques Linux, troisième édition est une autre bonne ressource.

Exemple minimal runnable

Testés dans un environnement QEMU + Buildroot entièrement reproductible, donc pourrait aider d'autres à leur travail de ioctl. en amont GitHub: Module noyau | tête partagé | userland.

La partie la plus ennuyeux a été entendu que certains bas ids sont détournés: ioctl pas appelé si cmd = 2 , vous devez utiliser des macros _IOx.

module de 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ée:

#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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top