Простейший символьный драйвер Linux (без создания /dev-файла)

Этот пример показывает, как создать и зарегистрировать символьный драйвер в ядре Linux без использования class_create() и без создания устройства в /dev. Модуль просто демонстрирует базовые функции open, read, write, release и взаимодействие через dmesg.

Автор: Garvis (aka ChatGPT)

Описание

  • Используется register_chrdev() для регистрации устройства.

  • Файл устройства не создаётся.

  • Все вызовы логируются в dmesg.

  • Возврат значений реализован как заглушка (ничего не читается и не пишется по факту).

Исходный код

// SPDX-License-Identifier: GPL-2.0
// char_driver_simple.c — Простейший символьный драйвер без /dev файла

#include <linux/module.h>    // Для всех модулей ядра
#include <linux/fs.h>        // Для file_operations
#include <linux/uaccess.h>   // Для доступа к пользовательскому пространству

#define DEVICE_NAME "garvis_simple"  // Имя устройства
static int major;                    // Major-номер, выделяется динамически

// Функция вызывается при открытии устройства
static int dev_open(struct inode *inode, struct file *file)
{
    pr_info("garvis_simple: Устройство открыто\n");
    return 0;
}

// Функция вызывается при чтении
static ssize_t dev_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
{
    pr_info("garvis_simple: Запрошено чтение (заглушка)\n");
    return 0;  // Не читаем данные
}

// Функция вызывается при записи
static ssize_t dev_write(struct file *file, const char __user *buf, size_t len, loff_t *offset)
{
    pr_info("garvis_simple: Запрошена запись %zu байт (заглушка)\n", len);
    return len;  // Проглатываем данные
}

// Функция вызывается при закрытии устройства
static int dev_release(struct inode *inode, struct file *file)
{
    pr_info("garvis_simple: Устройство закрыто\n");
    return 0;
}

// Структура операций устройства
static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = dev_open,
    .read = dev_read,
    .write = dev_write,
    .release = dev_release,
};

// Функция инициализации модуля
static int __init char_driver_simple_init(void)
{
    major = register_chrdev(0, DEVICE_NAME, &fops);  // 0 → динамический major
    if (major < 0) {
        pr_alert("garvis_simple: Ошибка регистрации устройства\n");
        return major;
    }
    pr_info("garvis_simple: Зарегистрировано с major номером %d\n", major);
    return 0;
}

// Функция выгрузки модуля
static void __exit char_driver_simple_exit(void)
{
    unregister_chrdev(major, DEVICE_NAME);
    pr_info("garvis_simple: Драйвер выгружен\n");
}

module_init(char_driver_simple_init);
module_exit(char_driver_simple_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Garvis");
MODULE_DESCRIPTION("Простой символьный драйвер без class_create");

Компиляция и загрузка

Создайте отдельный Makefile (например, Makefile_ge03):

obj-m := char_driver_simple.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Загрузите модуль:

make -f Makefile_ge03
sudo insmod char_driver_simple.ko
dmesg | tail
sudo rmmod char_driver_simple

Вывод будет примерно таким:

[....] garvis_simple: Зарегистрировано с major номером 240
[....] garvis_simple: Устройство открыто
[....] garvis_simple: Запрошена запись 12 байт (заглушка)
[....] garvis_simple: Устройство закрыто
[....] garvis_simple: Драйвер выгружен

Заключение

📌 Этот минималистичный модуль — отличная точка входа в разработку символьных драйверов под Linux. Он не использует классы, устройства, udev или sysfs, зато даёт понимание базового потока open → read/write → release.

—

Хочешь, могу сгенерировать .rst файл и отправить его как .zip, как раньше — дай команду.