/* ---------------------------------------------------------------------- */
/*
	linux/drivers/char/foo_sample.c

						Apr/06/98

*/
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
/*
 *
 * This module exports the foo io functions:
 * 
 *     'foo_open()'
 *     'foo_write()'
 *     'foo_read()'
 *     'foo_release()'
 *
 */
/* ---------------------------------------------------------------------- */

// #undef  FOO_DEBUG               /* Do you want to debug the driver?       */
#define  FOO_DEBUG
#define FOO_NAME "foo_sample"      /* A name for the driver                  */

#include <linux/module.h>

#include <linux/config.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/malloc.h>
#include <linux/fcntl.h>
#include <linux/delay.h>


#include <asm/system.h>


#include <linux/foo_sample.h>   /* foo driver definitions and structures  */

#include <linux/time.h>         /* For struct timeval                     */
#include <linux/major.h>        /* For major devices numbers              */
#include <linux/fs.h>           /* For struct file_operations             */
                                /* and register_*dev()                    */
#include <asm/segment.h>        /* For memcpy_*fs()                       */
#include <asm/io.h>             /* For inb*() and outb*                   */
#include <asm/system.h>         /* For assembler nop, cli and sti         */

#include <asm/delay.h>          /* For udelay() < 1ms                     */
#include <linux/ioport.h>       /* For check_region(), request_region(),  */
                                /* release_region() and get_ioport_list() */
#include <linux/mm.h>           /* verify_area() and VERIFY_READ or WRITE */

static int foo_status = NOT_PRESENT;        /* Status of the device       */



/* ---------------------------------------------------------------------- */
static int foo_read	(struct inode *inode, struct file *file,
                     char *buf, int count)
{
	int	num_bytes;

	int	foo_y;

	foo_y = inb_p	(FOO_PORT_1);

	num_bytes = sizeof (long);

	memcpy_tofs	(buf, (char *) &foo_y, num_bytes);

	return (num_bytes);
}

/* ---------------------------------------------------------------------- */
static int foo_write	(struct inode *inode, struct file *file,
                      const char *buf, int count)
{
	int	num_bytes;

	int	foo_x;

	num_bytes = sizeof (int);
	memcpy_fromfs	((char *) &foo_x, buf, num_bytes);
	outb_p	(foo_x, FOO_PORT_1);

	return (num_bytes);
}

/* ---------------------------------------------------------------------- */
/* This open funtion hasn't been designed to be openned by two  */
/* processes at the same time. We check this through DEV_IN_USE.*/
static int foo_open	(struct inode *inode, struct file *file)
{
    /* Initialization check result */
	switch (foo_status)
		{
		case NOT_PRESENT:
			return -ENXIO;  /* No such device */
		case DEV_IN_USE :
			return -EBUSY;  /* Device busy    */
		case DEV_READY  :
			 break;          /* Device ready   */
		default         :
			 return -ENXIO;  /* Unknown status */
        	}


    /* Successful return point */

	foo_status = DEV_IN_USE;

	return OK;
}

/* ---------------------------------------------------------------------- */
static void foo_release	(struct inode *inode, struct file *file)
{
	foo_status = DEV_READY;
}

/* ---------------------------------------------------------------------- */
static struct file_operations foo_fops = { /* Driver available functions */
        NULL,           /* foo_lseek   */
        foo_read,       /* foo_read    */
        foo_write,      /* foo_write   */
        NULL,           /* foo_readdir */
        NULL,           /* foo_select  */
        NULL,      /* foo_ioctl   */
        NULL,           /* foo_mmap    */
        foo_open,       /* foo_open    */
        foo_release     /* foo_release */
        };

/* ---------------------------------------------------------------------- */
int foo_sample_init	(void)
{
#ifdef FOO_DEBUG
    int len;
    char buf[4096];
#endif

printk (KERN_INFO "FOO_SAMPLE CHECK AAA *** Apr/06/98 *** P.M. 15:52 ***\n");

    /* Ask for a free major device number */
	if (register_chrdev (FOO_SAMPLE_MAJOR, FOO_NAME, &foo_fops))
		{
		printk("unable to get major %d for FOO\n", FOO_SAMPLE_MAJOR);
		return -EIO;
		}

printk(KERN_INFO "FOO_SAMPLE CHECK BBB.\n");

    /* Test if IO address is free */
	if (check_region (FOO_IO_BASE_ADDRESS, FOO_NUM_PORTS))
		{
		printk("FOO error: IO address already in use\n");
		foo_status = NOT_PRESENT;
		unregister_chrdev(FOO_SAMPLE_MAJOR, FOO_NAME);
		return -EIO;
		}

printk	(KERN_INFO "FOO_SAMPLE CHECK CCC.\n");
    /* Register IO address */
	request_region	(FOO_IO_BASE_ADDRESS, FOO_NUM_PORTS, "foo");

#ifdef FOO_DEBUG
    /* Get IO port list */
    len = get_ioport_list (buf);
    buf[len] = '\0';
    printk("FOO port list:\n%s\n", buf);
#endif

printk	(KERN_INFO "FOO_SAMPLE CHECK DDD. PM 15:45\n");


    /* Successful return point */
	printk ("FOO_SAMPLE: driver initialized with major %d\n", FOO_SAMPLE_MAJOR);
	foo_status = DEV_READY;

	return OK;
}

/* ---------------------------------------------------------------------- */
