/** @file analog_in.c

Contains analog input ioctl functions.

@par CREATION
@author Krauth Julien
@date   14.08.06

@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


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

/** Read 1 analog input channel.
*
* This function call all kapi functions to read 1 analog input 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 input to be read.
* @param [in] arg[1] (b_Gain)						: Gain selection.
* @param [in] arg[2] (b_Polarity)					: Selection of the input voltage range of the analog input to convert.
* @param [in] arg[3] (ui_ConvertinTiming)		: Selection of the conversion time from 10�s to 32767 us.
* @param [in] arg[4] (b_InterruptFlag)			: This parameter has to be 0 or APCI3001_DISABLE.<br>
* 																The interrupt is not implemeted!
* @param [out] arg[0] (ui_AnalogInputValue)	: The analog value.
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: Number of the analog input is wrong.
* @retval 3: The selected gain is wrong.
* @retval 4: The selected input voltage range is wrong.
* @retval 5: The selected conversion time is wrong.
* @retval 6: A wrong parameter has been passed for <br>
*				b_InterruptFlag or the user interrupt routine has <br>
*				not been installed.
* @retval 7: Time out.
* @retval -EFAULT : Fail to retrieve user data.
*/
int do_CMD_APCI3120_Read1AnalogInput (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	uint32_t ui_ArgArray[5];
	uint32_t ui_Tmp = 0;

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

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

	if(ret)
		return ret;

	/* Start the conversion */
	/* no need to lock */
	if ((ret = i_APCI3120_StartAnalogInput (pdev)) != 0)
	{
		return ret;
	}

	/* Wait the end of the conversion */
	{
		/* Set a timeout of 2 seconds */
		/* no need to lock */
		unsigned long volatile timeOut = jiffies + (HZ * 2);

		do {
			ret = i_APCI3120_ReadAnalogInputBit (pdev);

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

		} while (ret == 2);
	}

	/* Read the analog value */
	/* no need to lock */
	if ((ret = i_APCI3120_ReadAnalogInputValue (pdev, &ui_Tmp)) != 0)
	{
		return ret;
	}

	/* Return the value */
	if ( copy_to_user( (uint32_t __user *)arg , &ui_Tmp, sizeof(ui_Tmp) ) )
	{
		return -EFAULT;
	}

	return 0;
}

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

/** Read more analog input channel.
*
* This function call all kapi functions to read more analog input channel.
*
* @param [in] pdev										: The device to initialize.
* @param [in]	arg[0] (b_SequenzArraySize)		: Size of the sequence array (1 to 16 elements).
* @param [in]	arg[1] (pb_ChannelArray)			: Channels to read (array size = b_SequenzArraySize).
* @param [in]	arg[...] (pb_GainArray)				: Gain selection array.
* @param [in]	arg[...] (pb_PolarityArray)		: Selection of the input.
* @param [in]	arg[...] (ui_ConvertTiming)		: Selection of the conversion time from 10 to 32767 us.
* @param [in]	arg[...] (b_InterruptFlag)			: This parameter has to be 0 or APCI3001_DISABLE.<br>
* 																The interrupt is not implemeted!
*
* @param [out]	arg[0...] (pui_AnalogInputValueArray)	: Read values.
*
* @retval 0: No error.
* @retval 1: The handle parameter of the board is wrong.
* @retval 2: The size of the sequence table is wrong.
* @retval 3: Wrong parameter found in table "pb_ChannelArray".
* @retval 4: Wrong parameter found in table "pb_GainArray".
* @retval 5: Wrong parameter found in table "pb_PolarityArray".
* @retval 6: The selected conversion time is wrong.
* @retval 7: A wrong parameter has been passed for b_InterruptFlag.
* @retval 8: Time out.
* @retval -EFAULT : Fail to retrieve user data.
*/
int do_CMD_APCI3120_ReadMoreAnalogInputsEx (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	uint32_t ui_ArgArray[(16 * 3) + 1 + 1]; /* b_ChannelArray[16] + b_GainArray[16] + b_PolarityArray[16] + b_SequenzArraySize + ui_ConvertTiming */
	uint8_t b_ChannelArray[16] = {0};
	uint8_t b_GainArray[16] = {0};
	uint8_t b_PolarityArray[16] = {0};
	uint32_t ui_ConvertTiming = 0;
	uint32_t ui_AnalogInputValueArray[16] = {0};

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

	{
		int i;
		for (i=0; i<ui_ArgArray[0]; i++)
		{
			b_ChannelArray[i] = ui_ArgArray[1 + i];
			b_GainArray[i]    = ui_ArgArray[1 + ui_ArgArray[0] + i];
			b_PolarityArray[i]   = ui_ArgArray[1 + ui_ArgArray[0] + ui_ArgArray[0] + i];
		}
	}
	ui_ConvertTiming = ui_ArgArray[1 + ui_ArgArray[0] + ui_ArgArray[0] + ui_ArgArray[0]];

	/* no need to lock */
	if ((ret = i_APCI3120_ReadMoreAnalogInputs (pdev,
			(uint8_t) ui_ArgArray[0], // b_SequenzArraySize
			b_ChannelArray,
			b_GainArray,
			b_PolarityArray,
			ui_ConvertTiming,
			0,
			ui_AnalogInputValueArray)) != 0)
	{
		return ret;
	}

	/* Return the value */
	if ( copy_to_user( (uint32_t __user *)arg , &ui_AnalogInputValueArray, (sizeof(uint32_t) * ui_ArgArray[0]) ) )
	{
		return -EFAULT;
	}

	return 0;
}

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

