/** @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 "apci2016-private.h"

/* outputs are in BAR 1 */

EXPORT_NO_SYMBOLS;

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

/**
 * @note In this module, IO are indiced from 0
 */

/** flush internal output register representation to board 
 * access is 16 bits on BAR 1 (offset 0x4)
 * 
 * 
 * */
static __inline__ void FLUSH_OUTPUT(struct pci_dev * pdev)
{
	//printk("%s: %s %d %X\n",__DRIVER_NAME,__FUNCTION__,APCI2016_PRIVDATA(pdev)->DigitalOutputState,GET_BAR1(pdev) + APCI2016_DIGITAL_OUTPUT_REG);
	outw ( APCI2016_PRIVDATA(pdev)->DigitalOutputState, GET_BAR1(pdev) + APCI2016_DIGITAL_OUTPUT_REG);
	
}

/** read actual output status from board
 * 
 * access is 16 bits on BAR 1 (offset 0x4)
 * 
 * */
static __inline__ uint16_t GET_OUTPUT_STATUS(struct pci_dev * pdev)
{
	return inw ( GET_BAR1(pdev) + APCI2016_DIGITAL_OUTPUT_REG);
}

//-------------------------------------------------------------------	     
int do_CMD_APCI2016_Set1DigitalOutputOn(struct pci_dev * pdev, unsigned long arg )
{
	unsigned long irqstate;
	APCI2016_LOCK(pdev,&irqstate);
	{
		if( APCI2016_OUTP_MEM_ON(pdev) )
		{
			APCI2016_PRIVDATA(pdev)->DigitalOutputState = APCI2016_PRIVDATA(pdev)->DigitalOutputState |  (1 << arg);
		}
		else
		{
		   	APCI2016_PRIVDATA(pdev)->DigitalOutputState = (1 << arg);
		}
		FLUSH_OUTPUT(pdev); 
	}
	APCI2016_UNLOCK(pdev,irqstate);

	return 0;
}
//-------------------------------------------------------------------	     
int do_CMD_APCI2016_Set1DigitalOutputOff(struct pci_dev * pdev, unsigned long arg )
{
	unsigned long irqstate;
	APCI2016_LOCK(pdev,&irqstate);
	{
		if ( !APCI2016_OUTP_MEM_ON(pdev) )
		{
			APCI2016_UNLOCK(pdev,irqstate);
			return -EINVAL;
		}
		
		APCI2016_PRIVDATA(pdev)->DigitalOutputState &= (0xFFFFFFFF - (1 << arg));	
	
		FLUSH_OUTPUT(pdev);
	}
	APCI2016_UNLOCK(pdev,irqstate);	
	return 0;
}
//-------------------------------------------------------------------	     
int do_CMD_APCI2016_Set16DigitalOutputsOn(struct pci_dev * pdev, uint16_t arg )
{
	unsigned long irqstate;
	APCI2016_LOCK(pdev, &irqstate);
	{
		if( APCI2016_OUTP_MEM_ON(pdev) )
		{
			APCI2016_PRIVDATA(pdev)->DigitalOutputState |= arg  ;
		}
		else
		{
		   	APCI2016_PRIVDATA(pdev)->DigitalOutputState = arg;
		}
		FLUSH_OUTPUT(pdev); 
	}
	APCI2016_UNLOCK(pdev,irqstate);

	return 0;
}
//-------------------------------------------------------------------	     
int do_CMD_APCI2016_Set16DigitalOutputsOff(struct pci_dev * pdev, uint16_t arg )
{
	unsigned long irqstate;
	APCI2016_LOCK(pdev, &irqstate);
	{
		if ( !APCI2016_OUTP_MEM_ON(pdev) )
		{
			APCI2016_UNLOCK(pdev,irqstate);
			return -EINVAL;
		}
		APCI2016_PRIVDATA(pdev)->DigitalOutputState &= (0xFFFF - arg );	
	
		FLUSH_OUTPUT(pdev);
	}
	APCI2016_UNLOCK(pdev,irqstate);	
	return 0;
}

//-------------------------------------------------------------------
int do_CMD_APCI2016_Get1DigitalOutputStatus(struct pci_dev * pdev, unsigned int __user * arg)
{
	unsigned int index = 0; /* bit index */
	volatile uint16_t outpstatus = 0;
	
	if ( copy_from_user(&index, arg, sizeof(unsigned int)) )
		return -EFAULT;
	
	printk("%s:index=%d\n",__DRIVER_NAME,index);
	
	/* index given by user must be between 1 and 16 included*/	
	switch(index)
	{
		case 1 ... 16:
			break;
		default:
			return -EINVAL;
	}
	
	/* we want bit indexed from 0 */
	index--;
	
	{
		unsigned long irqstate;
		APCI2016_LOCK(pdev, &irqstate);
		{	
			 outpstatus = GET_OUTPUT_STATUS(pdev);
		}
		APCI2016_UNLOCK(pdev,irqstate);
	}
	outpstatus = (outpstatus >> index) & 0x1;
	
	/* cast outpstatus to constant type to avoid warning at compilation time */
	if ( copy_to_user(arg,(uint16_t *) &outpstatus, sizeof(uint16_t) ) )
		return -EFAULT;	
		
	return 0;
}
//-------------------------------------------------------------------

int do_CMD_APCI2016_Get16DigitalOutputStatus(struct pci_dev * pdev, uint16_t __user * arg )
{
	volatile uint16_t outpstatus = 0;
	unsigned long irqstate;
	
	APCI2016_LOCK(pdev, &irqstate);
	{	
		 outpstatus = GET_OUTPUT_STATUS(pdev);
	}
	APCI2016_UNLOCK(pdev, irqstate);
	
	/* cast outpstatus to constant type to avoid warning at compilation time */
	if ( copy_to_user(arg,(uint16_t *) &outpstatus, sizeof(uint16_t) ) )
		return -EFAULT;
	
	return 0;

}
