/** @file analog_in-kapi.c
 
   Kernel functions for the analog input channels.
 
   @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"

EXPORT_SYMBOL(i_APCI3001_InitAnalogInput);
EXPORT_SYMBOL(i_APCI3001_StartAnalogInput);
EXPORT_SYMBOL(i_APCI3001_ReadAnalogInputBit);
EXPORT_SYMBOL(i_APCI3001_ReadAnalogInputValue);
EXPORT_SYMBOL(i_APCI3001_ReadMoreAnalogInputs);

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

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

/** Initialize the analog input value.
 * 
 * @param [in] 	pdev			: The device to initialize.
 * @param [in]	b_ChannelNumber	: The analog input channel.
 * @param [in]	b_Gain			: Gain factor of the analog input: <br>
 *									- APCI3001_1_GAIN : Gain =  1 <br>
 *									- APCI3001_2_GAIN : Gain =  2 <br>
 *									- APCI3001_5_GAIN : Gain =  5 <br>
 *									- APCI3001_10_GAIN: Gain = 10
 * @param [in]	b_Polarity		: Polarity of the analog input: <br>
 *									- APCI3001_UNIPOLAR:  0V - +10V <br>
 *									- APCI3001_BIPOLAR: -10V - +10V
 * @param [in]	ui_ConvertTiming: The convert timing of the analog input: <br>
 *									from 10us up to 32767us
 * @param [in]	b_InterruptFlag: This parameter has to be 0 or APCI3001_DISABLE.<br> 
 * 																The interrupt is not implemeted!
 * 
 * @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.
 */                                                                           
INT i_APCI3001_InitAnalogInput (struct pci_dev *pdev, 
				BYTE b_ChannelNumber,
                                BYTE b_Gain, 
                                BYTE b_Polarity,
				UINT ui_ConvertTiming,
				BYTE b_InterruptFlag)
{
	BYTE  b_DummyRead = 0;
	USHORT ui_DummyRead = 0;

	b_InterruptFlag = APCI3001_DISABLE;

	if (!pdev) return 1;
	
	/* Check the channel number */		
	if (b_ChannelNumber >= APCI3001_PRIVDATA(pdev)->nrAnaIn) return 2;

	APCI3001_PRIVDATA(pdev)->lastChannel = b_ChannelNumber;
        
	/* Test the gain */
	if (b_Gain != APCI3001_1_GAIN  && 
		b_Gain != APCI3001_2_GAIN  && 
		b_Gain != APCI3001_5_GAIN  && 
		b_Gain != APCI3001_10_GAIN) return 3;

	/* Test the polarity */
	if (b_Polarity != APCI3001_UNIPOLAR && b_Polarity != APCI3001_BIPOLAR) return 4;

	/* Test the converting time */
	if ((ui_ConvertTiming < 10) || (ui_ConvertTiming > 32767)) return 5;
	
	
	if ((b_InterruptFlag == APCI3001_ENABLE) && (APCI3001_PRIVDATA(pdev)->interruptInitialized == APCI3001_DISABLE)) return 6;

	/* Set the interrupt mode */
	if (b_InterruptFlag == APCI3001_ENABLE)
		APCI3001_PRIVDATA(pdev)->intMode = APCI3001_EOC_INT_MODE;
	else
		APCI3001_PRIVDATA(pdev)->intMode = 0;
                    
	/* Initialise the sequence array */
	outw ((0 << 8) + (b_ChannelNumber | b_Gain | b_Polarity), GET_BAR1(pdev) + APCI3001_FIFO_ADDRESS);
		   
	/* Clear the FIFO of the board */
	b_DummyRead = inb (GET_BAR1(pdev) + APCI3001_RESET_FIFO);
				       
	/* Initialize the timer 0 mode 5 (Start by trigger) */
	APCI3001_PRIVDATA(pdev)->crt1 = (APCI3001_PRIVDATA(pdev)->crt1 & 0xFC) | APCI3001_TIMER_0_MODE_5;
		     
	outb (APCI3001_PRIVDATA(pdev)->crt1, GET_BAR1(pdev) + APCI3001_TIMER_CRT1);

	/* Write the interrupt select register */
	if (b_InterruptFlag == APCI3001_ENABLE)
		APCI3001_PRIVDATA(pdev)->modeSelect = ((APCI3001_PRIVDATA(pdev)->modeSelect & 0x34) | APCI3001_ENABLE_EOC_INT);
	else
		APCI3001_PRIVDATA(pdev)->modeSelect = APCI3001_PRIVDATA(pdev)->modeSelect & 0x34;
                       
	outb (APCI3001_PRIVDATA(pdev)->modeSelect, (GET_BAR1(pdev) + APCI3001_WRITE_MODE_SELECT));

	ui_DummyRead = inw(GET_BAR1(pdev) + APCI3001_RD_STATUS);

	/* Set gate 0 */
	APCI3001_PRIVDATA(pdev)->nWrAddr = (APCI3001_PRIVDATA(pdev)->nWrAddr & APCI3001_CLEAR_PA_PR) |  APCI3001_ENABLE_TIMER0;

	outw (APCI3001_PRIVDATA(pdev)->nWrAddr, GET_BAR1(pdev) + APCI3001_WR_ADDRESS);
			  
	/* Convert the Convertion Time to the right format */
	ui_ConvertTiming = (USHORT) (((DWORD) ((DWORD) (ui_ConvertTiming) * 2L)) - 2);

	APCI3001_PRIVDATA(pdev)->crt0.timer_select = APCI3001_SELECT_TIMER_0_WORD;
	outb (APCI3001_PRIVDATA(pdev)->crt0.raw, GET_BAR1(pdev) +  APCI3001_TIMER_CRT0);

	/* Write the conversion timing */
	outw (ui_ConvertTiming, GET_BAR1(pdev) + APCI3001_TIMER_VALUE);
  
	return 0;
}

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

