/** @par LICENCE
* @verbatim
Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
        
        ADDI-DATA GmbH 
        Dieselstrasse 3 
        D-77833 Ottersweier 
        Tel: +19(0)7223/9493-0 
        Fax: +49(0)7223/9493-92 
        http://www.addi-data-com 
        info@addi-data.com 
        
This program is free software; you can redistribute it and/or modify it under 
the terms of the GNU General Public License as published by the Free Software 
Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with 
this program; if not, write to the Free Software Foundation, 
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

You shoud also find the complete GPL in the COPYING file 
accompanying this source code.

* @endverbatim
*/

#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
	#include <linux/config.h>
#else
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
		#include <linux/autoconf.h>
	#else
		#include <generated/autoconf.h>
	#endif
#endif

#include <apcie1564-kapi.h>
#include "apcie1564-private.h"

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27)
	#define __user 
#endif

EXPORT_NO_SYMBOLS;

//------------------------------------------------------------------------------------------------------

int APCIE1564_Read32DigitalInput(struct pci_dev *pdev, uint32_t* input_value)
{
	*input_value = inl(GET_BAR3(pdev)+APCIE1564_DIGITAL_INPUT_REG);
	return 0;
}

//------------------------------------------------------------------------------------------------------

int do_CMD_APCIE1564_Read32DigitalInput(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	uint32_t input_value = 0;
	if ( APCIE1564_Read32DigitalInput(pdev, &input_value) )
	{
		return -EIO;
	}
	
	/* returns value to the user */
	if ( copy_to_user( (uint32_t __user *) arg, (uint32_t*) &input_value, sizeof(input_value) ) )
		return -EFAULT;
	
	return 0;
}

//------------------------------------------------------------------------------------------------------

int APCIE1564_InitAndStartDigitalInputInterrupt (struct pci_dev *pdev, int InterruptLogic, uint32_t InterruptMaskMode1,uint32_t InterruptMaskMode2)
{
	/* check parameters */
	
	/* InterruptLogic must be either 0 or 1 */
	switch(InterruptLogic)
	{
		case 0:
		case 1:
			break;
		default:
			return -EINVAL; 
	}
	
	/* only bits D4 to D19 are legal for mode 1 */
	if ( InterruptMaskMode1 & (~0x000FFFF0) )
	{
		return -EINVAL;
	}
	
	/* only bits D4 to D19 are legal for mode 2 */
	if ( InterruptMaskMode2 & (~0x000FFFF0) )
	{
		return -EINVAL;
	}
	
	/* initialise all registers */
	outl ( 0x0 , GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_MODUS1_REG);
	outl ( 0x0 , GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_MODUS2_REG);
	outl ( 0x0 , GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_LOGIC_REG);
	
	/* write mode 1 */
	outl ( (InterruptMaskMode1) , GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_MODUS1_REG);
	
	/* write mode 2 */
	outl ( (InterruptMaskMode2) , GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_MODUS2_REG);
	
	/* write compare logic */
	
	switch(InterruptLogic)
	{
		case 0: /* OR */
			outl ( 0x4 , GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_LOGIC_REG);
			break;
		case 1: /* AND */
			outl ( 0x6 , GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_LOGIC_REG);
			break;
		default: /* should not happen, right ? */
			break;
	}
	/* from now on, interrupt is enabled */
	
	return 0;
}

//------------------------------------------------------------------------------------------------------

int do_CMD_APCIE1564_InitAndStartDigitalInputInterrupt (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	struct
	{
		int InterruptLogic;
		uint32_t InterruptMaskMode1;
		uint32_t InterruptMaskMode2;
	} params;
	
	if ( copy_from_user(&params, (void __user *) arg, sizeof(params) ) )
		return -EFAULT;
	
	return APCIE1564_InitAndStartDigitalInputInterrupt (pdev, params.InterruptLogic, params.InterruptMaskMode1,params.InterruptMaskMode2);
}

//------------------------------------------------------------------------------------------------------

int APCIE1564_ClearAndStopDigitalInputInterrupt (struct pci_dev *pdev)
{
	/* re-initialise all registers */
	outl ( 0x0 , GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_MODUS1_REG);
	outl ( 0x0 , GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_MODUS2_REG);
	outl ( 0x0 , GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_LOGIC_REG);
	
	/* from now on, interrupt is disabled */
	return 0;
}

//------------------------------------------------------------------------------------------------------

int do_CMD_APCIE1564_ClearAndStopDigitalInputInterrupt (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	return APCIE1564_ClearAndStopDigitalInputInterrupt (pdev);
}

//------------------------------------------------------------------------------------------------------

int APCIE1564_GetDigitalInputInterruptConfig (struct pci_dev *pdev, int * Enabled, int * InterruptLogic, uint32_t * InterruptMaskMode1, uint32_t * InterruptMaskMode2)
{
	*Enabled = (inl ( GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_LOGIC_REG) >> 2) & 0x1;
	*InterruptLogic = (inl ( GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_LOGIC_REG) >> 1) & 0x1;
	*InterruptMaskMode1 = inl ( GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_MODUS1_REG);
	*InterruptMaskMode2 = inl ( GET_BAR3(pdev) + APCIE1564_DIGITAL_INPUT_INTERRUPT_MODUS2_REG);
	return 0;
}

//------------------------------------------------------------------------------------------------------

int do_CMD_APCIE1564_GetDigitalInputInterruptConfig (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	struct
	{
		int Enabled;
		int InterruptLogic;
		uint32_t InterruptMaskMode1;
		uint32_t InterruptMaskMode2;
	} params;
	
	{
		int ret = APCIE1564_GetDigitalInputInterruptConfig (pdev, &(params.Enabled), &(params.InterruptLogic), &(params.InterruptMaskMode1), &(params.InterruptMaskMode2) );
		if (ret)
			return ret;
	}
	
	if ( copy_to_user( (void __user *) arg, &params , sizeof(params) ) )
	{
		return -EFAULT;
	}
	return 0;
}