int do_CMD_APCI3120_ReadMoreAnalogInputs (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	uint32_t ui_ArgArray[(16 * 3) + 1 + 1]; /* b_ChannelArray[16] + b_GainArray[16] + b_PolarityArray[16] + b_SequenzArraySize + ui_ConvertTiming */
	uint8_t b_ChannelArray[16] = {0};
	uint8_t b_GainArray[16] = {0};
	uint8_t b_PolarityArray[16] = {0};
	uint32_t ui_ConvertTiming = 0;
	uint32_t ui_AnalogInputValueArray[16] = {0};

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

	{
		int i;
		for (i=0; i<ui_ArgArray[0]; i++)
		{
			b_ChannelArray[i]    = ui_ArgArray[1 + i];
			b_GainArray[i]       = ui_ArgArray[1 + ui_ArgArray[0] + i];
			b_PolarityArray[i]   = ui_ArgArray[1 + ui_ArgArray[0] + ui_ArgArray[0] + i];
		}
	}
	ui_ConvertTiming = ui_ArgArray[1 + ui_ArgArray[0] + ui_ArgArray[0] + ui_ArgArray[0]];

	/* no need to lock */
	if ((ret = i_APCI3120_InitReadMoreAnalogInput (pdev, (uint8_t) ui_ArgArray[0], b_ChannelArray, b_GainArray, b_PolarityArray, ui_ConvertTiming, 0)) != 0)
	{
		return ret;
	}

	/* no need to lock */
	if ((ret = i_APCI3120_StartReadMoreAnalogInput (pdev, (uint8_t) ui_ArgArray[0])) != 0)
	{
		return ret;
	}

	/* Wait the end of the conversion */
	{
		/* Set a timeout of 2 seconds per channel */
		unsigned long volatile timeOut = jiffies + (HZ * 2 * ui_ArgArray[0]);

		do {
			/* no need to lock */
			ret = i_APCI3120_TestEndOfReadMoreConversion (pdev);

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

		}	while (ret == 2);
	}
	/* no need to lock */
	if ((ret = i_APCI3120_ReadMoreValues (pdev, (uint8_t) ui_ArgArray[0], ui_AnalogInputValueArray)) != 0)
	{
		return ret;
	}

	/* Retunr the value */
	if ( copy_to_user( (uint32_t __user *)arg , &ui_AnalogInputValueArray, (sizeof(uint32_t) * ui_ArgArray[0]) ) )
	{
		return -EFAULT;
	}

	return 0;

}
//------------------------------------------------------------------------------

