/********************************************************************
 * FILE: 	PCIO	
 * Author: 	Kenny Huang
 ********************************************************************/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/system.h>
#include <linux/delay.h>
#include <linux/pci.h> 
#include <linux/string.h>

int minor = 0;
int major = 124;

struct pci_dev *dev = NULL;
unsigned int vendor = 0x494F; 
unsigned int device = 0x0C70;
unsigned int baseaddress = 0;
static char str_baseaddress[32];

/************************ PCIO_open *************************************/

static int PCIO_open(struct inode *inode, struct file *filp)
{
	printk("opening PCIO device driver...\n");


	if (filp->f_mode & FMODE_READ) {
  		//printk("io port, ready for reading...");
	}

	/* increment usage count */
	MOD_INC_USE_COUNT;


	major = MAJOR(inode->i_rdev);
	minor = MINOR(inode->i_rdev);
	printk("major: %d   minor: %d\n", major, minor);

	return 0; 
}



static int PCIO_release(struct inode *inode, struct file *filp)
{
	MOD_DEC_USE_COUNT;
	printk("PCIO device driver closed\n");
	return 0;

}


/************************ PCIO_read *************************************/


static ssize_t PCIO_read(struct file *filp, char *buf,
			   size_t channel, loff_t *offp)
{	
	int i;
/*
	if (!pci_present())
		return -ENODEV;

	dev = pci_find_device(vendor, device, dev);
	if (!dev) {
		printk("dev is NULL\n");
		return -1;
	}
	else {
		//printk("vendor: %X\n", dev->vendor);
		//printk("device: %X\n", dev->device);
		printk("BASEADDRESS[2]: %X\n", dev->base_address[2] - 1);
		baseaddress = dev->base_address[2] - 1;
		printk("baseaddress: %X\n", baseaddress);
	}
*/
/*	
	str_baseaddress[0] = (0xFF000000 & baseaddress) >> 24;
	str_baseaddress[1] = (0x00FF0000 & baseaddress) >> 16;
	str_baseaddress[2] = (0x0000FF00 & baseaddress) >> 8;
	str_baseaddress[3] = 0x000000FF & baseaddress;
	str_baseaddress[4] = 0;

	put_user(str_baseaddress[0], buf++);
	put_user(str_baseaddress[1], buf++);
	put_user(str_baseaddress[2], buf++);
	put_user(str_baseaddress[3], buf++);
	put_user(str_baseaddress[4], buf++);
*/

	return (ssize_t) baseaddress;

}



/************************* file operations ********************************/

struct file_operations PCIO_fops = {
	NULL,		
	PCIO_read, 	//IO16_read,		
	NULL,		// don't need write for our case	
	NULL,			
	NULL,	
	NULL,
	NULL,
	PCIO_open,
	NULL,
	PCIO_release
};


/**************************** init_module *************************************/

int init_module(void)
{
  	int err;
	/* 
         * register the device with the kernel 
	 * NOTE: 60-63, 240-254 are experimental range 
	 * 	 our device is registered with 125 and 0
	 */
	if ((err = register_chrdev(major, "PCIO", &PCIO_fops)) < 0 )
        	printk("unable to register device\n");
	//printk("registered device: %d successfully\n", err);

	if (!pci_present())
		return -ENODEV;

	dev = pci_find_device(vendor, device, dev);
	if (!dev) {
		printk("dev is NULL\n");
		return -1;
	}
	else {
		//printk("vendor: %X\n", dev->vendor);
		//printk("device: %X\n", dev->device);
		printk("BASEADDRESS[2]: %lX\n", dev->base_address[2] - 1);
		baseaddress = dev->base_address[2] - 1;
		printk("baseaddress: %X\n", baseaddress);
	}
	
	return 0;
}


/**************************** ges_cleanup *************************************/

void cleanup_module(void)
{
	unregister_chrdev(major, "PCIO");
	MOD_DEC_USE_COUNT;
   	printk("goodbye\n");
}








