/** @par LICENCE
* @verbatim
 Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code and the documentation.
        
        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 shoud find the complete GPL in the COPYING file accompanying 
    this source code. 
* @endverbatim
*/

#include "apci2032-private.h"



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

/**
 * @note In this module, IO are indiced from 0
 */

/** flush internal output register representation to board */
static __inline__ void FLUSH_OUTPUT(struct pci_dev * pdev)
{
	outl ( APCI2032_PRIVDATA(pdev)->DigitalOutputState,  ( GET_BAR1(pdev)) + APCI2032_DIGITAL_OUTPUT_REG);
}

/** read actual output status from board*/
static __inline__ uint32_t GET_OUTPUT_STATUS(struct pci_dev * pdev)
{
	return inl ( GET_BAR1(pdev) + APCI2032_DIGITAL_OUTPUT_REG);
}

//-------------------------------------------------------------------	     
int do_CMD_APCI2032_Set1DigitalOutputOn(struct pci_dev * pdev, uint32_t arg )
{
	unsigned long irqstate;
	APCI2032_LOCK(pdev,&irqstate);
	{
		if( APCI2032_OUTP_MEM_ON(pdev) )
		{
			APCI2032_PRIVDATA(pdev)->DigitalOutputState |=   (1 << arg);
		}
		else
		{
		   	APCI2032_PRIVDATA(pdev)->DigitalOutputState = 1 << arg;
		}
		FLUSH_OUTPUT(pdev); 
	}
	APCI2032_UNLOCK(pdev,irqstate);

	return 0;
}
//-------------------------------------------------------------------	     
int do_CMD_APCI2032_Set1DigitalOutputOff(struct pci_dev * pdev, uint32_t arg )
{
	unsigned long irqstate;
	APCI2032_LOCK(pdev, &irqstate);
	{
		if ( !APCI2032_OUTP_MEM_ON(pdev) )
		{
			APCI2032_UNLOCK(pdev, irqstate);
			return -EINVAL;
		}

		APCI2032_PRIVDATA(pdev)->DigitalOutputState &= (0xFFFFFFFF - (1 << arg));	
	
		FLUSH_OUTPUT(pdev);
	}
	APCI2032_UNLOCK(pdev, irqstate);	
	return 0;
}

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

//-------------------------------------------------------------------	     
int do_CMD_APCI2032_Set32DigitalOutputsOn(struct pci_dev * pdev, uint32_t arg )
{
	unsigned long irqstate;
	APCI2032_LOCK(pdev, &irqstate);
	{
		if( APCI2032_OUTP_MEM_ON(pdev) )
		{
			APCI2032_PRIVDATA(pdev)->DigitalOutputState |= arg  ;
		}
		else
		{
		   	APCI2032_PRIVDATA(pdev)->DigitalOutputState = arg;
		}
		FLUSH_OUTPUT(pdev); 
	}
	APCI2032_UNLOCK(pdev, irqstate);

	return 0;
}
//-------------------------------------------------------------------	     
int do_CMD_APCI2032_Set32DigitalOutputsOff(struct pci_dev * pdev, uint32_t arg )
{
	unsigned long irqstate;

	APCI2032_LOCK(pdev,&irqstate);
	{
		if ( !APCI2032_OUTP_MEM_ON(pdev) )
		{
			APCI2032_UNLOCK(pdev, irqstate);
			return -EINVAL;
		}
		APCI2032_PRIVDATA(pdev)->DigitalOutputState &= (0xFFFFFFFF - arg );	
	
		FLUSH_OUTPUT(pdev);
	}
	APCI2032_UNLOCK(pdev, irqstate);	
	return 0;
}

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

int do_CMD_APCI2032_Get32DigitalOutputStatus(struct pci_dev * pdev, uint32_t __user  * arg )
{
	volatile uint16_t outpstatus = 0;
	unsigned long irqstate;
	APCI2032_LOCK(pdev,&irqstate);
	{	
		 outpstatus = GET_OUTPUT_STATUS(pdev);
	}
	APCI2032_UNLOCK(pdev, irqstate);
	
	if ( copy_to_user(arg,(uint16_t *) &outpstatus, sizeof(uint16_t) ) )
		return -EFAULT;
	
	return 0;

}
