2010-02-15 65 views
12

所以我試圖編寫一個使用linux/timer.h文件的內核模塊。我只是在模塊內部工作,現在我正試圖從用戶程序開始工作。如何使用ioctl()來操作我的內核模塊?

這裏是我的內核模塊:

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

更具體地說,我希望我的用戶程序調用TimerSetup()函數。我知道我需要使用ioctl(),但我不知道如何在我的MODULE FILE中指定TimerSetup()應該可以通過ioctl()調用。

另外,我的第二個問題:我能夠insmod我的模塊和mknod到正確的主編號/ dev/mytimer。但是當我試圖打開()它以便我可以從中獲取文件描述符時,它會一直返回-1,我認爲這是錯誤的。我確定權限沒有問題(事實上,我確定它只是777)......它仍然不起作用......有什麼我失蹤了嗎?

下面是用戶程序,以防萬一:

#include <stdio.h> 

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

    return 0; 
} 

回答

20

您需要的示例代碼可以在drivers/watchdog/softdog.c(來自Linux 2.6.33編寫時)中找到,它說明了正確的文件操作以及如何允許userland用ioctl()填充結構。

對於任何需要編寫簡單字符設備驅動程序的人來說,這實際上是一個很好的工作教程。

我解剖了softdog的ioctl界面,當時answering my own question,這可能對你有幫助。

下面是它的要點(雖然遠不止這些)...

softdog_ioctl()你看到的結構watchdog_info的一個簡單的初始化是做廣告的功能,版本和設備信息:

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

然後我們看一個簡單的情況,用戶只是想獲得這些功能:

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

......這當然會填滿c或具有上述初始值的相應用戶空間watchdog_info。如果copy_to_user()失敗,則返回-EFAULT,導致相應的用戶空間ioctl()調用返回-1,並設置有意義的errno。

注意,魔術請求在linux/watchdog.h被實際的定義,使內核和用戶空間分享:

#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明顯表示「看門狗IOCTL」

您可以輕鬆地更進一步,讓您的驅動程序執行某些操作並將結果放入結構中並將其複製到用戶空間。例如,如果struct watchdog_info也有一個成員__u32 result_code。請注意,__u32只是內核版本uint32_t

使用ioctl(),用戶可以傳遞一個對象的地址,無論是內核的結構體,整數,期望內核將其應答寫入相同的對象,並將結果複製到提供的地址。

您需要做的第二件事是確保您的設備知道在某人打開,讀取,寫入或使用像ioctl()這樣的鉤子時可以執行的操作,學習softdog。

感興趣的是:

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

當你看到unlocked_ioctl處理程序要......你猜對了,softdog_ioctl()。

我想你可能會並列,與的ioctl()時也真的不存在了一層複雜性,它真的就是這麼簡單。出於同樣的原因,除非絕對必要,否則大多數內核開發人員會對新的ioctl接口感到厭煩。它很容易失去ioctl()將要填充的類型和你用來做這件事的魔術的軌跡,這是copy_to_user()失敗的主要原因經常導致內核被用戶空間進程阻塞磁盤睡眠。

對於定時器,我同意,的ioctl()是理智的最短路徑。

+0

我真的看了看,但並不完全瞭解它... 我看到了函數 static long softdog_ioctl(struct file * file,unsigned int cmd,unsigned long arg) 但是不明白裏面的內容。這是唯一的ioctl功能嗎? – hahuang65 2010-02-15 07:20:39

+1

真棒,它看起來像你做了一個很好的工作解釋你的帖子界面:)謝謝。 – hahuang65 2010-02-15 07:26:16

+0

雖然這個答案現在已經超過5年了,但我確實有一個問題。如果對'ioctl'的進一步實現令人不悅,那麼最佳選擇是什麼? – sherrellbc 2015-09-18 01:44:02

8

你缺少你file_operations結構.open函數指針指定函數當一個進程試圖打開設備文件被調用。您還需要爲您的ioctl函數指定一個.ioctl函數指針。

嘗試通過The Linux Kernel Module Programming Guide閱讀,特別是第4章(字符設備文件)和第7章(與設備文件交談)。

Chapter 4介紹file_operations結構,其保持指針由執行各種操作,諸如openioctl模塊/驅動程序定義的函數。

Chapter 7提供關於通過ioctls與模塊/驅動器進行通信的信息。

Linux Device Drivers, Third Edition是另一個很好的資源。

+0

感謝您的鏈接,它們看起來非常有幫助的。 :) – hahuang65 2010-02-15 07:26:39

+0

* Linux內核模塊編程指南*中有一些例子應該指向正確的方向。 – jschmier 2010-02-15 07:31:59

0

最小運行的例子

經測試,在一個完全可再現QEMU + Buildroot裏面的環境,所以可能幫助別人得到他們ioctl工作。 GitHub上游: kernel module | shared header | userland

最煩人的部分是理解某些低位ID被劫持:ioctl is not called if cmd = 2,您必須使用_IOx宏。

內核模塊:

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

共享標題:

#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 

用戶級:

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