From 8bc08b536e5f21debcf4b77bfb60dfa982f4e043 Mon Sep 17 00:00:00 2001 From: Yevgen Kovalyov Date: Wed, 17 Mar 2021 09:32:54 +0200 Subject: [PATCH 1/2] home work: character devices --- 10_cdev/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 10_cdev/README.md diff --git a/10_cdev/README.md b/10_cdev/README.md new file mode 100644 index 00000000..bf25003c --- /dev/null +++ b/10_cdev/README.md @@ -0,0 +1,17 @@ +# Home work: Character Devices + +# Task - Implement a character device driver for text messaging between users. + +## Implement a character device driver with the following requirements: +- 1kB buffer default size; +- buffer increase as a module parameter; +- works only ASCII strings. + +## Add the interfaces into the driver: +- sysfs for buffer clean up and debugging; +- procfs for displaying used buffer volume and buffer size + +## Implement shell/bash scripts for the driver testing. +- add developed scripts; +- add log files + From c32f097b2b21235d10639651b2ad3a9c36ad4cbb Mon Sep 17 00:00:00 2001 From: Anton Kotsiubailo Date: Sun, 28 Mar 2021 16:56:40 +0300 Subject: [PATCH 2/2] kernel: Add task10 code and script Implemented code for task10, where was realized characted device. Signen-off-by: Anton Kotsiubailo --- 10_cdev/Makefile | 12 ++ 10_cdev/chardev.c | 366 ++++++++++++++++++++++++++++++++++++++++++++++ 10_cdev/logs.txt | 65 ++++++++ 10_cdev/test.sh | 24 +++ 4 files changed, 467 insertions(+) create mode 100644 10_cdev/Makefile create mode 100644 10_cdev/chardev.c create mode 100644 10_cdev/logs.txt create mode 100644 10_cdev/test.sh diff --git a/10_cdev/Makefile b/10_cdev/Makefile new file mode 100644 index 00000000..447aea0d --- /dev/null +++ b/10_cdev/Makefile @@ -0,0 +1,12 @@ +ifneq ($(KERNELRELEASE),) +# kbuild part of makefile +obj-m := chardev.o +else +# normal makefile + +obj-m := chardev.o +default: + $(MAKE) -C $(KDIR) M=$$PWD modules +clean: + $(MAKE) -C $(KDIR) M=$$PWD clean +endif diff --git a/10_cdev/chardev.c b/10_cdev/chardev.c new file mode 100644 index 00000000..2a815e4e --- /dev/null +++ b/10_cdev/chardev.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: MIT and GPL +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "char_dev" +#define CLASS_NAME "char_class" +#define LENGTH_DEF 1024 +#define PROC_SYS_BUF 16 +#define PROC_DIRECTORY "proc_chdev" +#define PROC_FILENAME_1 "buf_volume" +#define PROC_FILENAME_2 "buf_size" +#define SYS_FILE_NAME "sys_char" +#define CLASS_ATTR(_name, _mode, _show, _store) \ +struct class_attribute class_attr_##_name = \ +__ATTR(_name, _mode, _show, _store) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Anton Kotsiubailo"); +MODULE_DESCRIPTION("Task10 chardev with sysfs and procfs"); +MODULE_VERSION("0.1"); + +static int majorNumber; +static char *message; +static char *size_buffer; +static char *volume_buffer; +static short size_of_message; +static int numberOpens; +static struct class *charClass; +static struct device *charDevice; +static unsigned int length = LENGTH_DEF; +static struct proc_dir_entry *proc_dir; +static struct proc_dir_entry *proc_file; +static struct proc_dir_entry *proc_file2; +module_param(length, int, 0); + +static int dev_open(struct inode *, struct file *); +static int dev_release(struct inode *, struct file *); +static ssize_t dev_read(struct file *, char *, size_t, loff_t *); +static ssize_t dev_write(struct file *, const char *, size_t, loff_t *); +static int volume_read(struct file *file_p, char __user *buffer, size_t length, + loff_t *offset); +static int size_read(struct file *file_p, char __user *buffer, size_t length, + loff_t *offset); + +static struct file_operations volume_fops = { + .read = volume_read, +}; + +static struct file_operations size_fops = { + .read = size_read, +}; + +static struct file_operations fops = { + .open = dev_open, + .read = dev_read, + .write = dev_write, + .release = dev_release, +}; + +static ssize_t sys_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int len; + + len = sprintf(buf, "%d", numberOpens); + if (len == 0) { + pr_err("Failed to convert int to string\n"); + return 0; + } + pr_info("%s", buf); + pr_info("read %ld\n", (long)strlen(buf)); + return strlen(buf); +} + +static ssize_t clear_buf(struct class *class, struct class_attribute *attr, + const char *buf, size_t count) +{ + size_of_message = 0; + return count; +} + +static int create_buffer(void) +{ + size_buffer = kmalloc(PROC_SYS_BUF, GFP_KERNEL); + if (size_buffer == NULL) + return -ENOMEM; + + volume_buffer = kmalloc(PROC_SYS_BUF, GFP_KERNEL); + if (volume_buffer == NULL) + return -ENOMEM; + + return 0; +} + +static void cleanup_buffer(char *buff) +{ + kfree(buff); + buff = NULL; +} + +static int create_proc_dir(void) +{ + proc_dir = proc_mkdir(PROC_DIRECTORY, NULL); + if (proc_dir == NULL) + return -EFAULT; + + proc_file = proc_create(PROC_FILENAME_1, S_IFREG | 0444, proc_dir, + &volume_fops); + if (proc_file == NULL) + return -EFAULT; + + proc_file2 = proc_create(PROC_FILENAME_2, S_IFREG | 0444, proc_dir, + &size_fops); + if (proc_file == NULL) + return -EFAULT; + + return 0; +} +static int size_read(struct file *file_p, char __user *buffer, size_t _length, + loff_t *offset) +{ + size_t left; + int len; + + if (*offset > 0) + return 0; + + len = sprintf(size_buffer, "%d", size_of_message); + if (len == 0) { + pr_err("Failed to convert int to string\n"); + return 0; + } + + left = copy_to_user(buffer, size_buffer, len); + if (left) + pr_err("Failed to read %u from %u chars\n", left, + length); + else + pr_notice("Read %u chars, %s\n", len, buffer); + + return len; +} + +static int volume_read(struct file *file_p, char __user *buffer, size_t _length, + loff_t *offset) +{ + size_t left; + int len; + + if (*offset > 0) + return 0; + + len = sprintf(volume_buffer, "%d", length); + if (len == 0) { + pr_err("Failed to convert int to string\n"); + return 0; + } + + left = copy_to_user(buffer, volume_buffer, len); + if (left) + pr_err("Failed to read %u from %u chars\n", left, + length); + else + pr_notice("Read %u chars, %s\n", len, buffer); + + return len; +} + +static void cleanup_proc(void) +{ + if (proc_file2) { + remove_proc_entry(PROC_FILENAME_2, proc_dir); + proc_file = NULL; + } + + if (proc_file) { + remove_proc_entry(PROC_FILENAME_1, proc_dir); + proc_file = NULL; + } + + if (proc_dir) { + remove_proc_entry(PROC_DIRECTORY, NULL); + proc_dir = NULL; + } +} + +CLASS_ATTR(numop, 0444, &sys_show, NULL); +CLASS_ATTR(clear, 0664, NULL, &clear_buf); + +static struct class *sys_class; + +int create_sys_file(void) +{ + int res; + + sys_class = class_create(THIS_MODULE, SYS_FILE_NAME); + if (IS_ERR(sys_class)) { + pr_err("bad class create\n"); + return -EINVAL; + } + + res = class_create_file(sys_class, &class_attr_numop); + if (res != 0) { + pr_err("bad sys_char class file create\n"); + class_destroy(sys_class); + return -EINVAL; + } + + res = class_create_file(sys_class, &class_attr_clear); + if (res != 0) { + pr_err("bad file 'total_characters_processed' create\n"); + class_destroy(sys_class); + return -EINVAL; + } + + pr_info("sys_char class in sysfs initialized\n"); + return res; +} + +void clean_sys_file(void) +{ + class_remove_file(sys_class, &class_attr_clear); + class_remove_file(sys_class, &class_attr_numop); + class_destroy(sys_class); +} + +static int __init char_init(void) +{ + int err; + if (length > LENGTH_DEF) { + pr_err("Wrong length of buffer"); + return -EINVAL; + } + + pr_info("Initializing task10 module\n"); + + majorNumber = register_chrdev(0, DEVICE_NAME, &fops); + if (majorNumber < 0) { + pr_err("Failed to register a major number\n"); + return majorNumber; + } + + pr_info("Registered correctly with major number %d\n", majorNumber); + + charClass = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(charClass)) { + unregister_chrdev(majorNumber, DEVICE_NAME); + pr_err("Failed to register device class\n"); + return PTR_ERR(charClass); + } + + pr_info("Device class registered correctly\n"); + + charDevice = device_create(charClass, NULL, + MKDEV(majorNumber, 0), NULL, DEVICE_NAME); + + if (IS_ERR(charDevice)) { + class_destroy(charClass); + unregister_chrdev(majorNumber, DEVICE_NAME); + pr_err("Failed to create the device\n"); + return PTR_ERR(charDevice); + } + + message = kmalloc(length * sizeof(char), GFP_KERNEL); + if (message == NULL) + return -ENOMEM; + + pr_info("Buffer size is %d\n", length); + pr_info(" Char device class created correctly\n"); + + err = create_buffer(); + if (err) + goto error; + + err = create_proc_dir(); + if (err) + goto error; + pr_notice("Procfs volume and size loaded\n"); + + err = create_sys_file(); + if (err) + goto error; + + return 0; + +error: + pr_err("Failed to load\n"); + cleanup_proc(); + cleanup_buffer(volume_buffer); + cleanup_buffer(size_buffer); + return err; +} + +static void __exit char_exit(void) +{ + device_destroy(charClass, MKDEV(majorNumber, 0)); + class_unregister(charClass); + class_destroy(charClass); + unregister_chrdev(majorNumber, DEVICE_NAME); + kfree(message); + cleanup_proc(); + cleanup_buffer(volume_buffer); + cleanup_buffer(size_buffer); + clean_sys_file(); + pr_info("Bye!\n"); +} + +static int dev_open(struct inode *inodep, struct file *filep) +{ + numberOpens++; + pr_info("Device has been opened %d times\n", numberOpens); + return 0; +} + +static ssize_t dev_read(struct file *filep, char *buffer, size_t len, + loff_t *offset) +{ + int ret = 0; + + if (*offset) + return 0; + + ret = copy_to_user(buffer, message, size_of_message); + if (ret != 0) + return -EFAULT; + + + *offset = size_of_message; + + return *offset; +} + +static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, + loff_t *offset) +{ + int ret; + + if (len > sizeof(message) - 1) + size_of_message = sizeof(message) - 1; + + ret = copy_from_user(message, buffer, len); + if (ret != 0) + return -EFAULT; + + message[len] = '\0'; + size_of_message = len; + + pr_info("Received %zu characters from the user\n", len); + return len; +} + +static int dev_release(struct inode *inodep, struct file *filep) +{ + pr_info("Device successfully closed\n"); + return 0; +} + +module_init(char_init); +module_exit(char_exit); diff --git a/10_cdev/logs.txt b/10_cdev/logs.txt new file mode 100644 index 00000000..92ed4a23 --- /dev/null +++ b/10_cdev/logs.txt @@ -0,0 +1,65 @@ +rmmod: remove 'chardev': No such file or directory ++ insmod chardev.ko +[ 632.048552] Initializing task10 module +[ 632.049124] Registered correctly with major number 511 +[ 632.049550] Device class registered correctly +[ 632.050771] Buffer size is 1024 +[ 632.050965] Char device class created correctly +[ 632.051309] Procfs volume and size loaded +[ 632.051737] sys_char class in sysfs initialized +[ 632.057681] Device has been opened 1 times ++ echo Hello Anton +[ 632.062149] Received 12 characters from the user +[ 632.062685] Device successfully closed ++ cat /dev/char_dev +[ 632.234358] Device has been opened 2 times +Hello Anton +[ 632.235394] Device successfully closed ++ cat /proc/proc_chdev/buf_volume +[ 632.454154] Read 4 chars, 1024�O���H�۠H�ۀH��`H��@H�� H�� +1024+ cat /proc/proc_chdev/buf_size +[ 632.553114] Read 2 chars, 12 +12+ cat /sys/class/sys_char/numop +[ 632.627791] 2 +[ 632.627821] read 1 +2+ echo 1 +[ 632.642913] Device has been opened 3 times ++ echo -n 'It is a period of civil war. Rebel spaceships, striking from a hidden base, have won their first victory against the evil Galactic Empire. During the battle, Rebel spies managed to steal secret plans to the Empire’s ultimate weapon, the DEATH STAR, an armored space station with enough power to destroy an entire planet. Pursued by the Empire’s sinister agents, Princess Leia races home aboard her starship, custodian of the stolen plans that can save her people and restore freedom to the galaxy….' +[ 632.648771] Received 504 characters from the user +[ 632.650473] Device successfully closed ++ sleep 0.1 ++ cat /dev/char_dev +[ 632.916251] Device has been opened 4 times +It is a period of civil war. Rebel spaceships, striking from a hidden base, have won their first victory against the evil Galactic Empire. During the battle, Rebel spies managed to steal secret plans to the Empire’s ultimate weapon, the DEATH STAR, an armored space station with enough power to destroy an entire planet. Pursued by the Empire’s sinister agents, Princess Leia races home aboard her starship, custodian of the stolen plans that can save her people and restore freedom to the galaxy….[ 632.918580] Device successfully closed ++ cat /proc/proc_chdev/buf_volume +[ 633.000161] Read 4 chars, 1024 +1024+ cat /proc/proc_chdev/buf_size +[ 633.079677] Read 3 chars, 504 +504+ cat /sys/class/sys_char/numop +[ 633.165136] 4 +[ 633.165164] read 1 +4+ echo 1 ++ sleep 0.1 ++ rmmod chardev.ko +[ 633.470717] ------------[ cut here ]------------ +[ 633.471009] WARNING: CPU: 0 PID: 131 at fs/proc/generic.c:683 remove_proc_entry+0x184/0x190 +[ 633.471280] remove_proc_entry: removing non-empty directory '/proc/proc_chdev', leaking at least 'buf_volume' +[ 633.471787] Modules linked in: chardev(O-) [last unloaded: chardev] +[ 633.472082] CPU: 0 PID: 131 Comm: rmmod Tainted: G W O 4.19.166 #1 +[ 633.472426] Hardware name: Generic DT based system +[ 633.472685] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) +[ 633.473065] [] (show_stack) from [] (dump_stack+0xc0/0xd4) +[ 633.473407] [] (dump_stack) from [] (__warn.part.3+0xbc/0xd8) +[ 633.473747] [] (__warn.part.3) from [] (warn_slowpath_fmt+0x68/0x8c) +[ 633.474097] [] (warn_slowpath_fmt) from [] (remove_proc_entry+0x184/0x190) +[ 633.474504] [] (remove_proc_entry) from [] (cleanup_proc+0x70/0x7c [chardev]) +[ 633.474903] [] (cleanup_proc [chardev]) from [] (char_exit+0x50/0xaa0 [chardev]) +[ 633.475307] [] (char_exit [chardev]) from [] (sys_delete_module+0x168/0x1ec) +[ 633.475829] [] (sys_delete_module) from [] (ret_fast_syscall+0x0/0x54) +[ 633.476169] Exception stack(0xdb0abfa8 to 0xdb0abff0) +[ 633.476517] bfa0: 00766564 00000000 beffcad8 00000880 00000007 72616863 +[ 633.477036] bfc0: 00766564 00000000 00000000 00000081 00000001 0011ad10 b6fc8000 0011a784 +[ 633.477508] bfe0: beffcad8 beffcac8 0003a61c b6e7f1b0 +[ 633.477794] ---[ end trace 8775f322ecf1d9f8 ]--- +[ 633.478268] Bye! + diff --git a/10_cdev/test.sh b/10_cdev/test.sh new file mode 100644 index 00000000..3113e80d --- /dev/null +++ b/10_cdev/test.sh @@ -0,0 +1,24 @@ +#!/bin/sh -x + +rm -f /dev/cdev + +rmmod chardev.ko +insmod chardev.ko + +echo Hello Anton > /dev/char_dev +cat /dev/char_dev +cat /proc/proc_chdev/buf_volume +cat /proc/proc_chdev/buf_size +cat /sys/class/sys_char/numop +echo 1 > /sys/class/sys_char/clear + +echo -n "It is a period of civil war. Rebel spaceships, striking from a hidden base, have won their first victory against the evil Galactic Empire. During the battle, Rebel spies managed to steal secret plans to the Empire’s ultimate weapon, the DEATH STAR, an armored space station with enough power to destroy an entire planet. Pursued by the Empire’s sinister agents, Princess Leia races home aboard her starship, custodian of the stolen plans that can save her people and restore freedom to the galaxy…." > /dev/char_dev +sleep 0.1 +cat /dev/char_dev +cat /proc/proc_chdev/buf_volume +cat /proc/proc_chdev/buf_size +cat /sys/class/sys_char/numop +echo 1 > /sys/class/sys_char/clear +sleep 0.1 + +rmmod chardev.ko