2010-02-15 22 views
12

Donc j'essaye d'écrire un module noyau qui utilise le fichier linux/timer.h. Je l'ai eu à travailler à l'intérieur juste le module, et maintenant j'essaye de le faire fonctionner à partir d'un programme d'utilisateur.Comment utiliser ioctl() pour manipuler mon module noyau?

Voici mon module de noyau:

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

Plus précisément, je veux que mon programme utilisateur pour appeler la fonction TimerSetup(). Je sais que j'aurai besoin d'utiliser ioctl() mais je ne sais pas comment spécifier dans mon fichier MODULE que TimerSetup() devrait être appelé via ioctl().

Aussi, ma deuxième question: J'ai été capable d'insmod mon module et aussi mknod dans/dev/mytimer avec le nombre majeur correct. Mais quand j'ai essayé de l'ouvrir() pour que je puisse en extraire le descripteur de fichier, il revenait -1, ce que je suppose être faux. Je me suis assuré que les autorisations étaient correctes (en fait, je l'ai fait 777 juste pour être sûr) ... Ça ne marche toujours pas ... Y a-t-il quelque chose qui me manque?

Voici le programme utilisateur au cas où:

#include <stdio.h> 

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

    return 0; 
} 

Répondre

20

L'exemple de code dont vous avez besoin se trouve dans drivers/watchdog/softdog.c (à partir de Linux 2.6.33 au moment où il a été écrit), qui illustre les opérations correctes des fichiers et permet à userland de remplir une structure avec ioctl().

C'est en fait un excellent tutoriel de travail pour tous ceux qui ont besoin d'écrire des pilotes de périphériques de caractères triviaux.

J'ai disséqué l'interface ioctl de softdog quand answering my own question, ce qui peut vous être utile.

est ici l'essentiel de celui-ci (bien loin d'être exhaustive) ...

En softdog_ioctl() vous voyez une initialisation simple watchdog_info struct qui annonce la fonctionnalité, informations sur la version et le dispositif:

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

Nous avons ensuite regarder un cas simple où l'utilisateur veut simplement obtenir ces capacités:

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

... qui bien sûr, remplira le c correspondant à l'espace utilisateur watchdog_info avec les valeurs initialisées ci-dessus.Si copy_to_user() échoue, -EFAULT est retourné, ce qui provoque l'invocation de l'appel ioctl() de l'espace utilisateur correspondant -1 avec un errno significatif.

Note, les demandes magiques sont réellement définis dans linux/watchdog.h, de sorte que le noyau et l'espace utilisateur de les partager:

#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 évidemment signifiant "chien de garde ioctl"

Vous pouvez facilement prendre C'est un peu plus loin, demandez à votre chauffeur de faire quelque chose et placez le résultat de ce quelque chose dans la structure et copiez-le dans l'espace utilisateur. Par exemple, si struct watchdog_info a également un membre __u32 result_code. Notez, __u32 est juste la version du noyau de uint32_t. Avec ioctl(), l'utilisateur passe l'adresse d'un objet, qu'il s'agisse d'une structure, d'un nombre entier, du noyau attendant le noyau pour écrire sa réponse dans un objet identique et copie les résultats à l'adresse fournie . La deuxième chose que vous devez faire est de vous assurer que votre appareil sait quoi faire quand quelqu'un ouvre, lit, écrit dessus, ou utilise un crochet comme ioctl(), que vous pouvez facilement voir par étudiant softdog.

Il est intéressant:

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

Où vous voyez le gestionnaire de unlocked_ioctl va ... vous l'aurez deviné, softdog_ioctl().

Je pense que vous pourriez juxtaposer une couche de complexité qui n'existe pas vraiment avec ioctl(), c'est vraiment simple. Pour cette même raison, la plupart des développeurs du noyau froncent les sourcils sur les nouvelles interfaces ioctl ajoutées, sauf si elles sont absolument nécessaires. Il est trop facile de perdre la trace du type que ioctl() va remplir par rapport à la magie que vous utilisez, ce qui est la principale raison pour laquelle copy_to_user() échoue, entraînant souvent le noyau à pourrir avec des hordes de processus userspace sommeil du disque.

Pour une minuterie, je suis d'accord, ioctl() est le chemin le plus court vers la santé mentale.

+0

J'ai effectivement regardé cela, mais je ne comprenais pas tout à fait ... J'ai vu la fonction statique long softdog_ioctl (fichier struct * fichier, cmd non signé int, argument long non signé) mais ne comprenait pas ce qu'il ya dedans .. Est-ce la seule fonction ioctl? – hahuang65

+1

Génial, on dirait que vous avez fait un bon travail en expliquant l'interface dans votre message :) Merci. – hahuang65

+0

Bien que cette réponse ait maintenant plus de 5 ans, j'avais une question. Si d'autres implémentations de 'ioctl' sont mal vues, quelle est l'alternative préférée? – sherrellbc

8

Il vous manque un pointeur de fonction .open dans votre structure file_operations pour spécifier la fonction à appeler lorsqu'un processus tente d'ouvrir le fichier de périphérique. Vous devrez également spécifier un pointeur de fonction .ioctl pour votre fonction ioctl. Essayez de lire The Linux Kernel Module Programming Guide, en particulier les chapitres 4 (Fichiers de périphériques de caractères) et 7 (Fichiers parlant vers un périphérique).

Chapter 4 introduit la structure file_operations, qui contient des pointeurs vers des fonctions définies par le module pilote/qui exécutent diverses opérations telles que open ou ioctl.

Chapter 7 fournit des informations sur la communication avec un module/lecteur via ioctls.

Linux Device Drivers, Third Edition est une autre bonne ressource.

+0

Merci pour les liens, ils ont vraiment utiles. :) – hahuang65

+0

Il existe quelques exemples dans * Le Guide de Programmation du Module Kernel Linux * qui devrait vous orienter dans la bonne direction. – jschmier

0

exemple minimal runnable

testé dans un QEMU totalement reproductible + environnement Buildroot, donc pourrait aider d'autres à leur travail ioctl. GitHub en amont: kernel module | shared header | userland. La partie la plus gênante était de comprendre que certains identifiants sont piratés: ioctl is not called if cmd = 2, vous devez utiliser les macros _IOx.

Module du noyau:

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

tête partagée:

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