/** @file dig_init.c
 
   Contains all functions to initialize digital inputs of the APCI-1648.
 
   @par CREATION  
   @author Krauth Julien
   @date   10.06.05
   
   @par VERSION
   @verbatim
   $LastChangedRevision:$
   $LastChangedDate:$
   @endverbatim   
   
   @par COMPILER
   @verbatim
   Compiler	: gcc
   Version	: gcc (GCC) 3.3.6 (Debian 1:3.3.6-5)   
   kernel	: 2.4.26
   @endverbatim  
   
   @par UPDATES
   @verbatim
   Date			:
   Author		: 
   Description	: 
   @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 "apci1648-private.h"
#include <apci1648-kapi.h>

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

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

/** Initialize all digital ports of the APCI-1648.
 *
 * @param [in,out]	pdev	Pointer on the device structure.
 * @param 			PortsSetting		Function argument. An array of 6 unsigned char that has to be initialized as following.<br>
 *                          Each index of the array is a port e.g.:<br>
 *                          PortsSetting [0]: Port 0 (0: Input direction / 1: Output direction)
 *                          PortsSetting [1]: Port 1 (0: Input direction / 1: Output direction)
 *                          PortsSetting [2]: Port 2 (0: Input direction / 1: Output direction)
 *                          PortsSetting [3]: Port 3 (0: Input direction / 1: Output direction)
 *                          PortsSetting [4]: Port 4 (0: Input direction / 1: Output direction)
 *                          PortsSetting [5]: Port 5 (0: Input direction / 1: Output direction)
 *
 * @retval 0		No error. 
 * @retval -EINVAL	Port settings are incorrect.
 */
int APCI1648_InitAllDigitalPorts(struct pci_dev *pdev, const unsigned char PortsSetting[APCI1648_PORT_NUMBER])
{
	/* Legal initializations values are 0 or 1 */
	int i;
	for (i=0; i < APCI1648_PORT_NUMBER; i++)
	{
		switch(PortsSetting[i])
		{
			case 0:
			case 1:
				break;
			default:
				return -EINVAL;
		}  	
	}
	/* Treat ports 0 - 3 */ 
    APCI1648_PRIVDATA(pdev)->RegisterSetting.Register1.AllPorts = 0; 
    APCI1648_PRIVDATA(pdev)->RegisterSetting.Register1.AllPorts = ((uint8_t)(PortsSetting [APCI1648_PORT0] * 0xFF) |
                                                                    ((uint8_t)(PortsSetting [APCI1648_PORT1] * 0xFF) << 8) |
                                                                    ((uint8_t)(PortsSetting [APCI1648_PORT2] * 0xFF) << 16) |
                                                                    ((uint8_t)(PortsSetting [APCI1648_PORT3] * 0xFF) << 24));
  

    /* Write ports 0 - 3 configuration */ 
	outl(APCI1648_PRIVDATA(pdev)->RegisterSetting.Register1.AllPorts, (GET_BAR0(pdev) + APCI1648_CMDREG_1)); 

	APCI1648_PRIVDATA(pdev)->RegisterSetting.Register2.AllPorts = 0;
    
	APCI1648_PRIVDATA(pdev)->RegisterSetting.Register2.AllPorts = ((uint8_t)(PortsSetting [APCI1648_PORT4] * 0xFF) | ((uint8_t)(PortsSetting [APCI1648_PORT5] * 0xFF) << 8) );

	/* Write ports 4 - 7 configuration */ 
	outl(APCI1648_PRIVDATA(pdev)->RegisterSetting.Register2.AllPorts, (GET_BAR0(pdev) + APCI1648_CMDREG_2));

	printk("reread1: %X\n",inl(GET_BAR0(pdev) + APCI1648_CMDREG_1));
	printk("reread2: %X\n",inl(GET_BAR0(pdev) + APCI1648_CMDREG_2));
  	return 0;	
}

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

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

/** Get all digital ports settings of the APCI-1648.
 *
 * @param [in,out]	pdev	Pointer on the device structure.
 * @param 			PortsSetting		Function argument. An array of 6 unsigned char that will contain ports settings.<br>
 *                          Each index of the array is a port e.g.:<br>
 *                          PortsSetting [0]: Port 0 (0: Input direction / 1: Output direction)
 *                          PortsSetting [1]: Port 1 (0: Input direction / 1: Output direction)
 *                          PortsSetting [2]: Port 2 (0: Input direction / 1: Output direction)
 *                          PortsSetting [3]: Port 3 (0: Input direction / 1: Output direction)
 *                          PortsSetting [4]: Port 4 (0: Input direction / 1: Output direction)
 *                          PortsSetting [5]: Port 5 (0: Input direction / 1: Output direction)
 *
 * @retval 0		No error. 
 */