/** Start the analog input conversion.
 * 
 * @param [in] 	pdev			: The device to initialize.
 * 
 * @retval 0: No error.
 * @retval 1: The handle parameter of the board is wrong.
 */
INT i_APCI3001_StartAnalogInput (struct pci_dev *pdev)
{
  	USHORT ui_DummyWrite  = 0;

	if (!pdev) return 1;

	/* Dummy write for the software trigger to start the conversation */
	outw (ui_DummyWrite, (GET_BAR1(pdev) + APCI3001_RD_STATUS));

	return 0;
}

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

/** Read the analog input conversion bit.
 * 
 * Test the endo of conversion.
 * 
 * @param [in] 	pdev			: The device to initialize.
 * 
 * @retval 0: No error.
 * @retval 1: The handle parameter of the board is wrong.
 * @retval 2: The EOC flag is not set.
 */
INT i_APCI3001_ReadAnalogInputBit (struct pci_dev *pdev)
{
	UINT ui_StatusValue = 0;
  
	if (!pdev) return 1;

	/* Reads the EOC (end of conversion) flag (low active) */
	ui_StatusValue = inw (GET_BAR1(pdev) + APCI3001_RD_STATUS);

	/* Return 2 if the EOC flag is not ready */
	if ((ui_StatusValue & APCI3001_EOC) != APCI3001_EOC)
	{
		return 2;
	}

	return 0;
}

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

/** Read the analog input value.
 * 
 * @param [in] 	pdev			: The device to initialize.
 * @param [out]	ui_ReadValue	: The analog input value.
 * 
 * @retval 0: No error.
 * @retval 1: The handle parameter of the board is wrong.
 */
INT i_APCI3001_ReadAnalogInputValue (struct pci_dev *pdev, UINT *ui_ReadValue)
{
		if (!pdev) return 1;
		
		*ui_ReadValue = inw (GET_BAR1(pdev));
				
		return 0;
}		
	
//------------------------------------------------------------------------------

/* returns true if conversion finished */
static int check_condition_ReadMoreAnalogInputs (struct pci_dev * pdev)
{
	return ( ((inw (GET_BAR1(pdev) + APCI3001_RD_STATUS)) & APCI3001_EOS) == APCI3001_EOS); 
}

