/** @file ioctl.c
*
* @author Sylvain Nahas
*
* This module implements the apc2032_do_ioctl() function.
*/

/** @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 "apci2032-private.h"
#include <apci2032-kapi.h>

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

//------------------------------------------------------------------------------
int apci2032_do_ioctl(struct pci_dev * pdev, unsigned int cmd, unsigned long arg)
{
    
    switch(cmd)
    {    
   
	       case CMD_APCI2032_CheckAndGetPCISlotNumber: 
	       {
		            /* this command returns an area of size CONFIG_APCI2032_MAX_BOARD_NBR+1 filled with the PCI_SLOT() value and as last field the major number associated to the device - this is deprecated and should not be used anymore.
					The call returns 0 when memory is too low or the number of boards  */	
		   
				    char * b_SlotArray = kmalloc(CONFIG_APCI2032_MAX_BOARD_NBR+1 * sizeof(char),GFP_KERNEL);
				    if (!b_SlotArray)
				{
					/* bad param. Let output some information for debug */
				printk("%s: CMD_APCI2032_CheckAndGetPCISlotNumber: -EINVAL\n",__DRIVER_NAME);
					return 0;
				}
					    
				memset(b_SlotArray,0,CONFIG_APCI2032_MAX_BOARD_NBR+1 );
				
				printk("%s: the use of CMD_APCI2032_CheckAndGetPCISlotNumber is deprecated\n",__DRIVER_NAME);
				
				/* record the PCI_SLOT for each device from 0 to CONFIG_APCI2032_MAX_BOARD_NBR  */
				{	    	
				int i;
				for(i = 0; ( ( i < atomic_read(&apci2032_count) ) || ( i< CONFIG_APCI2032_MAX_BOARD_NBR ) ); i++)
				{
					
					struct pci_dev * dev =  apci2032_lookup_board_by_index(i);
					
					if (!dev) break;
					
					b_SlotArray[i] = PCI_SLOT(dev->devfn);
					
				}
				    }
				    b_SlotArray[CONFIG_APCI2032_MAX_BOARD_NBR] = apci2032_majornumber;
				
				    if ( copy_to_user ( (unsigned char __user * )arg, 
				                b_SlotArray,
					    sizeof (BYTE[CONFIG_APCI2032_MAX_BOARD_NBR+1])) )
				{
					/* bad address. Let output some information for debug */
				printk("%s: CMD_APCI2032_CheckAndGetPCISlotNumber: -EFAULT\n",__DRIVER_NAME);
					kfree(b_SlotArray);
					return 0;
				}
				{
					int retval;
					/* return the smaller value between CONFIG_APCI2032_MAX_BOARD_NBR and number of board - Note: apci2032_count is assumed to to be always consistent with the system PCI devices list */
				    retval = ( atomic_read(&apci2032_count) < CONFIG_APCI2032_MAX_BOARD_NBR ? atomic_read(&apci2032_count) : CONFIG_APCI2032_MAX_BOARD_NBR );
				    
				    kfree(b_SlotArray);
				    return retval;
				}
				break;
			}
	
			case CMD_APCI2032_SetBoardInformation:
		   
		        /* This function can be used to set parameters and */
				/* initialises the board to a definite status      */
		        return 0;
		        break;
	                                                 
	
	   		case CMD_APCI2032_GetHardwareInformation:
	        	{
			    	str_BaseInformations s_BaseInformations;
					s_BaseInformations.ui_BaseAddress[0] = GET_BAR0(pdev);
					s_BaseInformations.ui_BaseAddress[1] = GET_BAR1(pdev);
					s_BaseInformations.ui_BaseAddress[2] = 0;
					s_BaseInformations.ui_BaseAddress[3] = 0;
					s_BaseInformations.ui_BaseAddress[4] = 0;
					s_BaseInformations.b_Interrupt = pdev->irq;
					s_BaseInformations.b_SlotNumber = PCI_SLOT(pdev->devfn);
			        	/* Returns the informations of base address, IRQ */
						/* to the user */
			            if ( copy_to_user ((str_BaseInformations __user *)arg,&s_BaseInformations, sizeof(s_BaseInformations)) )
							return -EFAULT;
						
						return 0;
		    	}
	        	break; 
	        	
			case CMD_APCI2032_SetDigitalOutputMemoryOn:
				{
					APCI2032_PRIVDATA(pdev)->OutputMemoryStatus = 1;
				}
				break;
	                  
			case CMD_APCI2032_SetDigitalOutputMemoryOff:
				{
					APCI2032_PRIVDATA(pdev)->OutputMemoryStatus = 0;			
				}
				return 0;				
	        case CMD_APCI2032_Set1DigitalOutputOn:
	        	{
	        		if ( (arg == 0) || (arg > 32) )
	        		{
	        			return -EINVAL;
	        		} 
					return do_CMD_APCI2032_Set1DigitalOutputOn(VOID_TO_PCIDEV(pdev), ((uint32_t) arg) - 1 );
	        	}
	        case CMD_APCI2032_Set1DigitalOutputOff:
	        	{
	        		if ( (arg == 0) || (arg > 32) )
	        		{
	        			return -EINVAL;
	        		} 
					return do_CMD_APCI2032_Set1DigitalOutputOff(VOID_TO_PCIDEV(pdev),((uint32_t) arg) - 1 );
	        	}
	        
	        case CMD_APCI2032_Set32DigitalOutputsOn:
	        	return do_CMD_APCI2032_Set32DigitalOutputsOn(pdev,arg);
	        
	        case CMD_APCI2032_Get32DigitalOutputStatus:
				return do_CMD_APCI2032_Get32DigitalOutputStatus(pdev,(uint32_t __user *) arg);
	        
	        case CMD_APCI2032_InitDigitalOutputInterrupt:
	        	return 0;
	        
	        case CMD_APCI2032_GetDigitalOutputInterruptStatus:
	        	return do_CMD_APCI2032_GetDigitalOutputInterruptStatus(pdev,(unsigned int __user *) arg);
	        
	        case CMD_APCI2032_ReleaseDigitalOutputInterrupt:
	        	return do_CMD_APCI2032_ReleaseDigitalOutputInterrupt(pdev);

	        case CMD_APCI2032_EnableDisableDigitalOutputInterrupt:
	        	return do_CMD_APCI2032_EnableDisableDigitalOutputInterrupt(pdev, (int) arg);
	        		        	
			default:
				return -EINVAL;
	    }
	    
	    return 0; 
}
