#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/config.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>

#include "envUtils.h"
#include "fanIOCtls.h"

#define FAN_MAJOR 120
#define FAN_NAME "Fan card"


unsigned long BASEPORT = 0x320;
static const unsigned long PORTLENGTH = 0x09;

MODULE_PARM(BASEPORT, "l");
MODULE_PARM_DESC(BASEPORT,
	"The base port address of the fan card. If unspecified 0x320 will be used");

// open function - called when the "file" /dev/fan0 is opened in userspace
static int fan_open(struct inode *inode, struct file *file) { return 0; }

// close function - called when the "file" /dev/fan0 is closed in userspace  
static int fan_release (struct inode *inode, struct file *file) { return 0; }

// ioctl - I/O control
static int fan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) {
	int retval = 0;
	
	/*************************************************************************************
	* int temps[5] format:
	* temps[0] = now, temps[1] = high, temps [2] = low, temps[3] = config, temps[4] = ch
	*************************************************************************************/
	int temps[5];
	int dblint[2];
	
	switch ( cmd ) {
		case DIAG_LED_CTL:
			retval = envDiagLED((int)arg);
			break;
		case READ_CFG_CTL:
			retval = envReadCfg();
			break;
		case READ_DIG_CTL:
			retval = envReadDig();
			break;
		case SET_DIG_CTL:
			retval = envSetDig((int)arg);
			break;
		case READ_ANA_CTL:
			retval = envReadAna((int)arg);
			break;
		case FAN_CTL:
			retval = envFan((int)arg);
			break;
		case SET_ENABLES_CTL:
			retval = envSetEnables((int)arg);
			break;
		case INIT_TEMP_CTL:
			envInitTemp((int)arg);
			retval = 0;
			break;
		case READ_TEMP_CTL:
			// Copy array from user space
			retval = copy_from_user(temps, (void *)arg, sizeof(int)*5);
			if(retval) return -EFAULT;
			
			envReadTemp(temps, temps+1, temps+2, temps+3, temps[4]); // Carry out read
			
			// Copy results of read back to user space
			retval += copy_to_user((void *)arg, temps, sizeof(int)*5);
			if(retval) return -EFAULT;

			break;
		case WRITE_TEMP_CTL:
			// Copy array from user space
			retval = copy_from_user(temps, (void *)arg, sizeof(int)*5);
			if(retval) return -EFAULT;
			
			envWriteTemp(temps[1], temps[2], temps[4]); // Carry out write
			break;
		case BUS_READ8_CTL:
			retval = BusIORead8((int)arg);
			break;
		case BUS_WRITE8_CTL:
			retval = copy_from_user(dblint, (void *)arg, sizeof(int)*2);
			if(retval) return -EFAULT;
			retval = BusIOWrite8(dblint[0], dblint[1]);
			break;
		case GET_PORTS_CTL:
			retval = BASEPORT;
			break;
		default:
			retval = -EINVAL;
	}
	return retval;
}

// define which file operations are supported
struct file_operations fan_fops = {
	owner:		THIS_MODULE,
	ioctl:		fan_ioctl,
	open:		fan_open,
	release:	fan_release,
};

// initialize module (and interrupt)
static int __init fan_init_module (void) {
	printk("initializing fan module\n");
	int fan, LED;
	// Grab major number
	if(register_chrdev (FAN_MAJOR, FAN_NAME, &fan_fops)) return -EIO;
	
	// Grab ports
	if(request_region(BASEPORT, PORTLENGTH, "Fan") == NULL) return -ENODEV;
	
	// Setup communication with the fan board
	if(envOpen(BASEPORT) != 0) return -EFAULT;
	
	// Setup default hardware configuration
	envWriteTemp(0, 0, 0); // Make sure the fan turns on and stays on.
	fan = envFan(1);
	LED = envDiagLED(1); // Have the LED on when the fan is on.
	printk("Environmental Fan Card: activating fan %i, activating LED %i\n", fan, LED);
	
	return 0;
}

// close and cleanup module
static void __exit fan_cleanup_module (void) {
	printk("cleaning up fan module\n");
	// Toggle the LED to indicate shutdown
//	if(envDiagLED(2) == 1) envDiagLED(1); else envDiagLED(0);
	release_region(BASEPORT, PORTLENGTH);
	unregister_chrdev (FAN_MAJOR, FAN_NAME);
}

module_init(fan_init_module);
module_exit(fan_cleanup_module);
MODULE_AUTHOR("Monterey Bay Aquarium Research Institute, D. Casner");
MODULE_DESCRIPTION("Linux Device Driver for Parvus Theremocool PC/104 Environmental Fan Card II");
MODULE_LICENSE("GPL");
