/
MaestroN
/
Eltex_Homework
Обзор
Документация
Войти
/
MaestroN
/
Eltex_Homework
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
Task_13/custom_device.c
355 строк
12 KB
maestronnn
changed task 13
13 фев 2025, 08:54
13 фев 2025, 08:54
d094351
Код
Авторство
О чём код?
/* * custom_device.c - пример символьного драйвера с поддержкой /proc и /sys, * операций read/write и ioctl. * * Задачи: * - Регистрация символьного устройства (/dev/custom_device) * - Реализация open, release, read, write * - Определение и обработка ioctl-команд для установки и получения состояния * - Создание записи в /proc (например, /proc/custom_device_info) для вывода статистики * - Создание атрибута в /sys (через класс устройства) для настройки параметров * - Выделение памяти с помощью kmalloc и синхронизация доступа через мьютекс */ #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> // alloc_chrdev_region, file_operations #include <linux/cdev.h> // cdev #include <linux/uaccess.h> // copy_to_user, copy_from_user #include <linux/slab.h> // kmalloc, kfree #include <linux/mutex.h> // mutex #include <linux/proc_fs.h> // proc_create #include <linux/seq_file.h> // seq_file #include <linux/device.h> // class, device, sysfs #include <linux/ioctl.h> // ioctl macros MODULE_LICENSE("GPL"); MODULE_AUTHOR("MaestroN"); MODULE_DESCRIPTION("Символьный драйвер с /proc и /sys интерфейсами, поддержкой read/write/ioctl"); /* --- Определения макросов и констант --- */ #define DEVICE_NAME "custom_device" #define CLASS_NAME "custom_device_class" #define PROC_ENTRY_NAME "custom_device_info" #define BUFFER_SIZE 1024 /* Определение ioctl команд */ #define CUSTOM_IOC_MAGIC 'k' #define CUSTOM_SET_STATE _IOW(CUSTOM_IOC_MAGIC, 1, int) // Установка состояния (пишем int) #define CUSTOM_GET_STATE _IOR(CUSTOM_IOC_MAGIC, 2, int) // Получение состояния (читаем int) /* --- Структура для хранения внутренних данных устройства --- */ struct custom_device_data { char *buffer; // Буфер для хранения данных size_t buffer_size; // Размер буфера int device_state; // Внутреннее состояние устройства int read_count; // Счётчик вызовов read int write_count; // Счётчик вызовов write struct mutex lock; // Мьютекс для синхронизации доступа }; static dev_t dev_num; // Номер устройства static struct cdev custom_cdev; // Структура cdev static struct class *custom_class = NULL; // Класс для sysfs static struct device *custom_device = NULL;// Устройство для sysfs static struct proc_dir_entry *proc_entry = NULL; // Запись в /proc static struct custom_device_data *device_data = NULL; // Указатель на данные устройства /* --- Функции операций с устройством --- */ /* Функция открытия устройства */ static int custom_open(struct inode *inode, struct file *file) { pr_info("custom_device: Device opened\n"); return 0; } /* Функция закрытия устройства */ static int custom_release(struct inode *inode, struct file *file) { pr_info("custom_device: Device closed\n"); return 0; } /* Функция чтения данных из устройства */ static ssize_t custom_read(struct file *file, char __user *user_buf, size_t len, loff_t *offset) { ssize_t ret = 0; if (mutex_lock_interruptible(&device_data->lock)) return -ERESTARTSYS; if (*offset >= device_data->buffer_size) { ret = 0; goto out; } if (*offset + len > device_data->buffer_size) len = device_data->buffer_size - *offset; if (copy_to_user(user_buf, device_data->buffer + *offset, len)) { ret = -EFAULT; goto out; } *offset += len; ret = len; device_data->read_count++; out: mutex_unlock(&device_data->lock); return ret; } /* Функция записи данных в устройство */ static ssize_t custom_write(struct file *file, const char __user *user_buf, size_t len, loff_t *offset) { ssize_t ret = 0; if (mutex_lock_interruptible(&device_data->lock)) return -ERESTARTSYS; if (*offset + len > BUFFER_SIZE) len = BUFFER_SIZE - *offset; if (copy_from_user(device_data->buffer + *offset, user_buf, len)) { ret = -EFAULT; goto out; } *offset += len; ret = len; device_data->write_count++; out: mutex_unlock(&device_data->lock); return ret; } /* Функция обработки ioctl-запросов */ static long custom_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int new_state; long ret = 0; if (mutex_lock_interruptible(&device_data->lock)) return -ERESTARTSYS; switch (cmd) { case CUSTOM_SET_STATE: if (copy_from_user(&new_state, (int __user *)arg, sizeof(new_state))) { ret = -EFAULT; break; } device_data->device_state = new_state; pr_info("custom_device: Device state set to %d\n", new_state); break; case CUSTOM_GET_STATE: if (copy_to_user((int __user *)arg, &device_data->device_state, sizeof(device_data->device_state))) { ret = -EFAULT; break; } pr_info("custom_device: Device state %d retrieved\n", device_data->device_state); break; default: ret = -EINVAL; break; } mutex_unlock(&device_data->lock); return ret; } /* Определение структуры file_operations для устройства */ static struct file_operations fops = { .owner = THIS_MODULE, .open = custom_open, .release = custom_release, .read = custom_read, .write = custom_write, .unlocked_ioctl = custom_ioctl, }; /* --- Реализация /proc интерфейса --- */ /* Функция вывода информации в /proc через seq_file */ static int proc_show(struct seq_file *m, void *v) { mutex_lock(&device_data->lock); seq_printf(m, "Device State: %d\n", device_data->device_state); seq_printf(m, "Read Count : %d\n", device_data->read_count); seq_printf(m, "Write Count : %d\n", device_data->write_count); mutex_unlock(&device_data->lock); return 0; } /* Функция открытия файла /proc */ static int proc_open_func(struct inode *inode, struct file *file) { return single_open(file, proc_show, NULL); } /* Структура proc_ops для /proc файла */ static const struct proc_ops proc_file_ops = { .proc_open = proc_open_func, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = single_release, }; /* --- Реализация sysfs интерфейса --- */ /* Функция вывода атрибута (show) */ static ssize_t custom_param_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t count; if (mutex_lock_interruptible(&device_data->lock)) return -ERESTARTSYS; count = scnprintf(buf, PAGE_SIZE, "%d\n", device_data->device_state); mutex_unlock(&device_data->lock); return count; } /* Функция записи атрибута (store) */ static ssize_t custom_param_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int new_value; int ret; ret = kstrtoint(buf, 10, &new_value); if (ret < 0) return ret; if (mutex_lock_interruptible(&device_data->lock)) return -ERESTARTSYS; device_data->device_state = new_value; mutex_unlock(&device_data->lock); return count; } /* Определяем атрибут устройства */ static DEVICE_ATTR(custom_param, 0664, custom_param_show, custom_param_store); /* --- Функции загрузки и выгрузки модуля --- */ static int __init custom_init(void) { int ret; pr_info("custom_device: Initializing module\n"); ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME); if (ret < 0) { pr_err("custom_device: Failed to allocate device number\n"); return ret; } cdev_init(&custom_cdev, &fops); custom_cdev.owner = THIS_MODULE; ret = cdev_add(&custom_cdev, dev_num, 1); if (ret < 0) { pr_err("custom_device: Failed to add cdev\n"); unregister_chrdev_region(dev_num, 1); return ret; } custom_class = class_create(CLASS_NAME); if (IS_ERR(custom_class)) { pr_err("custom_device: Failed to create class\n"); cdev_del(&custom_cdev); unregister_chrdev_region(dev_num, 1); return PTR_ERR(custom_class); } custom_device = device_create(custom_class, NULL, dev_num, NULL, DEVICE_NAME); if (IS_ERR(custom_device)) { pr_err("custom_device: Failed to create device\n"); class_destroy(custom_class); cdev_del(&custom_cdev); unregister_chrdev_region(dev_num, 1); return PTR_ERR(custom_device); } ret = device_create_file(custom_device, &dev_attr_custom_param); if (ret < 0) { pr_err("custom_device: Failed to create sysfs attribute\n"); device_destroy(custom_class, dev_num); class_destroy(custom_class); cdev_del(&custom_cdev); unregister_chrdev_region(dev_num, 1); return ret; } proc_entry = proc_create(PROC_ENTRY_NAME, 0444, NULL, &proc_file_ops); if (!proc_entry) { pr_err("custom_device: Failed to create proc entry\n"); device_remove_file(custom_device, &dev_attr_custom_param); device_destroy(custom_class, dev_num); class_destroy(custom_class); cdev_del(&custom_cdev); unregister_chrdev_region(dev_num, 1); return -ENOMEM; } device_data = kmalloc(sizeof(struct custom_device_data), GFP_KERNEL); if (!device_data) { pr_err("custom_device: Failed to allocate memory for device data\n"); proc_remove(proc_entry); device_remove_file(custom_device, &dev_attr_custom_param); device_destroy(custom_class, dev_num); class_destroy(custom_class); cdev_del(&custom_cdev); unregister_chrdev_region(dev_num, 1); return -ENOMEM; } device_data->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); if (!device_data->buffer) { pr_err("custom_device: Failed to allocate memory for buffer\n"); kfree(device_data); proc_remove(proc_entry); device_remove_file(custom_device, &dev_attr_custom_param); device_destroy(custom_class, dev_num); class_destroy(custom_class); cdev_del(&custom_cdev); unregister_chrdev_region(dev_num, 1); return -ENOMEM; } device_data->buffer_size = BUFFER_SIZE; device_data->device_state = 0; device_data->read_count = 0; device_data->write_count = 0; mutex_init(&device_data->lock); pr_info("custom_device: Module loaded successfully\n"); return 0; } static void __exit custom_exit(void) { pr_info("custom_device: Exiting module\n"); if (device_data) { if (device_data->buffer) kfree(device_data->buffer); kfree(device_data); } proc_remove(proc_entry); device_remove_file(custom_device, &dev_attr_custom_param); device_destroy(custom_class, dev_num); class_destroy(custom_class); cdev_del(&custom_cdev); unregister_chrdev_region(dev_num, 1); pr_info("custom_device: Module unloaded successfully\n"); } module_init(custom_init); module_exit(custom_exit);