//------------------------------------------------------------------------------
			
/** Read more analog input.
 *
 * @param [in] 	pdev				: The device to initialize.
 * @param [in]	b_SequenzArraySize		: Size of the sequence array (1 to 16 elements).
 * @param [in]	pb_ChannelArray			: Channels to read (array size = b_SequenzArraySize).
 * @param [in]	pb_GainArray			: Gain selection array.
 * @param [in]	pb_PolarityArray		: Selection of the input.
 * @param [in]	ui_ConvertTiming		: Selection of the conversion time from 10 to 32767 us.
 * @param [in]	b_InterruptFlag			: This parameter has to be 0 or APCI3001_DISABLE.<br> 
 * 																The interrupt is not implemeted!
 *
 * @param [out]	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.
 * 
 * @warning: this function should not be locked
 */
INT   i_APCI3001_ReadMoreAnalogInputs     (struct pci_dev *pdev, 
					   BYTE    b_SequenzArraySize,
					   BYTE *  pb_ChannelArray,
					   BYTE * pb_GainArray,
					   BYTE *  pb_PolarityArray,
					   UINT    ui_ConvertTiming,
					   BYTE b_InterruptFlag,
					   UINT * pui_AnalogInputValueArray)
{
	BYTE  b_Cpt;
	UINT ui_DummyRead;
	BYTE   b_DummyRead;
	UINT ui_RamArray [16];
	ULONG dw_NbrOfMicroSecond = 0;

		b_InterruptFlag = APCI3001_DISABLE;

		for (b_Cpt = 0; b_Cpt < b_SequenzArraySize; b_Cpt ++)
		{
			/* Test the channel number  */
			if (pb_ChannelArray [b_Cpt] >= 16)
			{
				return 3;
			}

			/* Test the gain */
			if (pb_GainArray [b_Cpt] != APCI3001_1_GAIN &&
			pb_GainArray [b_Cpt] != APCI3001_2_GAIN &&
			pb_GainArray [b_Cpt] != APCI3001_5_GAIN &&
			pb_GainArray [b_Cpt] != APCI3001_10_GAIN)
			{
				return 4;
			}

			/* Test the polarity */
			if (pb_PolarityArray [b_Cpt] != APCI3001_UNIPOLAR && pb_PolarityArray [b_Cpt] != APCI3001_BIPOLAR)
			{
				return 5;
			}

			ui_RamArray [b_Cpt] = (	(UINT) b_Cpt << 8) |
						(pb_GainArray     [b_Cpt] & 0xFF) |
						(pb_PolarityArray [b_Cpt] & 0xFF) |
						(pb_ChannelArray  [b_Cpt] & 0xFF);
		}

		if (b_SequenzArraySize > 16)
		{
			return 2;
		}

		/* Test the conversion time */
		if ((ui_ConvertTiming < 10) || (ui_ConvertTiming > 32767U))
		{
			return 6;
		}

		ui_DummyRead = inw (GET_BAR1(pdev));

		/* Clear the FIFO */
		b_DummyRead = inb (GET_BAR1(pdev) + APCI3001_RESET_FIFO);

		for (b_Cpt = 0; b_Cpt < b_SequenzArraySize; b_Cpt ++)
		{
			/* Initialize the sequence array */
			outw (ui_RamArray [b_Cpt],GET_BAR1(pdev) + APCI3001_FIFO_ADDRESS);
		}

		dw_NbrOfMicroSecond = b_SequenzArraySize * ui_ConvertTiming;
		ui_ConvertTiming    = (UINT)((ULONG)(((ULONG)((((ULONG)(ui_ConvertTiming)) * 2))))) - 2;

		/* Initialize Timer 0 mode 2 */
		outb (APCI3001_TIMER_0_MODE_2,GET_BAR1(pdev) + APCI3001_TIMER_CRT1);

		/* Select timer 0 */
		APCI3001_PRIVDATA(pdev)->crt0.timer_select = APCI3001_SELECT_TIMER_0_WORD;
		outb (APCI3001_PRIVDATA(pdev)->crt0.raw, GET_BAR1(pdev) + APCI3001_TIMER_CRT0);

		/* Set the conversion time */
		outw (ui_ConvertTiming,GET_BAR1(pdev) + APCI3001_TIMER_VALUE);

		/* Set the SCAN bit */
		outb (APCI3001_ENABLE_SCAN,GET_BAR1(pdev) + APCI3001_WRITE_MODE_SELECT);
		ui_DummyRead = inw (GET_BAR1(pdev) + APCI3001_RD_STATUS);

		outw (APCI3001_ENABLE_TIMER0 | (((UINT) b_SequenzArraySize - 1) << 8),GET_BAR1(pdev) + APCI3001_WR_ADDRESS);

		/* Start the conversion */
		outw (0,GET_BAR1(pdev) + APCI3001_START_CONVERSION);
		
		/* busywait with a timeout of dw_NbrOfMicroSecond + 100 microseconds for security */
		if ( apci3001_busywait_with_timeout(pdev, check_condition_ReadMoreAnalogInputs, 20+(dw_NbrOfMicroSecond/5)) )
			return 8;
		
		for (b_Cpt = 0; b_Cpt < b_SequenzArraySize; b_Cpt ++)
		{
			pui_AnalogInputValueArray [b_Cpt] = inw (GET_BAR1(pdev));
		}

	return 0;
}

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

