/** @file dig_outputs.c

   Contains all functions to set and reset digital outputs of the APCI-3200.

   @par CREATION
   @author Krauth Julien
   @date   06.06.05

   @par VERSION
   @verbatim
   $LastChangedRevision$
   $LastChangedDate$
   @endverbatim

   @par COMPILER
   @verbatim
   Compiler	: gcc
   Version	: gcc (GCC) 3.3.6 (Debian 1:3.3.6-5)
   kernel	: 2.4.26
   @endverbatim

   @par UPDATES
   @verbatim
   Date			:
   Author		:
   Description	:
   @endverbatim

   @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 "apci3200-private.h"

/**@def EXPORT_NO_SYMBOLS
 * Function in this file are not exported.
 */
EXPORT_NO_SYMBOLS;

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

/** Flush internal output register representation to board.
 *
 * @param [in]	pdev	Pointer on the device structure.
 */
static __inline__ void FLUSH_OUTPUT(struct pci_dev * pdev)
{
	outl (APCI3200_PRIVDATA(pdev)->DigitalOutputState, GET_BAR2(pdev));
}

/** Read actual output status from board.
 *
 * @param [in]	pdev	Pointer on the device structure.
 */
static __inline__ uint8_t GET_OUTPUT_STATUS(struct pci_dev * pdev)
{
	return inl (GET_BAR2(pdev));
}

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

/** Set 1 digital output channel on.
 *
 * @param [in,out]	pdev	Pointer on the device structure.
 * @param [in]		cmd		IOCTL command. The command is <br>
 * 							CMD_APCI3200_Set1DigitalOutputOn when using the IOCTL funktion.<br>
 * 							To call the function in kernel mode, cmd must be 0.
 * @param 			arg		Function argument. This is the digital output channel number (0 to 2).
 *
 * @retval 0		No error.
 * @retval -EINVAL	The output number is wrong.
 */
int do_CMD_APCI3200_Set1DigitalOutputOn(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	/* Legal values for channels are 0 to 2 included */
	switch(arg)
	{
		case 0 ... 2:
			break;
		default:
			return -EINVAL;
	}

	{
		unsigned long irqstate;
		APCI3200_LOCK(pdev,&irqstate);
		{
			if( APCI3200_OUTP_MEM_ON(pdev) )
			{
				APCI3200_PRIVDATA(pdev)->DigitalOutputState |=   (1 << arg);
			}
			else
			{
			   	APCI3200_PRIVDATA(pdev)->DigitalOutputState = (1 << arg);
			}
			FLUSH_OUTPUT(pdev);
		}
		APCI3200_UNLOCK(pdev,irqstate);
	}
	return 0;
}
//------------------------------------------------------------------------------

/** Set 1 digital output channel off.
 *
 * @param [in,out]	pdev	Pointer on the device structure.
 * @param [in]		cmd		IOCTL command. The command is <br>
 * 							CMD_APCI3200_Set1DigitalOutputOff when using the IOCTL funktion.<br>
 * 							To call the function in kernel mode, cmd must be 0.
 * @param 			arg		Function argument. This is the digital output channel number (0 to 2).
 *
 * @remark			This function has to be called only if output memory is on!
 *
 * @retval 0		No error.
 * @retval -EINVAL	The output number is wrong.
 * @retval -EINVAL	The output memory is off.
 */
