commit 7cd528626db3006de498feb0ac936f2c246d66a1 Author: KoroLion Date: Fri Jan 8 15:18:01 2021 +0300 Initial commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8536d48 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +BINARY := kl_char_driver +KERNEL := /lib/modules/$(shell uname -r)/build +KMOD_DIR := $(shell pwd) + +ccflags-y += -Wall +obj-m += $(BINARY).o +$(BINARY)-y := main.o + +$(BINARY).ko: main.c + make -C $(KERNEL) M=$(KMOD_DIR) modules + +showlog: + tail -n10 /var/log/messages +enable: $(BINARY).ko + -insmod $(BINARY).ko +disable: + -rmmod $(BINARY) +check: disable enable + echo wolf228 > /dev/kl_char_dev-0 && head -1 /dev/kl_char_dev-0 && make showlog + +clean: + make -C $(KERNEL) M=$(KMOD_DIR) clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e86a64 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +This driver creates devices in /dev. You could save anything to these devices and then read. + +Compile and check driver using **sudo make check** diff --git a/main.c b/main.c new file mode 100644 index 0000000..d365d9a --- /dev/null +++ b/main.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include +#include + +#define DEVICES_AMOUNT 3 +#define MAX_DATA_LEN 64 +const char DEVICE_NAME[] = "kl_char_dev"; + +static char user_data[DEVICES_AMOUNT][MAX_DATA_LEN]; + +static int char_dev_open(struct inode *inode, struct file *file); +static int char_dev_release(struct inode *inode, struct file *file); +static ssize_t char_dev_read(struct file *file, char __user *buf, size_t count, loff_t *offset); +static ssize_t char_dev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset); + +static const struct file_operations char_dev_fops = { + .owner = THIS_MODULE, + + .open = char_dev_open, + .release = char_dev_release, + .read = char_dev_read, + .write = char_dev_write +}; + +struct CHAR_DEV_DATA { + struct cdev cdev; +}; + +static int dev_major = 0; +static struct class *char_dev_class = NULL; +static struct CHAR_DEV_DATA char_dev_data[DEVICES_AMOUNT]; + +static int char_dev_uevent(struct device *dev, struct kobj_uevent_env *env) { + // permissions for device file + add_uevent_var(env, "DEVMODE=%#o", 0666); + return 0; +} + +static int char_dev_init(void) { + int err, i; + dev_t dev; + size_t dev_num; + + err = alloc_chrdev_region(&dev, 0, DEVICES_AMOUNT, DEVICE_NAME); + + dev_major = MAJOR(dev); + + char_dev_class = class_create(THIS_MODULE, DEVICE_NAME); + char_dev_class->dev_uevent = char_dev_uevent; + + for (i = 0; i < DEVICES_AMOUNT; i++) { + cdev_init(&char_dev_data[i].cdev, &char_dev_fops); + char_dev_data[i].cdev.owner = THIS_MODULE; + + dev_num = MKDEV(dev_major, i); + cdev_add(&char_dev_data[i].cdev, dev_num, 1); + device_create(char_dev_class, NULL, dev_num, NULL, "%s-%d", DEVICE_NAME, i); + + if (i % 2 == 0) { + strncpy(user_data[i], "WOLF\n", MAX_DATA_LEN); + } else { + strncpy(user_data[i], "LION\n", MAX_DATA_LEN); + } + } + + return 0; +} + +static void char_dev_exit(void) { + int i; + + for (i = 0; i < DEVICES_AMOUNT; i++) { + device_destroy(char_dev_class, MKDEV(dev_major, i)); + } + + class_unregister(char_dev_class); + class_destroy(char_dev_class); + + unregister_chrdev_region(MKDEV(dev_major, 0), MINORMASK); +} + +static int char_dev_open(struct inode *inode, struct file *file) { + int minor = MINOR(file->f_path.dentry->d_inode->i_rdev); + printk(KERN_INFO "#%s: minor %d opened\n", DEVICE_NAME, minor); + return 0; +} + +static int char_dev_release(struct inode *inode, struct file *file) { + int minor = MINOR(file->f_path.dentry->d_inode->i_rdev); + printk(KERN_INFO "#%s: minor %d closed\n", DEVICE_NAME, minor); + return 0; +} + +static ssize_t char_dev_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { + size_t datalen; + + int minor = MINOR(file->f_path.dentry->d_inode->i_rdev); + printk(KERN_INFO "#%s: reading from minor %d\n", DEVICE_NAME, minor); + + datalen = strlen(user_data[minor]); + + if (count > datalen) { + count = datalen; + } + + if (copy_to_user(buf, user_data[minor], count)) { + return -EFAULT; + } + + return count; +} + +static ssize_t char_dev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) { + size_t not_copied; + + int minor = MINOR(file->f_path.dentry->d_inode->i_rdev); + printk(KERN_INFO "#%s: writing to minor %d\n", DEVICE_NAME, minor); + + if (count > MAX_DATA_LEN) { + count = MAX_DATA_LEN; + } + + not_copied = copy_from_user(user_data[minor], buf, count); + if (not_copied != 0) { + printk(KERN_WARNING "#%s: unable to copy %zd bytes from the user\n", DEVICE_NAME, not_copied); + } + + user_data[minor][count] = 0; + + printk(KERN_INFO "#%s: received from user: %s", DEVICE_NAME, user_data[minor]); + + return count; +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("KoroLion "); + +module_init(char_dev_init); +module_exit(char_dev_exit);