/*****************************************************************************
 * $Id: proc_echo.c 1.1 2002/08/25 16:32:27 jimb Exp jimb $
 * Author   : Jim Brooks <linux@jimbrooks.org>
 * Purpose  : Linux kernel 2.4 module that creates a /proc/echo file.
 *            Echos (single-shot) what was previously written to it.
 *            Can be used as a skeleton for a read/write /proc file.
 *
 *            echo "This is a test." > /proc/echo
 *            cat /proc/echo
 *            This is a test.
 *
 * Legal    : Released without restrictions into the public domain.
 *            This software is provided "AS-IS" without any express or implied
 *            warranty nor guarantee.  In no event will the author be held
 *            liable for any damages arising from the use of this software
 *            nor derivative works.
 *****************************************************************************/

#define MODULE
#define __KERNEL__

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include "proc_echo.h"

/*
 * Defines the /proc file.
 */
static struct proc_dir_entry* pProcDirEntry = NULL;

/*
 * Echo buffer.
 */
#define ECHO_MAX_LEN	1024
static char* pEcho = NULL;
static int echoLen = 0;

/*****************************************************************************
 * This is called when the proc file is read.
 *****************************************************************************/
static int
proc_read( char*  buf,
           char** start,
           off_t  offset,
           int    count,
           int*   eof,
           void*  data )
{
	int		len;

	/*
	 * Was something previously written to the /proc file?
	 */
	if ( pEcho )
	{
		len = MIN(echoLen,count);
		memcpy( &buf[offset], pEcho, len );

		kfree( pEcho );	/* echo finished, free buffer */
		pEcho = NULL;
		echoLen = 0;
	}
	else
	{
		len = 0;
	}

    *eof = 1;
    return len;
}

/*****************************************************************************
 * This is called when the proc file is written to.
 *****************************************************************************/
static int
proc_write( struct file*  file,
            const char*	  bufUser, /* resides in user-space! */
            unsigned long count,
            void*         data )
{
	/*
	 * First, verify the pointer to user space.
	 */
	if ( ! access_ok( VERIFY_READ, bufUser, count ) )
		return -EFAULT;

	/*
	 * Store in kernel-space what is being written to /proc file by user code.
	 */
	echoLen = MIN( count, ECHO_MAX_LEN );
	pEcho = kmalloc( echoLen, GFP_KERNEL );

	copy_from_user( pEcho, bufUser, echoLen );

	return echoLen;
}

/*****************************************************************************
 * Initialize module.
 *****************************************************************************/
int
init_module( void )
{
	/*
	 * Create /proc file.
	 */
	pProcDirEntry = create_proc_entry( "echo", 0600, NULL );
	if ( ! pProcDirEntry )
	{
        printk( KERN_DEBUG "create_proc_entry() failed" );
        return -ENODEV;
	}
	pProcDirEntry->read_proc	= proc_read;
	pProcDirEntry->write_proc	= proc_write;

    return 0;
}

/*****************************************************************************
 * Release module.
 *****************************************************************************/
void
cleanup_module( void )
{
    /*
     * Remove /proc file and free echo buffer.
     */
    if ( pProcDirEntry ) remove_proc_entry( "echo", NULL );
	if ( pEcho ) kfree( pEcho );
}
