/** @file diagnostic-api.c

Contains diagnostic analog function (called when ioctl)

@par CREATION
@author Rose Jordan
@date   20.08.2010

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

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

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
  #define __iomem
#endif

/*
 * IOCTL command called to enable (or disable) to generate interruption when a short circuit on primary line is detected
 * @param in arg : 0 for disable , 1 for enable
 */
int do_CMD_apci3701_enable_disable_primary_short_circuit_interrupt(struct pci_dev * pdev, unsigned int cmd, unsigned long arg)
{
	/*
	 * Get the data given by the user
	 */
	uint8_t b_flag = (uint8_t)arg;

	if ( b_flag == 1)
	{
		// Enable the interruption for short circuit
		i_APCI3701_PrimaryTransducerEnableDisablePrimaryShortCircuitInterrupt(pdev, 1);

		// Enable test
		i_APCI3701_PrimaryTransducerEnableDisablePrimaryOpenInputTest(pdev,1);
	}
	else
	{
		i_APCI3701_PrimaryTransducerEnableDisablePrimaryShortCircuitInterrupt(pdev, 0);
		i_APCI3701_PrimaryTransducerEnableDisablePrimaryOpenInputTest(pdev,0);
	}

	return 0;
}

/*
 * IOCTL command called to enable or disable the open circuit test on the primary line
 * @param in arg : 0 for disable , 1 for enable
 */
int do_CMD_apci3701_enable_disable_primary_open_circuit_test(struct pci_dev * pdev, unsigned int cmd, unsigned long arg)
{
	/*
	 * Get the data given by the user
	 */
	uint8_t b_flag = (uint8_t)arg;

	return i_APCI3701_PrimaryTransducerEnableDisablePrimaryOpenInputTest(pdev,b_flag);
}

/*
 * IOCTL command called to enable the "rearm" the primary test
 * This enable to restart the card
 * When the card detected a short circuit on the primary line (when we activate the interruption for it), it stops to generate the signal
 * When we rearm the primary line, the signal is generated again
 */
int do_CMD_apci3701_ream_primary_open_short_circuit_test(struct pci_dev * pdev, unsigned int cmd, unsigned long arg)
{
	i_APCI3701_PrimaryTransducerRearmPrimaryOpenShortCircuitTest(pdev);
	return 0;
}

/*
 * IOCTL command called to enable (or disable) the diagnostic of a short circuit on the secondary line
 * @param in arg : 0 for disable , 1 for enable
 */
int do_CMD_apci3701_enable_disable_secondary_short_circuit_test(struct pci_dev * pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	/*
	 * Get the data given by the user
	 */
	uint8_t b_flag = (uint8_t)arg;

	ret = i_APCI3701_PrimaryTransducerEnableDisableSecondaryShortCircuitTest(pdev, b_flag);

	return ret;
}

/*
 * IOCTL command called to enable (or disable) the diagnostic of an open circuit on the secondary line
 * @param in arg : 0 for disable , 1 for enable
 */
int do_CMD_apci3701_enable_disable_secondary_open_circuit_test(struct pci_dev * pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	/*
	 * Get the data given by the user
	 */
	uint8_t b_flag = (uint8_t)arg;

	ret = i_APCI3701_PrimaryTransducerEnableDisableSecondaryOpenCircuitTest ( pdev, b_flag );

	return ret;
}

/*
 * IOCTL command called to test if a channel suffer of open circuit or not on secondary line
 * @param in: 	arg[0] => the index of the channel that we want to test
 * @param in:	arg[1] => the index of the transducer. ex: 24 for Solartron AX1
 *
 * @param out: 	arg[0] => 0 if there is no open circuit on this channel, 1 if there is an open circuit on this channel
 *
 * @retval:
 * 1:	channel index not valid
 * 2:	transducer not initialized
 * 3:	error while getting transducer's values
 * 4:	error while enabling secondary open circuit test
 * 5:	error while disabling secondary open circuit test
 * 6:	error while reading analog input value
 */
