Как я могу использовать ioctl () для управления моим модулем ядра? - PullRequest
13 голосов
/ 15 февраля 2010

Итак, я пытаюсь написать модуль ядра, который использует файл linux / timer.h. Я заставил его работать только внутри модуля, и теперь я пытаюсь заставить его работать из пользовательской программы.

Вот мой модуль ядра:

//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 (), но я не уверен, как указать в моем ФАЙЛЕ МОДУЛЯ, что TimerSetup () должен вызываться через ioctl ().

Кроме того, мой второй вопрос: я смог вставить свой модуль, а также 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;
}

Ответы [ 3 ]

20 голосов
/ 15 февраля 2010

Пример кода, который вам нужен, можно найти в drivers/watchdog/softdog.c (из Linux 2.6.33 на момент написания этой статьи), который иллюстрирует правильные файловые операции, а также как разрешить пользователю заполнять структуру с помощью ioctl ().

На самом деле это отличный, рабочий учебник для тех, кому нужно писать тривиальные драйверы символьных устройств.

Я анализировал интерфейс ioctl softdog, когда отвечал на мой собственный вопрос , который может быть вам полезен.

Вот суть этого (хотя и далеко не исчерпывающего) ...

В softdog_ioctl() вы видите простую инициализацию struct 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;

... что, конечно, заполнит соответствующее пользовательское пространство 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 явно означает "Watchdog 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 () - кратчайший путь к здравомыслию.

8 голосов
/ 15 февраля 2010

В вашей структуре file_operations отсутствует указатель на функцию .open, чтобы указать функцию, вызываемую при попытке процесса открыть файл устройства. Вам также потребуется указать указатель функции .ioctl для вашей функции ioctl.

Попробуйте прочитать Руководство по программированию модуля ядра Linux , в частности главы 4 (Файлы символьных устройств) и 7 (Разговор с файлами устройств).

Глава 4 представляет структуру file_operations, которая содержит указатели на функции, определенные модулем / драйвером, которые выполняют различные операции, такие как open или ioctl.

Глава 7 предоставляет информацию о связи с модулем / приводом через ioctls.

Драйверы для устройств Linux, третье издание - еще один хороший ресурс.

1 голос

Пример минимального запуска

Протестировано в полностью воспроизводимой среде QEMU + Buildroot, поэтому может помочь другим заставить их ioctl работать. GitHub upstream: модуль ядра | общий заголовок | 1011 * пользовательские программы *.

Самым раздражающим было понимание того, что некоторые низкие идентификаторы перехвачены: ioctl не вызывается, если 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;
}
...