/** @file ioctl.c
*
* @author Sylvain Nahas
*
* @date 05.01.06
*
* @version  $LastChangedRevision$
* @version $LastChangedDate$
*
* This module implements the xpci3009_do_ioctl and the GetHardwareInformation functions
*
*/

/** @par LICENCE
* @verbatim
 Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code and the documentation.

        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 shoud find the complete GPL in the COPYING file accompanying
    this source code.
* @endverbatim
*/

#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
	#include <linux/config.h>
#else
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
		#include <linux/autoconf.h>
	#else
		#include <generated/autoconf.h>
	#endif
#endif
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/pci.h>
#include <asm/io.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)
	#include <asm/system.h>
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0)
    #include <asm/uaccess.h>
#else
    #include <linux/uaccess.h>
#endif
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/sched.h>

#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/delay.h>

#include "xpci3009-private.h"
#include "vtable.h"
//#include "privdata.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

//------------------------------------------------------------------------------
/** dummy command to be called when a ioctl command is incorrect */
static int xpci3009_do_dummy(struct pci_dev * pdev, unsigned int cmd, unsigned long arg)
{
	printk (KERN_WARNING "%s: %d: invalid ioctl\n",__DRIVER_NAME,_IOC_NR(cmd));
	return -EINVAL;
}
//------------------------------------------------------------------------------
/** add new ioctl handlers here */
void xpci3009_init_vtable(vtable_t vtable)
{

	xpci3009_dummyze_vtable(vtable, xpci3009_do_dummy);


	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_GetHardwareInformation, do_CMD_xpci3009_GetHardwareInformation);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_TestInterrupt, do_CMD_xpci3009_TestInterrupt);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_InitAnalogInput , do_CMD_xpci3009_InitAnalogInput);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StartAnalogInput , do_CMD_xpci3009_StartAnalogInput);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReadAnalogInputBit , do_CMD_xpci3009_ReadAnalogInputBit);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReadAnalogInputValue , do_CMD_xpci3009_ReadAnalogInputValue);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_InitAnalogInputAutoRefresh , do_CMD_xpci3009_InitAnalogInputAutoRefresh);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StartAnalogInputAutoRefresh , do_CMD_xpci3009_StartAnalogInputAutoRefresh);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StopAnalogInputAutoRefresh , do_CMD_xpci3009_StopAnalogInputAutoRefresh);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReadAnalogInputAutoRefreshValueAndCounter , do_CMD_xpci3009_ReadAnalogInputAutoRefreshValueAndCounter);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReleaseAnalogInputAutoRefresh , do_CMD_xpci3009_ReleaseAnalogInputAutoRefresh);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_InitAnalogInputSequence , do_CMD_xpci3009_InitAnalogInputSequence);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StartAnalogInputSequence , do_CMD_xpci3009_StartAnalogInputSequence);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StopAnalogInputSequence , do_CMD_xpci3009_StopAnalogInputSequence);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReleaseAnalogInputSequence , do_CMD_xpci3009_ReleaseAnalogInputSequence);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_EnableDisableAnalogInputHardwareTrigger , do_CMD_xpci3009_EnableDisableAnalogInputHardwareTrigger);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_GetAnalogInputHardwareTriggerStatus , do_CMD_xpci3009_GetAnalogInputHardwareTriggerStatus);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_EnableDisableAnalogInputSoftwareTrigger , do_CMD_xpci3009_EnableDisableAnalogInputSoftwareTrigger);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_AnalogInputSoftwareTrigger , do_CMD_xpci3009_AnalogInputSoftwareTrigger);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_GetAnalogInputSoftwareTriggerStatus , do_CMD_xpci3009_GetAnalogInputSoftwareTriggerStatus);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_EnableDisableAnalogInputHardwareGate , do_CMD_xpci3009_EnableDisableAnalogInputHardwareGate);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_GetAnalogInputHardwareGateStatus , do_CMD_xpci3009_GetAnalogInputHardwareGateStatus);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_InitAnalogOutput , do_CMD_xpci3009_InitAnalogOutput);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReadAnalogOutputBit , do_CMD_xpci3009_ReadAnalogOutputBit);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_WriteAnalogOutputValue , do_CMD_xpci3009_WriteAnalogOutputValue);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_Read32DigitalInputs, do_CMD_xpci3009_Read32DigitalInputs);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_InitDigitalInputModuleFilter, do_CMD_xpci3009_InitDigitalInputModuleFilter);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_Set32DigitalOutputsOn , do_CMD_xpci3009_Set32DigitalOutputsOn);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_Set32DigitalOutputsOff, do_CMD_xpci3009_Set32DigitalOutputsOff);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_SetDigitalOutputMemoryOn, do_CMD_xpci3009_SetDigitalOutputMemoryOn);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_SetDigitalOutputMemoryOff, do_CMD_xpci3009_SetDigitalOutputMemoryOff);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_Get32DigitalOutputStatus, do_CMD_xpci3009_Get32DigitalOutputStatus);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_InitTimer, do_CMD_xpci3009_InitTimer);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReleaseTimer, do_CMD_xpci3009_ReleaseTimer);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StartTimer, do_CMD_xpci3009_StartTimer);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StartAllTimers, do_CMD_xpci3009_StartAllTimers);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_TriggerTimer, do_CMD_xpci3009_TriggerTimer);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_TriggerAllTimers, do_CMD_xpci3009_TriggerAllTimers);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StopTimer, do_CMD_xpci3009_StopTimer);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StopAllTimers, do_CMD_xpci3009_StopAllTimers);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReadTimerValue, do_CMD_xpci3009_ReadTimerValue);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReadTimerStatus, do_CMD_xpci3009_ReadTimerStatus);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_EnableDisableTimerInterrupt, do_CMD_xpci3009_EnableDisableTimerInterrupt);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_EnableDisableTimerHardwareOutput, do_CMD_xpci3009_EnableDisableTimerHardwareOutput);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_GetTimerHardwareOutputStatus, do_CMD_xpci3009_GetTimerHardwareOutputStatus);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_InitCounter, do_CMD_xpci3009_InitCounter);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReleaseCounter, do_CMD_xpci3009_ReleaseCounter);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StartCounter, do_CMD_xpci3009_StartCounter);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StartAllCounters, do_CMD_xpci3009_StartAllCounters);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_TriggerCounter, do_CMD_xpci3009_TriggerCounter);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_TriggerAllCounters, do_CMD_xpci3009_TriggerAllCounters);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StopCounter, do_CMD_xpci3009_StopCounter);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StopAllCounters, do_CMD_xpci3009_StopAllCounters);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ClearCounter, do_CMD_xpci3009_ClearCounter);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReadCounterValue, do_CMD_xpci3009_ReadCounterValue);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReadCounterStatus, do_CMD_xpci3009_ReadCounterStatus);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_EnableDisableCounterInterrupt, do_CMD_xpci3009_EnableDisableCounterInterrupt);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_EnableDisableCounterHardwareOutput, do_CMD_xpci3009_EnableDisableCounterHardwareOutput);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_GetCounterHardwareOutputStatus, do_CMD_xpci3009_GetCounterHardwareOutputStatus);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_InitWatchdog, do_CMD_xpci3009_InitWatchdog);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReleaseWatchdog, do_CMD_xpci3009_ReleaseWatchdog);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StartWatchdog, do_CMD_xpci3009_StartWatchdog);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StartAllWatchdogs, do_CMD_xpci3009_StartAllWatchdogs);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_TriggerWatchdog, do_CMD_xpci3009_TriggerWatchdog);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_TriggerAllWatchdogs, do_CMD_xpci3009_TriggerAllWatchdogs);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StopWatchdog, do_CMD_xpci3009_StopWatchdog);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_StopAllWatchdogs, do_CMD_xpci3009_StopAllWatchdogs);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReadWatchdogStatus, do_CMD_xpci3009_ReadWatchdogStatus);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_ReadWatchdogValue, do_CMD_xpci3009_ReadWatchdogValue);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_EnableDisableWatchdogInterrupt, do_CMD_xpci3009_EnableDisableWatchdogInterrupt);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_EnableDisableWatchdogHardwareOutput, do_CMD_xpci3009_EnableDisableWatchdogHardwareOutput);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable, CMD_xpci3009_GetWatchdogHardwareOutputStatus, do_CMD_xpci3009_GetWatchdogHardwareOutputStatus);

	/* Incremental counter */
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_InitCounter,do_CMD_XPCI3009_InitCounter);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_ClearCounterValue,do_CMD_XPCI3009_ClearCounterValue);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_ClearAllCounterValue,do_CMD_XPCI3009_ClearAllCounterValue);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_InitCompareLogic,do_CMD_XPCI3009_InitCompareLogic);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_EnableCompareLogic,do_CMD_XPCI3009_EnableCompareLogic);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_DisableCompareLogic,do_CMD_XPCI3009_DisableCompareLogic);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_Read16BitCounterValue,do_CMD_XPCI3009_Read16BitCounterValue);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_Read32BitCounterValue,do_CMD_XPCI3009_Read32BitCounterValue);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_SetDigitalChlOn,do_CMD_XPCI3009_SetDigitalChlOn);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_SetDigitalChlOff,do_CMD_XPCI3009_SetDigitalChlOff);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_EnableLatchInterrupt,do_CMD_XPCI3009_EnableLatchInterrupt);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_DisableLatchInterrupt,do_CMD_XPCI3009_DisableLatchInterrupt);

	// Utils
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_GetModulesId,do_CMD_XPCI3009_GetModulesId);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_GetBoardType,do_CMD_XPCI3009_GetBoardType);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_READ,do_CMD_XPCI3009_READ);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_WRITE,do_CMD_XPCI3009_WRITE);

	/* Digital I/O */
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_InitDigitalIO,do_CMD_XPCI3009_InitDigitalIO);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_ReadDigitalIOChlValue,do_CMD_XPCI3009_ReadDigitalIOChlValue);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_ReadDigitalIOPortValue,do_CMD_XPCI3009_ReadDigitalIOPortValue);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_SetDigitalIOMemoryOn,do_CMD_XPCI3009_SetDigitalIOMemoryOn);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_SetDigitalIOMemoryOff,do_CMD_XPCI3009_SetDigitalIOMemoryOff);

	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_SetDigitalIOChlOn,do_CMD_XPCI3009_SetDigitalIOChlOn);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_SetDigitalIOChlOff,do_CMD_XPCI3009_SetDigitalIOChlOff);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_SetDigitalIOPortOn,do_CMD_XPCI3009_SetDigitalIOPortOn);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_SetDigitalIOPortOff,do_CMD_XPCI3009_SetDigitalIOPortOff);

	/* Interrupts */
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_SetBoardIntRoutine,do_CMD_XPCI3009_SetBoardIntRoutine);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_ResetBoardIntRoutine,do_CMD_XPCI3009_ResetBoardIntRoutine);
	__xpci3009_DECLARE_IOCTL_HANDLER(vtable,CMD_XPCI3009_TestModuleInterrupt,do_CMD_XPCI3009_TestModuleInterrupt);
}

