Domanda

Così sto cercando di scrivere un modulo del kernel che utilizza il file linux / timer.h. Ho preso a lavorare all'interno solo il modulo, e ora sto cercando di farlo lavorare da un programma utente.

Ecco il mio modulo del kernel:

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

In particolare, voglio che il mio programma utente per chiamare la funzione TimerSetup (). So che avrò bisogno di utilizzare ioctl (), ma non sono sicuro di come specificare nel mio modulo FILE che TimerSetup () dovrebbe essere richiamabile tramite ioctl ().

Inoltre, la mia seconda domanda: sono stato in grado di insmod mio modulo e anche mknod in / dev / myTimer con il maggior numero corretto. Ma quando ho provato ad aprire () in modo che io possa ottenere il descrittore di file da esso, continuava a tornare -1, che sto assumendo è sbagliato. Ho fatto che i permessi sono stati bene (in realtà, ho fatto 777 solo per essere sicuri) ... E ancora non funziona ... C'è qualcosa che mi manca?

Ecco il programma utente nel caso in cui:

#include <stdio.h>

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

    return 0;
}
È stato utile?

Soluzione

Il codice di esempio è necessario può essere trovato in drivers/watchdog/softdog.c (da Linux 2.6.33, al momento questo è stato scritto), che illustra le operazioni sui file corretto così come il modo di consentire userland per riempire una struttura con ioctl ().

In realtà è un grande, un'esercitazione di lavoro per chiunque abbia bisogno di scrivere driver di periferica carattere banali.

I sezionato l'interfaccia ioctl di softdog quando rispondere alla mia propria domanda, che può essere utile a voi.

Ecco l'essenza di esso (anche se lontano dall'essere esaustivo) ...

In softdog_ioctl() si vede un semplice inizializzazione di struct watchdog_info che pubblicizza la funzionalità, la versione e dispositivo di informazioni:

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

Abbiamo poi guardiamo un semplice caso in cui l'utente vuole solo per ottenere queste funzionalità:

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

... che ovviamente, riempirà l'userspace corrispondente watchdog_info con i valori inizializzati sopra. Se copy_to_user () fallisce, viene restituito -EFAULT che provoca la corrispondente ioctl userspace () per restituire -1 con errno significativo sia impostato.

Nota, le richieste di magia sono in realtà definiti in linux / watchdog.h, in modo che il kernel e userspace condividerli:

#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 ovviamente significa "cane da guardia ioctl"

Si può facilmente prendere che un passo ulteriore, avendo il driver fare qualcosa e posizionare il risultato di qualcosa che nella struttura e copiarlo userspace. Per esempio, se struct watchdog_info anche avuto un __u32 result_code membro. Si noti, __u32 è solo la versione del kernel di uint32_t.

Con ioctl (), l'utente passa l'indirizzo di un oggetto, sia esso una struttura, intero, qualunque sia al kernel aspettando il kernel di scrivere la sua risposta in un oggetto identico e copiare i risultati per l'indirizzo che è stato fornito.

La seconda cosa che si sta per bisogno di fare è assicurarsi che il dispositivo sa cosa fare quando qualcuno apre, legge da esso, scrive ad esso, o utilizza un gancio come ioctl (), che si può facilmente vedere attraverso lo studio softdog.

Di interesse è:

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

Dove si vede il gestore unlocked_ioctl andando a ... avete indovinato, softdog_ioctl ().

Credo che si potrebbe essere giustapponendo un livello di complessità che in realtà non esiste quando si tratta di ioctl (), è davvero così semplice. Per lo stesso motivo, la maggior parte degli sviluppatori del kernel cipiglio su nuove interfacce ioctl essere aggiunti meno che non siano assolutamente necessari. Il suo solo troppo facile perdere la traccia del tipo che ioctl () sta andando a riempire vs la magia si utilizza per farlo, che è la ragione principale per cui copy_to_user () fallisce spesso con conseguente kernel decomposizione con orde di processi userspace bloccato in il sonno del disco.

Per un temporizzatore, sono d'accordo, ioctl () è il percorso più breve per la sanità mentale.

Altri suggerimenti

Ti manca un puntatore a funzione .open nella struttura file_operations per specificare la funzione da chiamare quando un processo tenta di aprire il file del dispositivo. Sarà necessario specificare un puntatore a funzione .ioctl per la funzione ioctl pure.

Prova la lettura attraverso La Guida alla programmazione di Linux Kernel Module , in particolare i capitoli 4 (Device Character Files) e 7 (Parlando con i file di dispositivo).

Capitolo 4 introduce la struttura file_operations, che detiene i puntatori a funzioni definite dal modulo / driver che eseguono varie operazioni come open o ioctl.

Capitolo 7 fornisce informazioni sulla comunicazione con un modulo / guidare via ioctls.

Linux Device Drivers, terza edizione è un'altra buona risorsa.

Minimal esempio eseguibile

testato in un ambiente completamente riproducibile QEMU + Buildroot, quindi potrebbe aiutare gli altri a ottenere il loro lavoro ioctl. GitHub monte: kernel modulo | intestazione comune | userland .

La parte più fastidiosa è stata la comprensione che alcuni ID bassi sono dirottati: ioctl è non chiamato se cmd = 2 , è necessario utilizzare le macro _IOx.

modulo del kernel:

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

intestazione in comune:

#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

ambiente utente:

#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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top