Pergunta

Então, estou tentando escrever um módulo de kernel que use o arquivo Linux/Timer.h. Consegui funcionar dentro apenas do módulo e agora estou tentando fazê -lo funcionar em um programa de usuário.

Aqui está o meu módulo de 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");
}

Mais especificamente, quero que meu programa de usuário chame a função timersetup (). Sei que vou precisar usar o ioctl (), mas não tenho certeza de como especificar no meu arquivo de módulo que o timerSetup () deve ser chamável via ioctl ().

Além disso, minha segunda pergunta: fui capaz de inserir meu módulo e também MKNOD em /dev /mytimer com o número principal correto. Mas quando tentei abrir () para que eu possa obter o descritor de arquivos, ele continuou retornando -1, o que estou supondo que esteja errado. Eu certifiquei -me de que as permissões estivessem bem (na verdade, fiz 777 apenas para ter certeza) ... ainda não funciona ... há algo que estou perdendo?

Aqui está o programa de usuários apenas no caso:

#include <stdio.h>

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

    return 0;
}
Foi útil?

Solução

O código de exemplo que você precisa pode ser encontrado em drivers/watchdog/softdog.c (do Linux 2.6.33 no momento em que isso foi escrito), que ilustra as operações de arquivo adequadas, bem como permitir que o Userland preencha uma estrutura com IOCTL ().

Na verdade, é um ótimo tutorial de trabalho para quem precisa escrever drivers de dispositivos de caracteres triviais.

Eu dissectei a interface IOCTL da SoftDog quando respondendo minha própria pergunta, o que pode ser útil para você.

Aqui está a essência disso (embora longe de ser exaustiva) ...

Dentro softdog_ioctl() Você vê uma simples inicialização de struct watchdog_info que anuncia as informações da funcionalidade, versão e dispositivo:

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

Em seguida, analisamos um caso simples em que o usuário só deseja obter esses recursos:

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

... que obviamente, preencherá o Usuáriospace Watchdog_info correspondente com os valores inicializados acima. Se copy_to_user () falhar, -efault será retornado, o que faz com que a chamada correspondente do userspace ioctl () retorne -1 com um errno significativo sendo definido.

Observe que as solicitações mágicas são realmente definidas no Linux/Watchdog.h, para que o kernel e o espaço dos usuários os compartilhem:

#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 significando obviamente "Watchdog ioctl"

Você pode facilmente dar um passo adiante, fazendo com que seu driver faça algo e coloque o resultado disso na estrutura e copie -o para o Usuários Space. Por exemplo, se struct watchdog_info também tivesse um membro __u32 result_code. Observação, __u32 é apenas a versão do kernel de uint32_t.

Com o ioctl (), o usuário passa o endereço de um objeto, seja uma estrutura, número inteiro, qualquer que seja para o kernel esperando que o kernel escreva sua resposta em um objeto idêntico e copie os resultados para o endereço fornecido.

A segunda coisa que você precisará fazer é garantir que seu dispositivo saiba o que fazer quando alguém abrir, ler, gravar ou usar um gancho como ioctl (), que você pode ver facilmente estudando o SoftDog.

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

Onde você vê o manipulador desbloqueado_ioctl indo para ... você adivinhou, softdog_ioctl ().

Eu acho que você pode estar justapondo uma camada de complexidade que realmente não existe ao lidar com o ioctl (), é realmente simples assim. Por esse mesmo motivo, a maioria dos desenvolvedores do kernel desaprova novas interfaces de IOCTL sendo adicionadas, a menos que sejam absolutamente necessárias. É muito fácil perder o controle do tipo que ioctl () preencherá vs a mágica que você usa para fazê -lo, que é a principal razão pela qual copy_to_user () falha geralmente resultando no kernel apodrecendo com hordas de processos de espaço de usuários presos sono de disco.

Para um cronômetro, eu concordo, o ioctl () é o caminho mais curto para a sanidade.

Outras dicas

Você está perdendo um .open ponteiro da função em seu file_operations Estrutura para especificar a função a ser chamada quando um processo tenta abrir o arquivo do dispositivo. Você precisará especificar um .ioctl Ponteiro da função para sua função IOCTL também.

Tente ler O guia de programação do módulo do kernel Linux, especificamente os capítulos 4 (arquivos de dispositivo de caracteres) e 7 (conversando com os arquivos do dispositivo).

Capítulo 4 apresenta o file_operations estrutura, que mantém ponteiros para funções definidas pelo módulo/driver que executam várias operações, como open ou ioctl.

Capítulo 7 Fornece informações sobre a comunicação com um módulo/unidade via IOCTLS.

Drivers de dispositivo Linux, terceira edição é outro bom recurso.

Exemplo mínimo de execução

Testado em um ambiente QEMU + Buildroot totalmente reproduzível, por isso pode ajudar outras pessoas a obter seus ioctl trabalhando. Github a montante:Módulo do kernel | Cabeçalho compartilhado | Userland.

A parte mais irritante foi entender que alguns IDs baixos são seqüestrados: O IOCTL não é chamado se CMD = 2 , você tem que usar _IOx macros.

Módulo de 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)

Cabeçalho compartilhado:

#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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top