/*
 *  linux/drivers/char/dmesg.c
 *
 *  Copyright (C) 2004  Ben Love
 *
 */

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

#define SUCCESS 0
#define DEVICE_MAJOR 45
#define DEVICE_NAME "dmesg"
#define MAX_BUF_LEN 80			/* one line of text */

/* When we retrieve the mutex, the device is open. */
static DECLARE_MUTEX (dmesg_sem);

static char dmesg_message[MAX_BUF_LEN + 1];

static int dmesg_open (struct inode *, struct file *);
static int dmesg_release (struct inode *, struct file *);
static ssize_t dmesg_read (struct file *, char *, size_t, loff_t *);
static ssize_t dmesg_write (struct file *, const char *, size_t, loff_t *);

struct file_operations fops = {
	.open = dmesg_open,
	.read = dmesg_read,
	.write = dmesg_write,
	.release = dmesg_release,
};

static int dmesg_open (struct inode * inode, struct file * file)
{
	int result;

	if (!suser())
		return -EACCES;

	result = down_trylock (&dmesg_sem);
	if (result != 0)
		return -EBUSY;
		
	MOD_INC_USE_COUNT;

	return SUCCESS;
}

static int dmesg_release (struct inode * inode, struct file * file)
{
	up (&dmesg_sem);
	
	MOD_DEC_USE_COUNT;

	return SUCCESS;
}

static int dmesg_read (struct file * fp, char * buffer, size_t length, loff_t *offset)
{
	/* Cannot read from dmesg this way. */

	if (!suser())
		return -EACCES;

	return -EPERM;
}

static int dmesg_write (struct file *fp, const char *buffer, size_t length, loff_t *offset)
{
	size_t ctr;

	if (!suser())
		return -EACCES;

	for (ctr = 0; ctr < length && ctr <= MAX_BUF_LEN; ctr++)
	{
		get_user (dmesg_message[ctr], buffer+ctr);
	}

	dmesg_message[ctr] = 0;

	printk (KERN_INFO "%s", dmesg_message);

	return ctr;
}

static void __exit dmesg_finish (void)
{
	int result;

	result = unregister_chrdev (DEVICE_MAJOR, DEVICE_NAME);
	if (result < 0)
	{
		printk (KERN_ERR "Failed to unregister dmesg device: %d\n", result);
		return;
	}
	
	printk (KERN_INFO "Unloading dmesg logging device\n");

	/*
	   if ((e1 = tty_unregister_driver(&serial_driver)))
	   printk("serial: failed to unregister serial driver (%d)\n",
	   e1);
	   if ((e2 = tty_unregister_driver(&callout_driver)))
	   printk("serial: failed to unregister callout driver (%d)\n", 
	   e2);
	   restore_flags(flags);
	 */
}

/*
 * The serial driver boot-time initialization code!
 */
static int __init dmesg_init (void)
{
	int result;

	result = register_chrdev (DEVICE_MAJOR, DEVICE_NAME, &fops);
	if (result < 0)
	{
		printk (KERN_ERR "Failed to register dmesg device: %d\n", result);
		return result;
	}

	printk (KERN_INFO "Starting dmesg logging device.\n");

	return SUCCESS;
}

module_init (dmesg_init);
module_exit (dmesg_finish);
MODULE_DESCRIPTION ("dmesg logging device");
MODULE_AUTHOR ("Ben Love <blove@christian.net>");
MODULE_LICENSE ("GPL");
/* Declare that this module supports /dev/dmesg */
MODULE_SUPPORTED_DEVICE ("dmesg");
