/** @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) 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 "apci3001-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�s.
 * @param [in] arg[4] (b_InterruptFlag)			: This parameter has to be 0 or PCI3001_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.       
 * @retval -ETIMEDOUT : Timeout
 */				
int do_CMD_APCI3001_Read1AnalogInput (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{											
	UINT ui_ArgArray[5];
	UINT ui_Tmp = 0;
	int ret;

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

	/* Initialize the selected channel */
	{
		
		unsigned long irqstate;
		APCI3001_LOCK(pdev,&irqstate);
		{
			/* Initialize the selected channel */
			ret = i_APCI3001_InitAnalogInput (	pdev, 
								(BYTE) ui_ArgArray[0],
								(BYTE) ui_ArgArray[1], 
								(BYTE) ui_ArgArray[2],
								(UINT) ui_ArgArray[3],
								(BYTE) ui_ArgArray[4]);
		}
		APCI3001_UNLOCK(pdev,irqstate);
	}

	if(ret)
		return ret;
 
	/* Start the conversion */
	/* no need to lock i_APCI3001_StartAnalogInput() */	
	if ((ret = i_APCI3001_StartAnalogInput (pdev)) != 0) 
	{
		return ret;
	}
	/* Wait the end of the conversion */	
	{
		/* Set a timeout of 2 seconds */
		unsigned long volatile timeOut = jiffies + (HZ * 2);
		
		do {								
			ret = i_APCI3001_ReadAnalogInputBit (pdev);

			/* If time out management */
			if (jiffies > timeOut) 
				return -ETIMEDOUT;
	
		} while (ret == 2);
	}
			
	/* Read the analog value */
	/* no need to lock i_APCI3001_ReadAnalogInputBit() */
	{
		if ((ret = i_APCI3001_ReadAnalogInputValue (pdev, &ui_Tmp)) )
		{
			return ret;
		}
	}	
	/* Return the value */	
	if ( copy_to_user( (UINT __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 PCI3001_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.
 * @retval -ETIMEDOUT : Timeout.
 */				
int do_CMD_APCI3001_ReadMoreAnalogInputsEx (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;                               
	INT i = 0;
	UINT ui_ArgArray[(16 * 3) + 1 + 1]; /* b_ChannelArray[16] + b_GainArray[16] + b_PolarityArray[16] + b_SequenzArraySize + ui_ConvertTiming */
	BYTE b_ChannelArray[16] = {0};
	BYTE b_GainArray[16] = {0};
	BYTE b_PolarityArray[16] = {0};
	UINT ui_ConvertTiming = 0;
	UINT ui_AnalogInputValueArray[16] = {0};

	if ( copy_from_user( ui_ArgArray, (UINT __user *)arg, sizeof(ui_ArgArray) ) )
	{
		return -EFAULT;
	}
	
	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 i_APCI3001_ReadMoreAnalogInputs() (furthermore it uses jiffies) */
	ret = i_APCI3001_ReadMoreAnalogInputs (pdev, 
						(BYTE) ui_ArgArray[0], // b_SequenzArraySize
	                    b_ChannelArray,
	                    b_GainArray,
	                    b_PolarityArray,
	                    ui_ConvertTiming,
	                    0,
	                    ui_AnalogInputValueArray);
	switch(ret)
	{
		case 0: /* No error */
			break;
		case 8:
			return -ETIMEDOUT;
		default:
  			return ret; 
	}

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

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

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

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

    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 i_APCI3001_InitReadMoreAnalogInput() */
    ret = i_APCI3001_InitReadMoreAnalogInput (pdev, (BYTE) ui_ArgArray[0], b_ChannelArray, b_GainArray, b_PolarityArray, ui_ConvertTiming, 0);

	if(ret)
		return ret;

	/* no need to lock i_APCI3001_StartReadMoreAnalogInput()  */ 
    if ((ret = i_APCI3001_StartReadMoreAnalogInput (pdev, (BYTE) 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 i_APCI3001_TestEndOfReadMoreConversion() */
            ret = i_APCI3001_TestEndOfReadMoreConversion (pdev);

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

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

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

    return 0;

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

int do_CMD_APCI3001_InitReadMoreAnalogInput (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	INT i = 0;
	UINT ui_ArgArray[(16 * 3) + 1 + 1]; /* b_ChannelArray[16] + b_GainArray[16] + b_PolarityArray[16] + b_SequenzArraySize + ui_ConvertTiming */
	BYTE b_ChannelArray[16] = {0};
	BYTE b_GainArray[16] = {0};
	BYTE b_PolarityArray[16] = {0};
	UINT ui_ConvertTiming = 0;

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

    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 i_APCI3001_ReadMoreValues() */
	ret = i_APCI3001_InitReadMoreAnalogInput (pdev, (BYTE) ui_ArgArray[0], b_ChannelArray, b_GainArray, b_PolarityArray, ui_ConvertTiming, 0);
	if(ret)
		return ret;

    return 0;
}

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

int do_CMD_APCI3001_StartReadMoreAnalogInput (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
	int i_ErrorCode = 0;
	UINT ui_Arg;

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

	/* no need to lock */
    if ((i_ErrorCode = i_APCI3001_StartReadMoreAnalogInput (pdev, (BYTE) ui_Arg)) != 0)
    {
    	return i_ErrorCode;
	}

    return 0;
}

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

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

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

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

	if ( copy_from_user( &ui_Arg, (UINT __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 */
    	i_ErrorCode = i_APCI3001_TestEndOfReadMoreConversion (pdev);

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

	} while (i_ErrorCode == 2);

    return i_ErrorCode;
}

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

int do_CMD_APCI3001_ReadMoreValues (struct pci_dev *pdev, unsigned int cmd, unsigned long arg)
{
   	INT i_ErrorCode = 0;
   	UINT ui_ArgArray[16 + 1]; /* b_SequenzArraySize + ui_AnalogInputValueArray */
   	UINT ui_AnalogInputValueArray[16] = {0};

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

	/* no need to lock */
    if ((i_ErrorCode = i_APCI3001_ReadMoreValues (pdev, (BYTE) ui_ArgArray[0], ui_AnalogInputValueArray)) != 0)
    {
         return i_ErrorCode;
    }

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

      return 0;
}

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