// SPDX-License-Identifier: GPL-2.0
/*
 * Minimal Linux kernel module example.
 *
 * This module creates /dev/hello_kmod. Reading from it returns a short
 * message. It is intentionally small so the basic driver shape is visible.
 */

#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "hello_kmod"
#define CLASS_NAME "hello_class"

static int major_number;
static struct class *hello_class;
static struct device *hello_device;
static const char message[] = "hello from a tiny kernel driver\n";

static int hello_open(struct inode *inode, struct file *file)
{
	pr_info("hello_kmod: device opened\n");
	return 0;
}

static int hello_release(struct inode *inode, struct file *file)
{
	pr_info("hello_kmod: device closed\n");
	return 0;
}

static ssize_t hello_read(struct file *file, char __user *buffer,
			  size_t length, loff_t *offset)
{
	size_t message_len = sizeof(message) - 1;
	size_t bytes_to_copy;

	if (*offset >= message_len)
		return 0;

	bytes_to_copy = min(length, message_len - (size_t)*offset);

	if (copy_to_user(buffer, message + *offset, bytes_to_copy))
		return -EFAULT;

	*offset += bytes_to_copy;
	return bytes_to_copy;
}

static const struct file_operations hello_fops = {
	.owner = THIS_MODULE,
	.open = hello_open,
	.read = hello_read,
	.release = hello_release,
};

static int __init hello_init(void)
{
	major_number = register_chrdev(0, DEVICE_NAME, &hello_fops);
	if (major_number < 0) {
		pr_err("hello_kmod: failed to register char device\n");
		return major_number;
	}

	hello_class = class_create(CLASS_NAME);
	if (IS_ERR(hello_class)) {
		unregister_chrdev(major_number, DEVICE_NAME);
		return PTR_ERR(hello_class);
	}

	hello_device = device_create(hello_class, NULL, MKDEV(major_number, 0),
				     NULL, DEVICE_NAME);
	if (IS_ERR(hello_device)) {
		class_destroy(hello_class);
		unregister_chrdev(major_number, DEVICE_NAME);
		return PTR_ERR(hello_device);
	}

	pr_info("hello_kmod: loaded as /dev/%s with major %d\n",
		DEVICE_NAME, major_number);
	return 0;
}

static void __exit hello_exit(void)
{
	device_destroy(hello_class, MKDEV(major_number, 0));
	class_destroy(hello_class);
	unregister_chrdev(major_number, DEVICE_NAME);
	pr_info("hello_kmod: unloaded\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Digital Rhyme Knowledge Base");
MODULE_DESCRIPTION("Minimal Linux character device kernel module");
