2010-02-15 13 views
12

Así que estoy tratando de escribir un módulo kernel que use el archivo linux/timer.h. Lo tengo para trabajar dentro del módulo, y ahora estoy tratando de hacerlo funcionar desde un programa de usuario.¿Cómo uso ioctl() para manipular mi módulo kernel?

Aquí es mi módulo 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"); 
} 

Más específicamente, quiero que mi programa de usuario para llamar a la función TimerSetup(). Sé que tendré que usar ioctl() pero no estoy seguro de cómo especificar en mi ARCHIVO DE MÓDULO que TimerSetup() debería poder llamarse a través de ioctl().

Además, mi segunda pregunta: Me fue capaz de insmod mi módulo y también en mknod/dev/myTimer con el mayor número correcto. Pero cuando traté de abrirlo() para poder obtener el descriptor del archivo, siguió devolviendo -1, lo cual supongo que es incorrecto. Me aseguré de que los permisos estuvieran bien (de hecho, lo hice 777 solo para estar seguro) ... Todavía no funciona ... ¿Hay algo que me falta?

Aquí es el programa de usuario por si acaso:

#include <stdio.h> 

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

    return 0; 
} 

Respuesta

20

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 archivo adecuadas y cómo permitir que userland complete una estructura con ioctl().

En realidad, es un tutorial excelente y funcional para cualquiera que necesite escribir controladores de dispositivos de caracteres triviales.

disequé interfaz ioctl de softdog cuando answering my own question, que puede ser útil para usted.

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

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

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

A continuación, Mire un caso simple donde el usuario solo quiere obtener estas capacidades:

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

... que por supuesto, llenará la c oresponde el espacio de usuario watchdog_info con los valores inicializados arriba.Si copy_to_user() falla, se devuelve -EFAULT que hace que la llamada ioctl() de espacio de usuario correspondiente devuelva -1 con un error errno significativo.

Nota, 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á, hacer que el controlador haga algo y colocar el resultado de ese elemento en la estructura y copiarlo en el espacio de usuario. Por ejemplo, si struct watchdog_info también tenía un miembro __u32 result_code. Tenga en cuenta que __u32 es solo la versión del kernel de uint32_t.

Con ioctl(), el usuario pasa la dirección de un objeto, ya sea una estructura, entero, lo que sea para el kernel esperando que el kernel escriba su respuesta en un objeto idéntico y copie los resultados a la dirección que se proporcionó .

Lo segundo que tendrá que hacer es asegurarse de que su dispositivo sepa qué hacer cuando alguien abre, lee, escribe o utiliza un enlace como ioctl(), que puede ver fácilmente por estudiando 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, 
}; 

donde se ve el manejador unlocked_ioctl va a ... lo has adivinado, softdog_ioctl().

Creo que puede yuxtaponer una capa de complejidad que realmente no existe cuando se trata de ioctl(), realmente es así de simple. Por esa misma razón, la mayoría de los desarrolladores del núcleo fruncen el ceño al agregar nuevas interfaces ioctl a menos que sean absolutamente necesarias. Es demasiado fácil perder la pista del tipo que ioctl() va a llenar frente a la magia que usas para hacerlo, que es la razón principal por la que copy_to_user() falla y el kernel se pudre con hordas de procesos de espacio de usuario atrapados dormir disco

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

+0

De hecho miré eso, pero no lo entendí ... Vi la función static long softdog_ioctl (archivo struct * file, unsigned int cmd, unsigned long arg) pero no entendí lo que hay en ella .. . ¿Es esa la única función ioctl? – hahuang65

+1

Impresionante, parece que hiciste un buen trabajo explicando la interfaz en tu publicación :) Gracias. – hahuang65

+0

Aunque esta respuesta ahora tiene más de 5 años, tenía una pregunta. Si otras implementaciones de 'ioctl' son desaprobadas, ¿cuál es la alternativa preferida? – sherrellbc

8

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

tratar de leer a través de The Linux Kernel Module Programming Guide, específicamente los capítulos 4 (Personaje Dispositivo Archivos) y 7 (Hablando con archivos de dispositivo).

Chapter 4 introduce la estructura de file_operations, que mantiene punteros a funciones definidas por el módulo/controlador que realizan diversas operaciones tales como open o ioctl.

Chapter 7 proporciona información sobre la comunicación con un módulo/unidad a través de ioctls.

Linux Device Drivers, Third Edition es otro buen recurso.

+0

Gracias por los enlaces, se ven muy útiles. :) – hahuang65

+0

Hay algunos ejemplos en * La Guía de programación del módulo Kernel de Linux * que debe orientarlo en la dirección correcta. – jschmier

0

ejemplo ejecutable Mínimo

prueba en un ambiente QEMU + Buildroot totalmente reproducibles, por lo que podrían ayudar a otros a obtener su trabajo ioctl. GitHub upstream: kernel module | shared header | userland.

La parte más molesta fue entender que algunos identificadores bajos están secuestrados: ioctl is not called if cmd = 2, tiene que usar las 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; 
} 
Cuestiones relacionadas