/** Initialize read more analog input.
 *
 * @param [in] pdev						: The device to initialize.
 * @param [in]	b_SequenzArraySize	: Size of the sequence array (1 to 16 elements).
 * @param [in]	pb_ChannelArray		: Channels to read (array size = b_SequenzArraySize).
 * @param [in]	pb_GainArray			: Gain selection array.
 * @param [in]	pb_PolarityArray		: Selection of the input.
 * @param [in]	ui_ConvertTiming		: Selection of the conversion time from 10 to 32767 us.
 * @param [in]	b_InterruptFlag		: This parameter has to be 0 or APCI3001_DISABLE.<br> 
 * 																The interrupt is not implemeted!
 * 
 * @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.
 */
INT i_APCI3001_InitReadMoreAnalogInput (struct pci_dev *pdev, 
													BYTE b_SequenzArraySize,
													BYTE *  pb_ChannelArray,
													BYTE * pb_GainArray,
													BYTE *  pb_PolarityArray,
													UINT ui_ConvertTiming,
													BYTE b_InterruptFlag)
{
	BYTE  b_Cpt;
	UINT ui_DummyRead;
	BYTE   b_DummyRead;
	UINT ui_RamArray [16];
	ULONG dw_NbrOfMicroSecond = 0;

		b_InterruptFlag = APCI3001_DISABLE;

		if (!pdev) return 1;

		for (b_Cpt = 0; b_Cpt < b_SequenzArraySize; b_Cpt ++)
		{
			/* Test the channel number  */
			if (pb_ChannelArray [b_Cpt] >= 16)
			{
				return 3;
			}

			/* Test the gain */
			if (pb_GainArray [b_Cpt] != APCI3001_1_GAIN &&
			pb_GainArray [b_Cpt] != APCI3001_2_GAIN &&
			pb_GainArray [b_Cpt] != APCI3001_5_GAIN &&
			pb_GainArray [b_Cpt] != APCI3001_10_GAIN)
			{
				return 4;
			}

			/* Test the polarity */
			if (pb_PolarityArray [b_Cpt] != APCI3001_UNIPOLAR && pb_PolarityArray [b_Cpt] != APCI3001_BIPOLAR)
			{
				return 5;
			}

			ui_RamArray [b_Cpt] = (	(UINT) b_Cpt << 8) |
						(pb_GainArray     [b_Cpt] & 0xFF) |
						(pb_PolarityArray [b_Cpt] & 0xFF) |
						(pb_ChannelArray  [b_Cpt] & 0xFF);
		}

		if (b_SequenzArraySize > 16)
		{
			return 2;
		}

		/* Test the conversion time */
		if ((ui_ConvertTiming < 10) || (ui_ConvertTiming > 32767U))
		{
			return 6;
		}

		ui_DummyRead = inw (GET_BAR1(pdev));

		/* Clear the FIFO */
		b_DummyRead = inb (GET_BAR1(pdev) + APCI3001_RESET_FIFO);

		for (b_Cpt = 0; b_Cpt < b_SequenzArraySize; b_Cpt ++)
		{
			/* Initialize the sequence array */
			outw (ui_RamArray [b_Cpt],GET_BAR1(pdev) + APCI3001_FIFO_ADDRESS);
		}

		dw_NbrOfMicroSecond = b_SequenzArraySize * ui_ConvertTiming;
		ui_ConvertTiming    = (UINT)((ULONG)(((ULONG)((((ULONG)(ui_ConvertTiming)) * 2))))) - 2;

		/* Initialize Timer 0 mode 2 */
		outb (APCI3001_TIMER_0_MODE_2,GET_BAR1(pdev) + APCI3001_TIMER_CRT1);

		/* Select timer 0 */
		APCI3001_PRIVDATA(pdev)->crt0.timer_select = APCI3001_SELECT_TIMER_0_WORD;
		outb (APCI3001_PRIVDATA(pdev)->crt0.raw, GET_BAR1(pdev) + APCI3001_TIMER_CRT0);

		/* Set the conversion time */
		outw (ui_ConvertTiming,GET_BAR1(pdev) + APCI3001_TIMER_VALUE);

		/* Set the SCAN bit */
		outb (APCI3001_ENABLE_SCAN,GET_BAR1(pdev) + APCI3001_WRITE_MODE_SELECT);
		ui_DummyRead = inw (GET_BAR1(pdev) + APCI3001_RD_STATUS);

		return 0;
}

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