int do_CMD_APCI3120_InitReadMoreAnalogInput (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	uint32_t ui_ArgArray[(16 * 3) + 1 + 1]; /* b_ChannelArray[16] + b_GainArray[16] + b_PolarityArray[16] + b_SequenzArraySize + ui_ConvertTiming */
	uint8_t b_ChannelArray[16] = {0};
	uint8_t b_GainArray[16] = {0};
	uint8_t b_PolarityArray[16] = {0};
	uint32_t ui_ConvertTiming = 0;

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

	{
		int i;
		for (i=0; i<ui_ArgArray[0]; i++)
		{
			b_ChannelArray[i]		= ui_ArgArray[1 + i];
			b_GainArray[i]			= ui_ArgArray[1 + ui_ArgArray[0] + i];
			b_PolarityArray[i]	= ui_ArgArray[1 + ui_ArgArray[0] + ui_ArgArray[0] + i];
		}
	}
	ui_ConvertTiming = ui_ArgArray[1 + ui_ArgArray[0] + ui_ArgArray[0] + ui_ArgArray[0]];

	/* no need to lock */
	if ((ret = i_APCI3120_InitReadMoreAnalogInput (pdev, (uint8_t) ui_ArgArray[0], b_ChannelArray, b_GainArray, b_PolarityArray, ui_ConvertTiming, 0)) != 0)
	{
		return ret;
	}

	return 0;
}

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

int do_CMD_APCI3120_StartReadMoreAnalogInput (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	uint32_t ui_Arg;

	if ( copy_from_user( &ui_Arg, (uint32_t __user *)arg, sizeof(ui_Arg) ) )
	{
		return -EFAULT;
	}
	/* no need to lock */
	if ((ret = i_APCI3120_StartReadMoreAnalogInput (pdev, (uint8_t) ui_Arg)) != 0)
	{
		return ret;
	}

	return 0;
}

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

int do_CMD_APCI3120_TestEndOfReadMoreConversion (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	/* no need to lock */
	return (i_APCI3120_TestEndOfReadMoreConversion (pdev));
}

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

int do_CMD_APCI3120_TestReadMoreAnalogInput (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	unsigned long volatile timeOut = 0;
	unsigned int ui_Arg = 0;
	int ret = 0;

	if ( copy_from_user( &ui_Arg, (uint32_t __user *)arg, sizeof(ui_Arg) ) )
	{
		return -EFAULT;
	}

	/* Set a timeout of 2 seconds per channel */
	timeOut = jiffies + (HZ * 2 * ui_Arg);

	do {
		/* no need to lock */
		ret = i_APCI3120_TestEndOfReadMoreConversion (pdev);

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

	} while (ret == 2);

	if (ret == 1)
		return 1;

	return 0;
}

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

int do_CMD_APCI3120_ReadMoreValues (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	uint32_t ui_ArgArray[16 + 1]; /* b_SequenzArraySize + ui_AnalogInputValueArray */
	uint32_t ui_AnalogInputValueArray[16] = {0};

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

	/* no need to lock */
	if ((ret = i_APCI3120_ReadMoreValues (pdev, (uint8_t) ui_ArgArray[0], ui_AnalogInputValueArray)) != 0)
	{
		return ret;
	}

	/* Return the value */
	if ( copy_to_user( (uint32_t __user *)arg , &ui_AnalogInputValueArray, (sizeof(uint32_t) * ui_ArgArray[0]) ) )
	{
		return -EFAULT;
	}

	return 0;
}

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

// Parameter: struct apci3120_sequence_acquisition_data sequence_data: enables to give all the required information about the sequence
// See the description in the include file : apci3120.h

int do_CMD_APCI3120_InitAnalogInputSequenceAcquisition (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	struct apci3120_sequence_acquisition_data sequence_data;

	// Get the data given in parameter
	if ( copy_from_user( &sequence_data, (struct apci3120_sequence_acquisition_data __user *)arg, sizeof(sequence_data) ) )
	{
		return -EFAULT;
	}

	// When we have an interruption, we send packet that contains the value of the channel
	// To always have a correspondance between these packets, the module between the value must be equal to 0
	if ((sequence_data.number_of_acquisition % sequence_data.sequence_array_size) != 0)
		return 18;

	return i_APCI3120_InitAnalogInputSequenceAcquisition(pdev, &sequence_data);
}

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

int do_CMD_APCI3120_StartAnalogInputSequenceAcquisition (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	if ( (ret = i_APCI3120_StartAnalogInputSequenceAcquisition(pdev)) != 0 )
	{
		return ret;
	}
	return 0;
}

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

int do_CMD_APCI3120_StopSequenceAcquisition (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	if ( (ret = i_APCI3120_StopSequenceAcquisition(pdev)) != 0 )
	{
		return ret;
	}
	return 0;
}