int do_CMD_APCI3200_Set1DigitalOutputOff(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{

	/* Legal values for channels are 0 to 2 included */
	switch(arg)
	{
		case 0 ... 2:
			break;
		default:
			return -EINVAL;
	}

	{
		unsigned long irqstate;
		APCI3200_LOCK(pdev,&irqstate);
		{
			if ( !APCI3200_OUTP_MEM_ON(pdev) )
			{
				APCI3200_UNLOCK(pdev,irqstate);
				return -EINVAL;
			}
			APCI3200_PRIVDATA(pdev)->DigitalOutputState &= (0x1F - (1 << arg));

			FLUSH_OUTPUT(pdev);
		}
		APCI3200_UNLOCK(pdev,irqstate);
	}
	return 0;
}

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

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

/** Set 3 digital outputs channel on.
 *
 * @param [in,out]	pdev	Pointer on the device structure.
 * @param [in]		cmd		IOCTL command. The command is <br>
 * 							CMD_APCI3200_Set3DigitalOutputsOn when using the IOCTL funktion.<br>
 * 							To call the function in kernel mode, cmd must be 0.
 * @param 			arg		Function argument. This is the digital output channel number (0 to 7).<br>
 * 							Exemple:
 * 							Output 0: arg=1
 * 							Output 1: arg=2
 * 							Output 2: arg=4
 * 							Output 1 and 2: arg=3
 *
 * @retval 0		No error.
 * @retval -EINVAL	The output number is wrong.
 */
int do_CMD_APCI3200_Set3DigitalOutputsOn(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{

	/* Legal values for channels are 0 to 7 included */
	switch(arg)
	{
		case 0 ... 7:
			break;
		default:
			return -EINVAL;
	}

	{
		unsigned long irqstate;
		APCI3200_LOCK(pdev, &irqstate);
		{
			if( APCI3200_OUTP_MEM_ON(pdev) )
			{
				APCI3200_PRIVDATA(pdev)->DigitalOutputState |= arg;
			}
			else
			{
			   	APCI3200_PRIVDATA(pdev)->DigitalOutputState = arg;
			}
			FLUSH_OUTPUT(pdev);
		}
		APCI3200_UNLOCK(pdev,irqstate);
	}
	return 0;
}
//------------------------------------------------------------------------------

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

/** Set 3 digital outputs channel off.
 *
 * @param [in,out]	pdev	Pointer on the device structure.
 * @param [in]		cmd		IOCTL command. The command is <br>
 * 							CMD_APCI3200_Set3DigitalOutputsOff when using the IOCTL funktion.<br>
 * 							To call the function in kernel mode, cmd must be 0.
 * @param 			arg		Function argument. This is the digital output channel number (0 to 7).<br>
 * 							Exemple:
 * 							Output 0: arg=1
 * 							Output 1: arg=2
 * 							Output 2: arg=4
 * 							Output 1 and 2: arg=3
 *
 * @remark			This function has to be called only if output memory is on!
 *
 * @retval 0		No error.
 * @retval -EINVAL	The output number is wrong.
 * @retval -EINVAL	The output memory is off.
 */
int do_CMD_APCI3200_Set3DigitalOutputsOff(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{

	/* Legal values for channels are 0 to 7 included */
	switch(arg)
	{
		case 0 ... 7:
			break;
		default:
			return -EINVAL;
	}
	{
		unsigned long irqstate;

		APCI3200_LOCK(pdev,&irqstate);
		{
			if ( !APCI3200_OUTP_MEM_ON(pdev) )
			{
				APCI3200_UNLOCK(pdev,irqstate);
				return -EINVAL;
			}
			APCI3200_PRIVDATA(pdev)->DigitalOutputState &= (0x1F - arg );

			FLUSH_OUTPUT(pdev);
		}
		APCI3200_UNLOCK(pdev,irqstate);
	}
	return 0;
}
//------------------------------------------------------------------------------

/** Set the digital output memory on.
 *
 * When output memory is setting on. Output state is holding, and <br>
 * the new output(s) is(are) set or reset.
 *
 * @param [in,out]	pdev	Pointer on the device structure.
 * @param [in]		cmd		IOCTL command. The command is <br>
 * 							CMD_APCI3200_SetDigitalOutputMemoryOn when using the IOCTL funktion.<br>
 * 							To call the function in kernel mode, cmd must be 0.
 * @param 			arg		Function argument. Not use by thos function, can be set to 0.
 *
 * @retval 0		No error.
 */
int do_CMD_APCI3200_SetDigitalOutputMemoryOn(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	unsigned long irqstate;

	APCI3200_LOCK(pdev,&irqstate);
	{
		APCI3200_PRIVDATA(pdev)->OutputMemoryStatus = 1;
	}
	APCI3200_UNLOCK(pdev,irqstate);

	return 0;
}
//------------------------------------------------------------------------------

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

/** Set the digital output memory off.
 *
 * @param [in,out]	pdev	Pointer on the device structure.
 * @param [in]		cmd		IOCTL command. The command is <br>
 * 							CMD_APCI3200_SetDigitalOutputMemoryOff when using the IOCTL funktion.<br>
 * 							To call the function in kernel mode, cmd must be 0.
 * @param 			arg		Function argument. Not use by thos function, can be set to 0.
 *
 * @retval 0		No error.
 */
int do_CMD_APCI3200_SetDigitalOutputMemoryOff(struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	unsigned long irqstate;

	APCI3200_LOCK(pdev,&irqstate);
	{
		APCI3200_PRIVDATA(pdev)->OutputMemoryStatus = 0;
	}
	APCI3200_UNLOCK(pdev,irqstate);
	return 0;
}
//------------------------------------------------------------------------------
