2010-02-15 17 views
12

Tak więc próbuję napisać moduł jądra, który używa pliku linux/timer.h. Mam go do pracy wewnątrz modułu, a teraz próbuję go uruchomić z programu użytkownika.Jak używać ioctl() do manipulowania modułem jądra?

Oto mój moduł jądra:

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

Dokładniej, chcę mój program użytkownika, aby wywołać funkcję TimerSetup(). Wiem, że będę musiał użyć ioctl(), ale nie jestem pewien, jak określić w moim pliku MODULE, że TimerSetup() powinien być wywoływany przez ioctl().

Moje drugie pytanie: udało mi się insmodować mój moduł, a także mknod w/dev/mytimer z poprawnym numerem głównym. Ale kiedy próbowałem otworzyć() to tak, że mogę dostać deskryptor pliku z niego, to powracało, zwracając -1, co zakładam, jest złe. Upewniłem się, że uprawnienia są w porządku (w rzeczywistości zrobiłem to 777 tylko po to, aby się upewnić) ... To nadal nie działa ... Czy jest coś, czego mi brakuje?

Oto program użytkownika tylko w przypadku:

#include <stdio.h> 

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

    return 0; 
} 

Odpowiedz

20

Przykładowy kod, którego potrzebujesz, znajduje się w drivers/watchdog/softdog.c (od Linuksa 2.6.33 w momencie, w którym został napisany), co ilustruje prawidłowe operacje na plikach oraz sposób zezwalania użytkownikowi na wypełnianie struktury za pomocą ioctl().

To naprawdę świetny, działający samouczek dla każdego, kto chce pisać proste sterowniki urządzeń znaków.

Wyczyściłem interfejs ioctl softdog, gdy answering my own question, co może być pomocne dla Ciebie.

Oto sedno nim (choć dalekie od wyczerpująca) ...

W softdog_ioctl() widać prostą inicjalizacji struct watchdog_info że reklamuje funkcjonalności, wersji i urządzenia informacje:

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

Następnie spójrz na prosty przypadek, w którym użytkownik chce tylko uzyskać te możliwości:

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

... które oczywiście wypełnią c orresponding userspace watchdog_info z zainicjalizowanymi wartościami powyżej.Jeśli funkcja copy_to_user() zawiedzie, zwracana jest wartość -EFAULT, która powoduje, że odpowiadająca jej przestrzeń użytkownika wywołania ioctl() zwraca -1 z ustawionym znaczącym errno.

Uwaga, Magic żądania są właściwie zdefiniowane w linux/watchdog.h, tak, że jądro i Strefa dzielić się nimi:

#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 oczywiście oznaczający "Watchdog ioctl"

można łatwo wziąć że krok dalej, po tym, jak twój kierowca zrobi coś i umieści wynik tego czegoś w strukturze i skopiuje go do przestrzeni użytkownika. Na przykład, jeśli struct watchdog_info również miał członka __u32 result_code. Uwaga: __u32 to tylko wersja jądra uint32_t.

W ioctl(), użytkownik przekazuje adres obiektu, czy to strukturę, liczbę całkowitą, cokolwiek do jądra, spodziewając się, że jądro napisze odpowiedź w identycznym obiekcie i skopiuje wyniki na podany adres .

Drugą rzeczą, którą musisz zrobić, to upewnić się, że twoje urządzenie wie, co zrobić, gdy ktoś się otworzy, odczyta z niego, napisze do niego lub użyje haka podobnego do ioctl(), który możesz łatwo zobaczyć za pomocą studiując softdog.

Interesujący jest:

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

Gdzie widzisz obsługi unlocked_ioctl zamiar ... zgadliście, softdog_ioctl().

Myślę, że możesz zestawiać warstwę złożoności, która tak naprawdę nie istnieje w przypadku ioctl(), to naprawdę jest takie proste. Z tego samego powodu, większość deweloperów jądra czuwa nad dodawaniem nowych interfejsów ioctl, chyba że są absolutnie konieczne. Jego zbyt łatwe jest zignorowanie typu, jaki wypełni ioctl() w stosunku do magii, której używasz, co jest głównym powodem, dla którego funkcja copy_to_user() często kończy się niepowodzeniem, co powoduje, że jądro gnije z hordami procesów przestrzeni użytkownika zablokowanych sen dysku.

Dla zegara, zgadzam się, ioctl() jest najkrótszą ścieżką do zdrowia psychicznego.

+0

Właściwie to obejrzałem, ale nie całkiem to rozumiałem ... Widziałem funkcję statyczne długie softdog_ioctl (plik pliku struct *, unsigned int cmd, unsigned long arg) , ale nie rozumiem, co jest w nim .. Czy to jedyna funkcja ioctl? – hahuang65

+1

Awesome, wygląda na to, że wykonałeś dobrą robotę wyjaśniając interfejs w twoim poście :) Dziękuję. – hahuang65

+0

Chociaż ta odpowiedź ma teraz ponad 5 lat, miałem pytanie. Jeśli dalsze implementacje 'ioctl' są mile widziane, jaka jest preferowana alternatywa? – sherrellbc

8

Brakuje wskaźnik .open funkcji w strukturze file_operations określić funkcję, która będzie wywoływana, gdy proces próbuje otworzyć plik urządzenia. Będziesz musiał podać wskaźnik funkcji .ioctl również dla funkcji ioctl.

Spróbuj przeczytać przez The Linux Kernel Module Programming Guide, w szczególności rozdziały 4 (Pliki urządzeń znaków) i 7 (Rozmowy z plikami urządzeń).

Chapter 4 wprowadza strukturę file_operations, który posiada wskaźniki do funkcji określonych przez moduł/sterownik, który wykonywać różne operacje, takie jak open lub ioctl.

Chapter 7 dostarcza informacji na temat komunikacji z modułem/dyskiem poprzez ioctls.

Linux Device Drivers, Third Edition to kolejny dobry zasób.

+0

Dzięki za linki, wyglądają naprawdę pomocne. :) – hahuang65

+0

Istnieje kilka przykładów w * Przewodniku programowania modułów jądra Linux *, który powinien wskazać ci właściwy kierunek. – jschmier

0

Minimal runnable przykład

przetestowany w pełni powtarzalny środowiska QEMU + buildroot, więc może pomagać innym uzyskać ich ioctl pracy. GitHub upstream: kernel module | shared header | userland.

Najbardziej denerwującą częścią było zrozumienie, że niektóre low-id są porwane: ioctl is not called if cmd = 2, musisz użyć makr _IOx.

moduł 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) 

wspólna nagłówek:

#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; 
} 
Powiązane problemy