Pregunta

Así que estoy tratando de escribir un módulo del kernel que utiliza el archivo Linux / timer.h. Tengo que trabajar dentro del módulo, y ahora estoy tratando de conseguir que funcione a partir de un programa de usuario.

Aquí está mi módulo del núcleo:

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

Más específicamente, quiero que mi programa de usuario para llamar a la función TimerSetup (). Sé que voy a tener que usar ioctl (), pero no estoy seguro de cómo especificar en mi archivo de módulo que TimerSetup () debe ser exigible a través de ioctl ().

Además, mi segunda pregunta: yo era capaz de insmod mi módulo y también en mknod / dev / myTimer con el mayor número correcto. Pero cuando traté de abrir () de modo que puedo conseguir el archivo descriptor de ella, que volvía -1, que estoy asumiendo que está mal. Me aseguré de que los permisos estaban bien (de hecho, lo hice 777 sólo para estar seguro) ... Todavía no funciona ... ¿Hay algo que me falta?

Este es el programa de usuario en caso de que:

#include <stdio.h>

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

    return 0;
}
¿Fue útil?

Solución

El código de ejemplo que necesita se puede encontrar en drivers/watchdog/softdog.c (desde Linux 2.6.33 en el momento en que esto fue escrito), que ilustra las operaciones de archivos adecuados, así como la forma de permitir espacio de usuario para llenar una estructura con ioctl ().

En realidad es un gran, tutorial de trabajo para cualquier persona que tiene que escribir controladores de dispositivo de caracteres triviales.

disequé interfaz ioctl de softdog cuando responder a mi propia pregunta , que puede ser útil en su caso.

Aquí está la esencia de la misma (aunque lejos de ser exhaustiva) ...

En softdog_ioctl() ves una inicialización sencilla de watchdog_info estructura que se anuncia la funcionalidad, la versión y la información del dispositivo:

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

A continuación, observamos un caso sencillo en el que el usuario sólo quiere obtener estas capacidades:

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

... que por supuesto, llenará el espacio de usuario correspondiente watchdog_info con los valores inicializados anteriores. Si copy_to_user () falla, -EFAULT se devuelve que hace que el correspondiente espacio de usuario ioctl () para volver -1 con una errno significativa está estableciendo.

Tenga en cuenta, las solicitudes de magia son en realidad define en linux / watchdog.h, de manera que el núcleo y el espacio de usuario a compartir:

#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 obviamente significa "perro guardián ioctl"

Usted puede tomar un paso más allá, con su conductor haga algo y colocar el resultado de que algo en la estructura y copiarlo en el espacio de usuario. Por ejemplo, si watchdog_info estructura también tenía un __u32 result_code miembro. Tenga en cuenta, __u32 es sólo la versión del kernel de uint32_t.

Con ioctl (), el usuario pasa la dirección de un objeto, ya sea una estructura, número entero, cualquiera que sea al núcleo esperando que el kernel para escribir su respuesta en un objeto idéntico y copiar los resultados en la dirección que se proporcionó.

La segunda cosa que usted va a tener que hacer es asegurarse de que su dispositivo sabe qué hacer cuando alguien abre, lee de ella, escribe a ella, o utiliza un gancho como ioctl (), que se puede ver fácilmente a través del estudio softdog.

Es de interés:

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

Cuando se ve el manejador unlocked_ioctl va a ... adivinaron, softdog_ioctl ().

Creo que es posible que se yuxtaposición de una capa de complejidad que realmente no existe cuando se trata de ioctl (), lo que realmente es así de simple. Por esa misma razón, la mayoría de los desarrolladores del kernel fruncen el ceño en nuevas interfaces ioctl que se añade a menos que sea absolutamente necesario. Su demasiado fácil perder la noción del tipo que ioctl () va a llenar vs la magia que utiliza para hacerlo, que es la razón principal de que copy_to_user () falla a menudo resulta en el núcleo de la descomposición con hordas de los procesos de usuario atrapados en el sueño de disco.

En un temporizador, de acuerdo, ioctl () es el camino más corto a la cordura.

Otros consejos

se echa en falta un puntero de función .open en su estructura file_operations para especificar la función que se llamará cuando un proceso intenta abrir el archivo de dispositivo. Tendrá que especificar un puntero de función .ioctl para su función ioctl también.

Trate de leer a través de La Guía de programación de Linux Kernel Módulo , específicamente los capítulos 4 (dispositivo de caracteres archivos) y 7 (Hablando con archivos de dispositivo).

Capítulo 4 presenta la estructura file_operations, que mantiene punteros a las funciones definidas por el módulo / controlador que realizan diversas operaciones tales como open o ioctl.

Capítulo 7 proporciona información sobre la comunicación con un módulo / conducir a través de ioctls.

Linux Device Drivers, tercera edición es otro buen recurso.

Minimal ejemplo runnable

prueba en un ambiente QEMU + Buildroot totalmente reproducibles, por lo que podría ayudar a otros a su trabajo ioctl. GitHub aguas arriba: módulo del kernel | cabecera compartida | espacio de usuario.

La parte más molesto fue entender que algunos identificadores de bajas son secuestrados: ioctl es no se llama si cmd = 2 , usted tiene que usar macros _IOx.

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)

cabecera compartida:

#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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top