Frage

Also ich versuche, eine Kernel-Modul zu schreiben, die die Linux / timer.h-Datei verwendet. Ich habe es innerhalb nur das Modul zu arbeiten, und jetzt versuche ich, es von einem Anwenderprogramm zur Arbeit zu kommen.

Hier ist mein Kernel-Modul:

//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");
}

Insbesondere möchte ich meine Anwenderprogramm die TimerSetup () Funktion aufzurufen. Ich weiß, dass ich brauche ioctl () zu verwenden, aber ich bin nicht sicher, wie in meiner Modul-Datei angeben, dass TimerSetup () soll aufrufbar über ioctl ().

Auch meine zweite Frage: Ich konnte meinen Modul insmod und auch mknod in / dev / MyTimer mit der richtigen Hauptnummer. Aber als ich versuchte zu öffnen () ist es so, dass ich den Dateideskriptor von ihm erhalten kann, ist es gehalten Rückkehr -1, ich gehe davon aus falsch ist. Ich habe die Berechtigungen sicher waren in Ordnung (in der Tat, ich habe es 777 nur um sicher zu sein) ... Es funktioniert immer noch nicht ... Gibt es etwas, was ich vermisst habe?

Hier ist das Anwenderprogramm für alle Fälle:

#include <stdio.h>

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

    return 0;
}
War es hilfreich?

Lösung

Der Beispielcode Sie müssen in drivers/watchdog/softdog.c gefunden werden können (von Linux 2.6.33 zum Zeitpunkt des geschrieben wurde), die auch die richtigen Dateioperationen zeigen, wie man Userland zu ermöglichen, eine Struktur mit ioctl () zu füllen.

Es ist eigentlich ein großes, Arbeits Tutorial für jeden, der trivial Zeichengerätetreiber schreiben muss.

I seziert softdog der ioctl-Schnittstelle, wenn meine eigene Frage zu beantworten , die hilfreich sein können Sie.

Hier ist der Kern von ihm (wenn auch bei weitem nicht erschöpfend) ...

In softdog_ioctl() sehen Sie eine einfache Initialisierung von struct watchdog_info dieser wirbt Funktionalität, Version und Geräteinformationen:

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

Wir freuen dann in einem einfachen Fall, dass der Benutzer nur will diese Fähigkeiten erhalten:

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

... was natürlich den entsprechenden User-Space watchdog_info mit den initialisierten Werten oben füllen. Wenn copy_to_user () fehlschlägt, -EFAULT zurückgegeben, die den entsprechenden User-Space ioctl () Aufruf bewirkt, -1 zurück mit einem aussagekräftigen errno eingestellt wird.

Beachten Sie, sind die magischen Anfragen tatsächlich definiert in linux / watchdog.h, so dass der Kernel und Userspace-Anteil ihnen:

#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 offensichtlich bedeutet "Watchdog ioctl"

Sie können leicht nehmen, dass ein Schritt weiter, mit Ihrem Fahrer etwas tun, und das Ergebnis dieser etwas in der Struktur platzieren und kopieren Sie sie in User-Space. Zum Beispiel, wenn struct watchdog_info hatte auch ein Mitglied __u32 result_code. Beachten Sie, __u32 ist nur der Kernel-Version von uint32_t.

Mit ioctl (), der Benutzer übergibt die Adresse eines Objekts, sei es eine Struktur, integer, was auch immer an den Kernel den Kernel erwartet seine Antwort in einem identischen Objekt zu schreiben und kopieren Sie die Ergebnisse an die Adresse, die zur Verfügung gestellt wurde.

Die zweite Sache, die Sie gehen zu müssen, tun, stellen Sie sicher, Ihr Gerät weiß, was zu tun ist, wenn jemand öffnet, liest von ihm, schreibt sie, oder verwendet einen Haken wie ioctl (), die Sie leicht durch das Studium sehen softdog.

Von Interesse ist:

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

Wenn Sie sehen die unlocked_ioctl Handler gehen ... Sie ahnen es, softdog_ioctl ().

Ich glaube, Sie könnten eine Schicht von Komplexität werden Nebeneinanderstellen, die wirklich existiert nicht, wenn sie mit ioctl () zu tun, es ist wirklich so einfach. Aus demselben Grund, die Stirn runzeln die meisten Kernel-Entwickler auf neue ioctl-Schnittstellen hinzugefügt werden, wenn sie nicht unbedingt erforderlich sind. Es ist einfach zu leicht zu verlieren Spur von der Art, dass ioctl () vs der Magie Sie es tun verwenden füllen wird, die der Hauptgrund ist, dass copy_to_user () steckte mit Horden von Fäulnis User-Space Prozessen im Kernel führt oft fehl Platten schlafen.

Für einen Timer, ich bin einverstanden, ioctl () ist der kürzeste Weg zur Vernunft.

Andere Tipps

Sie sind ein .open Funktionszeiger in Ihrer file_operations Struktur fehlen die Funktion angeben, die aufgerufen werden, wenn ein Prozess versucht, die Gerätedatei zu öffnen. Sie erhalten einen .ioctl Funktionszeiger für Ihre ioctl-Funktion als auch angeben.

Versuchen Sie, durch das Lesen Der Linux-Kernel-Modul-Programmieranleitung , speziell Kapitel 4 (Rolle Gerätedateien) und 7 (Im Gespräch mit Gerätedateien).

Kapitel 4 führt die file_operations Struktur, die Zeiger hält zu Funktionen, die von dem Modul / Treiber definiert, die verschiedenen Operationen wie open oder ioctl zuführen.

Kapitel 7 Informationen bietet mit einem Modul auf der Kommunikation / Antrieb über ioctls.

Linux Device Drivers, Third Edition eine weitere gute Ressource ist.

Minimal runnable Beispiel

Getestet in einer vollständig reproduzierbar QEMU + Buildroot Umgebung, so könnte helfen, andere bekommen ihre ioctl Arbeiten. GitHub Upstream: Kernel-Modul | gemeinsamen Header | Userland .

Das nervigste Teil wurde zu verstehen, dass einige niedrige ids entführt werden: ioctl ist nicht genannt, wenn cmd = 2 , müssen Sie _IOx Makros verwenden.

Kernel-Modul:

#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)

Shared-Header:

#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;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top