//------------------------------------------------------------------------------
int xpci3009_do_ioctl(struct pci_dev * pdev,unsigned int cmd,unsigned long arg)
{

//	printk("%s\n",__FUNCTION__); // __FILE__,__FUNCTION__,__LINE__

	/* boundaries check
	 *
	 * VTABLE_ELEMENT_NB(vtable_t) = __xpci3009_UPPER_IOCTL_CMD +1
	 * maximum index value = __xpci3009_UPPER_IOCTL_CMD = VTABLE_ELEMENT_NB(vtable_t) -1
	 *
	 * the idea here is to favorize compilation-time
	 *
	 * */

	if (_IOC_NR(cmd) > (VTABLE_ELEMENT_NB(vtable_t)-1) )
		return xpci3009_do_dummy(pdev,cmd,arg);

	/* call actual ioctl handler - should be safe now */
	return (xpci3009_vtable[_IOC_NR(cmd)]) (pdev, cmd, arg);
}
//------------------------------------------------------------------------------
int do_CMD_xpci3009_GetHardwareInformation(struct pci_dev * pdev, unsigned int cmd, unsigned long arg)
{
	struct xpci3009_USER_str_BoardInformations xpci3009_USER_s_BoardInformations;
	xpci3009_USER_s_BoardInformations.dw_BoardBaseAddress[0] = (unsigned long) GET_MEM_BAR0(pdev);
	xpci3009_USER_s_BoardInformations.dw_BoardBaseAddress[1] = GET_BAR1(pdev);
	xpci3009_USER_s_BoardInformations.dw_BoardBaseAddress[2] = GET_BAR2(pdev);
	xpci3009_USER_s_BoardInformations.dw_BoardBaseAddress[3] = (unsigned long) GET_MEM_BAR3(pdev);
	xpci3009_USER_s_BoardInformations.b_InterruptNbr = pdev->irq;
	xpci3009_USER_s_BoardInformations.b_SlotNumber = PCI_SLOT(pdev->devfn);

	if ( copy_to_user ((struct xpci3009_USER_str_BoardInformations __user *)arg,&xpci3009_USER_s_BoardInformations, sizeof(struct xpci3009_USER_str_BoardInformations)) )
		return -EFAULT;

	return 0;
}