int APCI1648_GetAllDigitalPortsSetting(struct pci_dev *pdev, unsigned char PortsSetting[6])
{
	/* Read setting register value on ports 0 - 3 */      
	APCI1648_PRIVDATA(pdev)->RegisterSetting.Register1.AllPorts = inl((GET_BAR0(pdev) + APCI1648_CMDREG_1));
	printk("Port[0...3]: %X\n",APCI1648_PRIVDATA(pdev)->RegisterSetting.Register1.AllPorts);
	/* Read setting register value on ports 4 - 7 */      
	APCI1648_PRIVDATA(pdev)->RegisterSetting.Register2.AllPorts = inl((GET_BAR0(pdev) + APCI1648_CMDREG_2));
	printk("Port[4...7]: %X\n",APCI1648_PRIVDATA(pdev)->RegisterSetting.Register1.AllPorts);
	/* Copy ports settings to return them to user */
	{
		int i;
		for (i=0; i<4; i++)
			PortsSetting [APCI1648_PORT0 + i] = APCI1648_PRIVDATA(pdev)->RegisterSetting.Register1.SinglePort[i] & 0x1;
		for (i=0; i<2; i++)         
			PortsSetting [APCI1648_PORT4 + i] = APCI1648_PRIVDATA(pdev)->RegisterSetting.Register2.SinglePort[i] & 0x1;
	}
	return 0;
}

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

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

/** Initialize one port of the APCI-1648.
 *
 * @param [in,out]	pdev	Pointer on the device structure.
 * @param 			arg		DigitalPortsArg_t
 *
 * @retval 0		No error. 
 * @retval -EINVAL	The port number or the initialization value is incorrect.
 */
int APCI1648_InitDigitalPort(struct pci_dev *pdev, struct DigitalPortsArg_t * arg)
{	  
	/* Legal port value is 0 to 5 (include) */
    if ( arg->Port > 5 )
    	return -EINVAL;

	/* Legal initializations values are 0 or 1 */
    if ( arg->Value > 1 )
    	return -EINVAL;  	         

	/* Test if the first or the second register has to be used */
    if ( arg->Port < 4 )
    {
         /* Store the new setting */
         APCI1648_PRIVDATA(pdev)->RegisterSetting.Register1.SinglePort[arg->Port] = arg->Value;
      
         /* Apply the setting */
         outl(APCI1648_PRIVDATA(pdev)->RegisterSetting.Register1.AllPorts, ((GET_BAR0(pdev) + APCI1648_CMDREG_1)));
    }
    else
    {
         /* Store the new setting */
         APCI1648_PRIVDATA(pdev)->RegisterSetting.Register2.SinglePort[arg->Port - 4] = arg->Value;
      
         /* Apply the setting */
         outl(APCI1648_PRIVDATA(pdev)->RegisterSetting.Register2.AllPorts, ((GET_BAR0(pdev) + APCI1648_CMDREG_2)));
    }

    return 0;
}

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

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

/** Get one port initialization of the APCI-1648.
 *
 * @param [in,out]	pdev	Pointer on the device structure.
 * @param 			arg		DigitalPortsArg_t
 *
 * @retval 0		No error. 
 * @retval -EINVAL	The port number is incorrect.
 */
int APCI1648_GetDigitalPortSetting(struct pci_dev *pdev, struct DigitalPortsArg_t * arg)
{      
	/* Legal port value is 0 to 5 (include) */
    if ( arg->Port > 5 )
    	return -EINVAL;  		         

	/* Test if the first or the second register has to be used */
    if ( arg->Port < 4 )
    {
    	/* Get the setting */
		APCI1648_PRIVDATA(pdev)->RegisterSetting.Register1.AllPorts = inl((GET_BAR0(pdev) + APCI1648_CMDREG_1));

        /* Store the setting */
        arg->Value = APCI1648_PRIVDATA(pdev)->RegisterSetting.Register1.SinglePort[arg->Port];      
	}
    else
    {
    	/* Get the setting */
        APCI1648_PRIVDATA(pdev)->RegisterSetting.Register2.AllPorts = inl((GET_BAR0(pdev) + APCI1648_CMDREG_2));

        /* Store the setting */
        arg->Value = APCI1648_PRIVDATA(pdev)->RegisterSetting.Register2.SinglePort[arg->Port];
	}

	return 0;
}
