// SPDX-License-Identifier: GPL-2.0

//#define DEBUG 1

#include <asm/page.h>
#include <linux/debugfs.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rwsem.h>

#ifdef DEBUG
#include <linux/delay.h>
#endif

#define BUFFER_LENGTH 10

static struct dentry *folder;
static struct dentry *id_file;
static struct dentry *foo_file;

static char *id_msg;
static char *foo_msg;
static struct rw_semaphore foo_sem;

static ssize_t id_read(struct file *, char __user *buffer, size_t length,
		       loff_t *offset)
{
	int bytes = 0;
	const char *msg_tmp = id_msg;

	/*
	 * TODO
	 if (!*(msg_tmp + *offset)) {
	 *offset = 0;
	 return 0;
	 }
	 */

	msg_tmp += *offset;

	while (*msg_tmp && bytes < length) {
		put_user(*(msg_tmp), (buffer + bytes));
		bytes++;
		msg_tmp++;
	}

	*offset += bytes;
	return bytes;
}

static ssize_t id_write(struct file *, const char __user *buffer,
			size_t size, loff_t *)
{
	int ret;
	char *tmp_buffer;

	if (size == 0)
		return 0;

	tmp_buffer = kzalloc(BUFFER_LENGTH, GFP_KERNEL);

	if (copy_from_user(tmp_buffer, buffer, BUFFER_LENGTH)) {
		kfree(tmp_buffer);
		return -EINVAL;
	}

	*(tmp_buffer + BUFFER_LENGTH - 1) = '\0';
	pr_info("Test %s-\n", tmp_buffer);

	if (!strcmp(tmp_buffer, "congruent"))
		ret = BUFFER_LENGTH;
	else
		ret = -EINVAL;

	kfree(tmp_buffer);
	return ret;
}

static const struct file_operations id_fops = {
	.owner = THIS_MODULE,
	.read = id_read,
	.write = id_write,
};

static ssize_t foo_read(struct file *, char __user *buffer, size_t count,
			loff_t *offset)
{
	int bytes = -EINVAL;
	int tmp_len = strlen(foo_msg);

	if (*offset > tmp_len) {
		bytes = -EFAULT;
	} else if (down_read_trylock(&foo_sem)) {
		char *tmp_str = foo_msg + *offset;

		tmp_len = strlen(tmp_str);
		bytes = tmp_len > count ? count : tmp_len;

		if (copy_to_user(buffer, tmp_str, bytes))
			bytes = -EFAULT;
		else
			*offset += bytes;
#ifdef DEBUG
		msleep(5000);
#endif
		up_read(&foo_sem);
	}

	return bytes;
}

static ssize_t foo_write(struct file *, const char __user *buffer,
			 size_t count, loff_t *offset)
{
	int bytes = -EINVAL;

	if ((*offset + count) > PAGE_SIZE) {
		bytes = -EFAULT;
	} else if (down_write_trylock(&foo_sem)) {
		bytes = count;
		if (copy_from_user(foo_msg, buffer, bytes))
			bytes = -EINVAL;
#ifdef DEBUG
		msleep(5000);
#endif
		up_write(&foo_sem);
	}

	return bytes;
}

static const struct file_operations foo_fops = {
	.owner = THIS_MODULE,
	.read = foo_read,
	.write = foo_write,
};

static int __init my_init(void)
{
	init_rwsem(&foo_sem);
	id_msg = kmalloc(12, GFP_KERNEL);
	foo_msg = kmalloc(PAGE_SIZE, GFP_KERNEL);
	strscpy(id_msg, "pouetpouet\n", 12);
	pr_info("Coucou le gens !!!!\n");

	folder = debugfs_create_dir("eudyptula", NULL);
	id_file = debugfs_create_file("id", 0666, folder, NULL, &id_fops);
	debugfs_create_u64("jiffies", 0444, folder, &jiffies_64);
	foo_file = debugfs_create_file("foo", 0664, folder, NULL, &foo_fops);

	return 0;
}

static void __exit my_exit(void)
{
	debugfs_remove_recursive(folder);
	kfree(id_msg);
	kfree(foo_msg);
	pr_info("Tschuss !!!\n");
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("rick <rick@gnous.eu>");
MODULE_DESCRIPTION("Module pour tester le debugfs.");