/** Start read more analog input.
 *
 * @param [in] 	pdev						: The device to initialize.
 * @param [in]		b_SequenzArraySize	: Size of the sequence array (1 to 16 elements).
 * 
 * @retval 0: No error.
 * @retval 1: The handle parameter of the board is wrong.
 * @retval 2: The size of the sequence table is wrong.
 */
INT i_APCI3001_StartReadMoreAnalogInput (struct pci_dev *pdev, BYTE b_SequenzArraySize)
{
	if (!pdev) return 1;

	if (b_SequenzArraySize > 16) return 2;
		
	/* Set the scan sequence */
	outw (APCI3001_ENABLE_TIMER0 | (((unsigned short int) b_SequenzArraySize - 1) << 8),GET_BAR1(pdev) + APCI3001_WR_ADDRESS);

	/* Starts the conversion */
	outw (0,GET_BAR1(pdev) + APCI3001_START_CONVERSION);

	return 0;
}

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

/** Test end of conversion of read more analog input.
 *
 * @param [in] 	pdev	: The device to initialize.
 * 
 * @retval 0: No error (Conversion is not running).
 * @retval 1: The handle parameter of the board is wrong.
 * @retval 2: Conversion is running.
 */
INT i_APCI3001_TestEndOfReadMoreConversion (struct pci_dev *pdev)
{
	UINT ui_PortValue = 0;

	if (!pdev) return 1;

	ui_PortValue = inw (GET_BAR1(pdev) + APCI3001_RD_STATUS);

	if (((ui_PortValue & APCI3001_EOS) >> 13) == 0)
		return 2;

	return 0;
}

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

/** Start read more analog input.
 *
 * @param [in] 	pdev						: The device to initialize.
 * @param [in]		b_SequenzArraySize	: Size of the sequence array (1 to 16 elements).
 * @param [out]	pui_Values				: 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.
 */
INT i_APCI3001_ReadMoreValues (struct pci_dev *pdev, BYTE b_SequenzArraySize, UINT * pui_Values)
{
	BYTE b_Cpt = 0;

	if (!pdev) return 1;

	if (b_SequenzArraySize > 16) return 2;

	/* Read the analog value */
	for (b_Cpt = 0; b_Cpt < b_SequenzArraySize; b_Cpt ++)
	{
		pui_Values [b_Cpt] = inw (GET_BAR1(pdev));
	}

	return 0;
}

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

