#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/mutex.h>

#include <asm/uaccess.h>
#include <linux/proc_fs.h>

#define HELLO_VERSION "0.1"

MODULE_DESCRIPTION("Hello Module");
MODULE_VERSION(HELLO_VERSION);
MODULE_AUTHOR("Copyright (C) You");
MODULE_LICENSE("GPL");

#define HELLO_LEN 1024

static char hello_data[HELLO_LEN];

DEFINE_MUTEX(hello_mutex);

static struct proc_dir_entry *hello_file;

static int hello_parameter;

static int hello_read_proc(char *page, char **start, off_t off,
			   int count, int *eof, void *data)
{
	char *p = page;
	p += sprintf(p, "I am a kernel module!\n");

	mutex_lock(&hello_mutex);
	if (hello_data[0] != '\0')
		p += sprintf(p, "string is -> [%s]\n", hello_data);
	mutex_unlock(&hello_mutex);

	// signal end of file
	*eof = 1;

	return p-page;
}

static int hello_write_proc(struct file *file, const char __user *buffer,
			    unsigned long count, void *data)
{
	unsigned long ocount = count;

	if (!count)
		return -EINVAL;
	if (count > HELLO_LEN-1)
		count = HELLO_LEN-1;

	mutex_lock(&hello_mutex);
	if (copy_from_user(hello_data, buffer, count))
		return -EFAULT;
	mutex_unlock(&hello_mutex);

	hello_data[count] = '\0';

	return ocount;
}

static int __init hello_init(void)
{
	printk(KERN_DEBUG "[hello_init]\n");

	hello_file = create_proc_entry("hello",
				       0666, NULL);
	if (hello_file) {
		hello_file->owner = THIS_MODULE;
		hello_file->read_proc = hello_read_proc;
		hello_file->write_proc = hello_write_proc;
	}

	return 0;
}

static void __exit hello_exit(void)
{
	printk(KERN_DEBUG "[hello_exit]\n");

	remove_proc_entry("hello", NULL);

	return;
}

module_param(hello_parameter, int, 0444);
MODULE_PARM_DESC(hello_parameter, "Some Parameter");

module_exit(hello_exit);
module_init(hello_init);
