/** @file analog_out.c

Contains analog output ioctl functions.

@par CREATION
@author Krauth Julien
@date   05.03.07

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

@par LICENCE
@verbatim
Copyright (C) 2009  ADDI-DATA GmbH for the source code of this module.

ADDI-DATA GmbH
Airpark Business Center
Airport Boulevard B210
77836 Rheinmuenster
Germany
Tel: +49(0)7229/1847-0
Fax: +49(0)7229/1847-200
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 "apci3120-private.h"

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

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

/** Write 1 analog output channel.
*
* This function call all kapi functions to write 1 analog output channel.
* It can be possible to use separate functions to do that once they are
* implemented.
*
* @param [in] pdev								: The device to initialize.
* @param [in] arg[0] (b_Channel)				: Number of the analog output to be set.
* @param [in] arg[1] (b_Polarity)				: Selection of the output voltage range of the analog output to convert.
* @param [in] arg[2] (ui_ValueToWrite)			: Analog value to write on the output.
*  *
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: Number of the analog output is wrong.
* @retval 3: The selected output voltage range is wrong.
* @retval 4: Wrong output value for the output channel.
* @retval 5: Time out.
* @retval -EFAULT : Fail to retrieve user data.
*/
int do_CMD_APCI3120_Write1AnalogOutput (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	uint32_t ui_ArgArray[5];

	if ( copy_from_user( ui_ArgArray, (uint32_t __user *)arg, sizeof(ui_ArgArray) ) )
		return -EFAULT;

	/* Initialize the selected channel */
	{
		unsigned long irqstate;
		APCI3120_LOCK(pdev,&irqstate);
		{
			ret = i_APCI3120_InitAnalogOutput (pdev,
					(uint8_t) ui_ArgArray[0],
					(uint8_t) ui_ArgArray[1]);
		}
		APCI3120_UNLOCK(pdev,irqstate);
	}

	if(ret)
		return ret;

	/* Wait that output is ready to write a new value */
	{
		/* Set a timeout of 2 seconds */
		unsigned long volatile timeOut = jiffies + (HZ * 2);

		do {
			ret = i_APCI3120_ReadAnalogOutputBit (pdev);

			/* If time out management */
			if (jiffies > timeOut) return 5;

		} while (ret == 2);
	}

	/* Write the analog value */
	{
		unsigned long irqstate;
		APCI3120_LOCK(pdev,&irqstate);
		{
			ret = i_APCI3120_Write1AnalogOutput (pdev,
					(uint8_t) ui_ArgArray[0],
					(uint32_t) ui_ArgArray[2]);
		}
		APCI3120_UNLOCK(pdev,irqstate);
	}
	if(ret)
	{
		if (ret == 2)
			ret++;

		return ret;
	}

	return 0;
}

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

/** Write more analog output channel.
*
* This function call all kapi functions to write 1 analog output channel.
* It can be possible to use separate functions to do that once they are
* implemented.
*
* @param [in] pdev					: The device to initialize.
* @param [in] arg[0] (b_FisrtChannel)			: The first channel number up which is to be written.
* @param [in] arg[1] (b_NumberOfChannel)		: The number of channels to write.
* @param [in] arg[2] (b_PolarityArray)			: Selection of the output voltage range.
* @param [in] arg[..] (ui_WriteValueArray)		: Analog values to write on the output channles.
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: Number of the analog output is wrong.
* @retval 3: The selected output voltage range is wrong.
* @retval 4: Wrong output value for the output channel.
* @retval 5: Time out.
* @retval -EFAULT : Fail to retrieve user data.
*/
int do_CMD_APCI3120_WriteMoreAnalogOutput (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	uint32_t ui_ArgArray[8 + 8 + 1 + 1]; /* b_FisrtChannel + b_NumberOfChannel + b_PolarityArray + ui_WriteValueArray */

	if ( copy_from_user( ui_ArgArray, (uint32_t __user *)arg, sizeof(ui_ArgArray) ) )
		return -EFAULT;

	/* Initialize all channels */
	{
		unsigned long irqstate;
		APCI3120_LOCK(pdev,&irqstate);
		{
			int i;
			for (i=0; i<ui_ArgArray[1]; i++)
			{
				if ((ret = i_APCI3120_InitAnalogOutput (pdev,
						(uint8_t) (ui_ArgArray[0] + i),
						(uint8_t) ui_ArgArray[(2 + i)])) != 0)
				{
					break;
				}

			}
		}
		APCI3120_UNLOCK(pdev,irqstate);
	}
	if(ret)
		return ret;

	{
		unsigned long irqstate;
		APCI3120_LOCK(pdev,&irqstate);
		{
			ret = i_APCI3120_WriteMoreAnalogOutput (pdev, &ui_ArgArray[(2 + ui_ArgArray[1])]);
		}
		APCI3120_UNLOCK(pdev,irqstate);
	}
	if (ret)
	{
		if (ret > 1)
			ret += 2;

		return ret;
	}

	return 0;
}

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


