/*
 * Implement the hello, world character device driver.
 * Sad, but true.
 * Create the device with:
 *
 * mknod /dev/hw c 31 0
 *
 * - Jon. Tombs Dec '93
 *
 * TODO:
 *  o- Ioctl to change the return string?
 *  o- Better error treatment.
 *  o- Support of minor numbers.
 */


/* Kernel includes */

#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <asm/segment.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/module.h>

/*
 * NB. we must include the kernel idenfication string in to install the module.
 */
#include "/linux/tools/version.h"
static char kernel_version[] = UTS_RELEASE;


/* the magic return string */
static char hw_reply[]	= "Hello World\n";


#define HW_MAJOR    31 /* nice and high */

/*
 * The driver.
 */

/*
 * Read requests on the hello world device.
 * The magic string is copied byte by byte to user space.
 * Who cares about speed?
 */
static int hw_read(struct inode * node,struct file * file,char * buf,int count)
{
	int left;

	int hw_pos = file->f_pos % (sizeof(hw_reply) - 1);

	for (left = count; left > 0; left--) {
	 
		put_fs_byte(hw_reply[hw_pos++],buf);
		buf++;
		if (hw_pos == (sizeof(hw_reply) - 1))
		  hw_pos = 0;
	}
	file->f_pos+=count;
	return count;
}

/*
 * support seeks on the device
 */
static int hw_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
{
	switch (orig) {
		case 0:
			file->f_pos = offset;
			return file->f_pos;
		case 1:
			file->f_pos += offset;
			return file->f_pos;
		default: /* how does the device have an end? */
			return -EINVAL;
	}
}


/*
 * Our special open code.
 * MOD_INC_USE_COUNT make sure that the driver memory is not freed
 * while the device is in use.
 */
static int
hw_open( struct inode* ino, struct file* filep)
{
   MOD_INC_USE_COUNT;
   return 0;   
}

/*
 * Now decrement the use count.
 */
static void
hw_close( struct inode* ino, struct file* filep)
{
   MOD_DEC_USE_COUNT;
}

static struct file_operations hw_fops = {
	hw_lseek,
	hw_read,
	NULL,
	NULL,		/* hw_readdir */
	NULL,		/* hw_select */
	NULL,		/* hw_ioctl */
	NULL,
	hw_open,
	hw_close,
	NULL		/* fsync */
};


/*
 * And now the modules code and kernel interface.
 */



#ifdef __cplusplus
extern "C" {
#endif


extern int printk( const char* fmt, ...);


int
init_module( void) {
	printk( "drv_hello.c:  init_module called\n");
	if (register_chrdev(HW_MAJOR, "hw", &hw_fops)) {
	  printk("register_chrdev failed: goodbye world :-(\n");
	  return -EIO;
	} else
	  printk( "Hello, world\n");
	
	return 0;
}

void
cleanup_module( void) {
  printk( "drv_hello.c:  cleanup_module called\n");

  if (MOD_IN_USE)
    printk("hw: device busy, remove delayed\n");

   
  if (unregister_chrdev(HW_MAJOR, "hw") != 0) {
    printk("cleanup_module failed\n");
  } else {
    printk("cleanup_module succeeded\n");
  }    
}

#ifdef __cplusplus
}
#endif

