/*  Modified by:      Christian Löpke <christian.loepke@hs-bochum.de>
 *  chardev-1write.c: Creates a char device that says Mr. Anderson or a 
 *                    written name how many times he read from the dev
 *                    file.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>	/* for put_user */

/*  
 *  Prototypes - this would normally go in a .h file
 */
int init_module(void);
void cleanup_module(void);
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);

#define SUCCESS 0
#define DEVICE_NAME "chardev"	/* Dev name as it appears in /proc/devices   */
#define BUF_LEN 80		/* Max length of the message from the device */
#define USERBUF_LEN 20		/* Max length for user definable name */
/* 
 * Global variables are declared as static, so are global within the file. 
 */

static int Major;	    /* Major number assigned to our device driver */
static int Device_Open = 0; /* Is device open?  
			     * Used to prevent multiple access to device */
static char msg[BUF_LEN];   /* The msg the device will give when asked */
static char *msg_Ptr;	    /* The current char from msg we are writing */

static char msg_username[USERBUF_LEN]; /* User definable name */
static int told_you_counter = 0; /* Counts the outputs I've made */

static struct file_operations fops = {
	.read = device_read,
	.write = device_write,
	.open = device_open,
	.release = device_release
};

/*
 * This function is called when the module is loaded
 */
int init_module(void)
{
        Major = register_chrdev(0, DEVICE_NAME, &fops);

	if (Major < 0) {
	  printk(	KERN_ALERT 
			"%s: Registering char device failed with %d\n", 
			DEVICE_NAME,			
			Major);
	  return Major;
	}

	printk(	KERN_INFO "%s: I was assigned major number %d. To talk to\n",
		DEVICE_NAME,		
		Major);
	printk(	KERN_INFO 
		"%s: the driver, create a dev file with\n", DEVICE_NAME);
	printk(	KERN_INFO "%s: 'mknod /dev/%s c %d 0'.\n",
		DEVICE_NAME,
		DEVICE_NAME,
		Major);
	printk(	KERN_INFO 
		"%s: Try various minor numbers. Try to cat and echo to\n",
		DEVICE_NAME);
	printk(	KERN_INFO "%s: the device file.\n", DEVICE_NAME);
	printk(	KERN_INFO 
		"%s: Remove the device file and module when done.\n", 
		DEVICE_NAME);

	sprintf(msg_username, "Mr. Anderson"); //Setup initial username.
	return SUCCESS;
}

/*
 * This function is called when the module is unloaded
 */
void cleanup_module(void)
{
	/* 
	 * Unregister the device 
	 */
	unregister_chrdev(Major, DEVICE_NAME);
}

/*
 * Methods
 */

/* 
 * Called when a process tries to open the device file, like
 * "cat /dev/mycharfile"
 */
static int device_open(struct inode *inode, struct file *file)
{
	if (Device_Open)
		return -EBUSY;

	Device_Open++;
	sprintf(msg,
		"I already told you %d times Hello world!, %s.\n",
		told_you_counter++,
		msg_username);
	msg_Ptr = msg;
	try_module_get(THIS_MODULE);

	return SUCCESS; /* Device successfully opened */
}

/* 
 * Called when a process closes the device file.
 */
static int device_release(struct inode *inode, struct file *file)
{
	Device_Open--;		/* We're now ready for our next caller */

	/* 
	 * Decrement the usage count, or else once you opened the file, you'll
	 * never get get rid of the module. 
	 */
	module_put(THIS_MODULE);

	return 0; /* Device sucessfully closed */
}

/* 
 * Called when a process, which already opened the dev file, attempts to
 * read from it.
 */
static ssize_t device_read(struct file *filp,	/* see include/linux/fs.h   */
			   char *buffer,	/* buffer to fill with data */
			   size_t length,	/* length of the buffer     */
			   loff_t * offset)
{
	/*
	 * Number of bytes actually written to the buffer 
	 */
	int bytes_read = 0;

	/*
	 * If we're at the end of the message, 
	 * return 0 signifying end of file 
	 */
	if (*msg_Ptr == 0)
		return 0;

	/* 
	 * Actually put the data into the buffer 
	 */
	while (length && *msg_Ptr) {

		/* 
		 * The buffer is in the user data segment, not the kernel 
		 * segment so "*" assignment won't work.  We have to use 
		 * put_user which copies data from the kernel data segment to
		 * the user data segment. 
		 */
		put_user(*(msg_Ptr++), buffer++);

		length--;
		bytes_read++;
	}

	/* 
	 * Most read functions return the number of bytes put into the buffer
	 */
	return bytes_read;
}

/*  
 * Called when a process writes to dev file: echo "hi" > /dev/hello 
 */
static ssize_t
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
	/* Since nobody wants to read I have to correct the already told you
	   counter! */
	told_you_counter--;
	if(len >= USERBUF_LEN) {
		printk(	KERN_WARNING 
			"%s: Sorry, your input is too long. Max %d allowed!\n",
			DEVICE_NAME,
			USERBUF_LEN);	  
		return -ENOMEM; //Return not enaugh memory
	}
	else {
		int i;
		for(i = 0; i < len; i++) {//Save the given string
			if(get_user(msg_username[i], buff + i) < 0) { //If returning error code
				printk(KERN_WARNING 
					   "%s: This memory does not belong to you!\n",
						DEVICE_NAME);
				return -EFAULT; //Stop reading from userspace.
			}			
			//msg_username[i] = buff[i]; //Heres was the CRASH
			//printk(KERN_INFO "Got %c at %d from %d bytes.\n", buff[i], i, len);
		}
		for(i = len; i < USERBUF_LEN; i++) //Zeroing the rest of mem
			msg_username[i] = 0;
		printk(KERN_INFO "%s: Successfully got %d bytes!\n",
		       DEVICE_NAME, (int)len);
		if(msg_username[len-1] == '\n') //Got new line terminator ?
		  msg_username[len-1] = 0; //Then terminate it!
		return len; //Emit successfully stored len bytes
	}	
}