int do_CMD_apci3701_test_channel_secondary_line_open_circuit(struct pci_dev * pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	uint16_t open_circuit_val = 0;
	uint16_t short_circuit_val = 0;
	uint32_t value = 0;
	uint32_t is_open_circuit = 0; 	// 0 if no open circuit and 1 if there is an open circuit
									// this value will be returned

	/*
	 * Get the data given by the user
	 */
	uint32_t arg_array[2];

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

	if ( arg_array[0] >= APCI3701_NUMBER_OF_ANALOG_INPUT)
		return 1;

	// First of all, we test if the transducer was initialized
	if ( APCI3701_PRIVDATA(pdev)->transducers_type[arg_array[0]] == 0 )
		return 2;

	ret = i_APCI3701_get_transducer_secondary_line_diagnostic_values( pdev, (uint16_t)arg_array[1], &short_circuit_val, &open_circuit_val);
	if ( ret != 0 )
	{
		return 3;
	}

	/*
	 * Enable the use of open circuit diagnostic on secondary line
	 */
	if ( i_APCI3701_PrimaryTransducerEnableDisableSecondaryOpenCircuitTest(pdev, 1) != 0)
		return 4;


	/*
	 * Read the value of the channel
	 */
	if ( i_APCI3701_read_1_analog_channel(pdev, (uint8_t)arg_array[0], &value) != 0 )
		return 6;

	/*
	 * Test if we have an open circuit or not
	 */
	if ( value > open_circuit_val )
	{
		is_open_circuit = 1;
	}
	else
	{
		is_open_circuit = 0;
	}

	/*
	 * Disable the use of open circuit diagnostic on secondary line
	 */
	if ( i_APCI3701_PrimaryTransducerEnableDisableSecondaryOpenCircuitTest(pdev, 0) != 0)
		return 5;

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

	return 0;
}

/*
 * IOCTL command called to test if a channel suffer of short circuit or not on secondary line
 * @param in: 	arg[0] => the index of the channel that we want to test
 * @param in:	arg[1] => the index of the transducer. ex: 24 for Solartron AX1
 *
 * @param out: 	arg[0] => 0 if there is no short circuit on this channel, 1 if there is a short circuit on this channel
 *
 * @retval:
 * 1:	channel index not valid
 * 2:	transducer not initialized
 * 3:	error while getting transducer's values
 * 4:	error while enabling secondary short circuit test
 * 5:	error while disabling secondary short circuit test
 * 6:	error while reading analog input value
 */
int do_CMD_apci3701_test_channel_secondary_line_short_circuit(struct pci_dev * pdev, unsigned int cmd, unsigned long arg)
{
	int ret = 0;
	uint16_t open_circuit_val = 0;
	uint16_t short_circuit_val = 0;
	uint32_t value = 0;
	uint32_t is_short_circuit = 0; 	// 0 if no open circuit and 1 if there is an open circuit
									// this value will be returned

	/*
	 * Get the data given by the user
	 */
	uint32_t arg_array[2];

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

	if ( arg_array[0] >= APCI3701_NUMBER_OF_ANALOG_INPUT)
		return 1;

	// First of all, we test if the transducer was initialized
	if ( APCI3701_PRIVDATA(pdev)->transducers_type[arg_array[0]] == 0 )
		return 2;

	ret = i_APCI3701_get_transducer_secondary_line_diagnostic_values( pdev, (uint16_t)arg_array[1], &short_circuit_val, &open_circuit_val);
	if ( ret != 0 )
	{
		return 3;
	}

	/*
	 * Enable the use of open circuit diagnostic on secondary line
	 */
	if ( i_APCI3701_PrimaryTransducerEnableDisableSecondaryShortCircuitTest(pdev, 1) != 0)
		return 4;


	/*
	 * Read the value of the channel
	 */
	if ( i_APCI3701_read_1_analog_channel(pdev, (uint8_t)arg_array[0], &value) != 0 )
		return 6;

	/*
	 * Test if we have an open circuit or not
	 */
	if ( value < short_circuit_val )
	{
		is_short_circuit = 1;
	}
	else
	{
		is_short_circuit = 0;
	}

	/*
	 * Disable the use of open circuit diagnostic on secondary line
	 */
	if ( i_APCI3701_PrimaryTransducerEnableDisableSecondaryShortCircuitTest(pdev, 0) != 0)
		return 5;

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

	return 0;
}

/*
 * IOCTL command called to test the primary line status

 * @param out: 	arg[0] => the status. 0 if nothing, 1 if open circuit, 2 if short circuit, 3 if both
 */
int do_CMD_apci3701_test_primary_line(struct pci_dev * pdev, unsigned int cmd, unsigned long arg)
{
	/*
	 * Get the data given by the user
	 */
	int ret = 0;
	uint32_t arg_array[1];
	uint32_t status = 0;

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

	if (  (ret = i_APCI3701_test_primary_line( pdev, &status)) != 0 )
	{
		return ret;
	}

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

	return ret;
}
