/*
 * Source File : me1400.c                                                 
 * Destination : me1400.o                                                
 * Author      : GG (Guenter Gebhardt)                                 
 *                                                                     
 * Description : Meilhaus ME1400 driver modul for vxWorks               
 *               (Tornado 2.0.2)                                       
 *                                                                     
 * File History: Version   Date       Editor   Action                  
 *---------------------------------------------------------------------
 *               1.00.00   01.07.20   GG       first release           
 *                                                                     
 *---------------------------------------------------------------------
 *                                                                     
 * Description:                                                        
 *                                                                     
 *   Contains the entire driver module code for the                    
 *   Meilhaus ME1400 boards. However, the definitions and type          
 *   declarations are kept in the headerfile called me1400.h. 
 */

#include <ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <vxWorks.h>
#include <drv/pci/pciConfigLib.h>
#include <sysLib.h>
#include <ioLib.h>
#include <semLib.h>
#include <taskLib.h>
#include <intLib.h>
#include <sigLib.h>


/* Include-File for the Meilhaus ME1400 I/O board */
#include "me1400.h"

/* Number of the driver in the driver table */
static int me1400_drv_no;


/* Board specific data are kept global */
static me1400_info_type me1400_info_vec[6 * ME1400_MAX_DEVICES];


/* Number of boards, detected from the BIOS */
static int me1400_board_count;


/* Semaphores used by the driver */
static SEM_ID me1400_sem_id_open;   /* used in the open and close routine */


/* Prototypes */
static STATUS me1400_init_board(me1400_info_type *info, int pci_bus_no, int pci_dev_no, int pci_func_no);
static STATUS me1400_reset_board(me1400_info_type *info);

static int me1400_open(me1400_info_type *info, char *remainder, int flags, int mode);
static int me1400_close(me1400_info_type *info, char *remainder);
static STATUS me1400_ioctl(me1400_info_type *info, int service, void *arg);

static void __inline__ me1400OutByte(unsigned int port, unsigned char value);
static void __inline__ me1400OutWord(unsigned int port, unsigned short value);
static void __inline__ me1400OutLong(unsigned int port, unsigned int value);
static unsigned char __inline__ me1400InByte(unsigned int port);
static unsigned short __inline__ me1400InWord(unsigned int port);
static unsigned int __inline__ me1400InLong(unsigned int port);

static void me1400_isr(me1400_info_type *);

static STATUS me1400_get_board_version(me1400_version_enum_type *, me1400_info_type *);
static STATUS me1400_get_board_info(me1400_info_type *, me1400_info_type *);
static STATUS me1400_read_port(me1400_rw_port_type *, me1400_info_type *);
static STATUS me1400_write_port(me1400_rw_port_type *, me1400_info_type *);
static STATUS me1400_setup_port(me1400_setup_port_type *, me1400_info_type *);
static STATUS me1400_read_timer(me1400_rw_timer_type *, me1400_info_type *);
static STATUS me1400_write_timer(me1400_rw_timer_type *, me1400_info_type *);
static STATUS me1400_setup_timer(me1400_setup_timer_type *, me1400_info_type *);
static STATUS me1400_setup_timer_connect(unsigned char *, me1400_info_type *);
static STATUS me1400_irq_clockout(unsigned char *, me1400_info_type *);
static STATUS me1400_get_int_count(int *, me1400_info_type *);
static STATUS me1400_reg_task(int *arg, me1400_info_type *info);

static int file_handle = -1;
extern VOID user_isr();

extern void* memset(void*,int,size_t);
extern STATUS pciIntConnect(VOIDFUNCPTR*,VOIDFUNCPTR,int);
extern STATUS pciIntDisconnect2(VOIDFUNCPTR*,VOIDFUNCPTR,int);

/*
 * Routine:
 *   me1400_drv
 *
 * Description:
 *   With this function the driver is initialized. First it gets the pci
 *   hardware.
 *   Then it initializes the global data structure and the hardware with
 *   me1400_init_board().
 *   At last it installs the driver in the systems driver table with
 *   iosDrvInstall().
 *
 * Parameter list:
 *   Name           Type          Access             Description
 *--------------------------------------------------------------------------
 *
 * Result:
 *   On success the return value is OK else ERROR.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */
STATUS me1400_drv(void)
{
	STATUS result = OK;
	int board_count = 0;
	int sort_count = 0;
	int init_count = 0;
	int pci_bus_no;       /* PCI bus number */
	int pci_dev_no;       /* PCI device number */
	int pci_func_no;      /* PCI function number */

	CALL_PDEBUG("me1400_drv() is executed\n");

	/* Set the board context to 0 */
	memset(&me1400_info_vec, 0, sizeof(me1400_info_vec));


	/* init pci library */
	if (pciConfigLibInit(PCI_MECHANISM_1,
			PCI_CONFIG_ADDR,
			PCI_CONFIG_DATA,
			0) != OK) {
		ERROR_PDEBUG("me1400_drv():Cannot init pci library !\n");
		return ERROR;
	}

	/* Search for ME1400 boards */
	for (sort_count = 0; sort_count < ME1400_MAX_DEVICES; sort_count++, board_count++) {
		result = pciFindDevice(PCI_VENDOR_ID_MEILHAUS,
			PCI_DEVICE_ID_MEILHAUS_ME1400,
			sort_count,
			&pci_bus_no,
			&pci_dev_no,
			&pci_func_no);
		if (result == ERROR)
			break;

		PDEBUG("me1400_drv():Board %d:ME1400 found\n", board_count);
		me1400_info_vec[board_count].version = ME1400;
		me1400_info_vec[board_count].board_count = board_count;

		result = me1400_init_board(&me1400_info_vec[board_count],
			pci_bus_no,
			pci_dev_no,
			pci_func_no);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_drv():Board %d:Can't init board !\n", board_count);
			return ERROR;
		}
		else {
			init_count ++;
			PDEBUG("me1400_drv():init_count = %d\n", init_count);
		}
	}

	/* Search for ME1400A boards */
	for (sort_count = 0; sort_count < ME1400_MAX_DEVICES; sort_count++, board_count++) {
		result = pciFindDevice(PCI_VENDOR_ID_MEILHAUS,
			PCI_DEVICE_ID_MEILHAUS_ME1400A,
			sort_count,
			&pci_bus_no,
			&pci_dev_no,
			&pci_func_no);
		if (result == ERROR)
			break;

		PDEBUG("me1400_drv():Board %d:ME1400A found\n", board_count);
		me1400_info_vec[board_count].version = ME1400A;
		me1400_info_vec[board_count].board_count = board_count;

		result = me1400_init_board(&me1400_info_vec[board_count],
			pci_bus_no,
			pci_dev_no,
			pci_func_no);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_drv():Board %d:Can't init board !\n", board_count);
			return ERROR;
		}
		else {
			init_count ++;
			PDEBUG("me1400_drv():init_count = %d\n", init_count);
		}
	}

	/* Search for ME1400B boards */
	for (sort_count = 0; sort_count < ME1400_MAX_DEVICES; sort_count++, board_count++) {
		result = pciFindDevice(PCI_VENDOR_ID_MEILHAUS,
			PCI_DEVICE_ID_MEILHAUS_ME1400B,
			sort_count,
			&pci_bus_no,
			&pci_dev_no,
			&pci_func_no);
		if (result == ERROR)
			break;

		PDEBUG("me1400_drv():Board %d:ME1400B found\n", board_count);
		me1400_info_vec[board_count].version = ME1400B;
		me1400_info_vec[board_count].board_count = board_count;

		result = me1400_init_board(&me1400_info_vec[board_count],
			pci_bus_no,
			pci_dev_no,
			pci_func_no);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_drv():Board %d:Can't init board !\n", board_count);
			return ERROR;
		}
		else {
			init_count ++;
			PDEBUG("me1400_drv():init_count = %d\n", init_count);
		}
	}

	/* Search for ME1400E boards */
	for (sort_count = 0; sort_count < ME1400_MAX_DEVICES; sort_count++, board_count++) {
		result = pciFindDevice(PCI_VENDOR_ID_MEILHAUS,
			PCI_DEVICE_ID_MEILHAUS_ME1400E,
			sort_count,
			&pci_bus_no,
			&pci_dev_no,
			&pci_func_no);
		if (result == ERROR)
			break;

		PDEBUG("me1400_drv():Board %d:ME1400E found\n", board_count);
		me1400_info_vec[board_count].version = ME1400E;
		me1400_info_vec[board_count].board_count = board_count;

		result = me1400_init_board(&me1400_info_vec[board_count],
			pci_bus_no,
			pci_dev_no,
			pci_func_no);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_drv():Board %d:Can't init board !\n", board_count);
			return ERROR;
		}
		else {
			init_count ++;
			PDEBUG("me1400_drv():init_count = %d\n", init_count);
		}
	}

	/* Search for ME1400EA boards */
	for (sort_count = 0; sort_count < ME1400_MAX_DEVICES; sort_count++, board_count++) {
		result = pciFindDevice(PCI_VENDOR_ID_MEILHAUS,
			PCI_DEVICE_ID_MEILHAUS_ME1400EA,
			sort_count,
			&pci_bus_no,
			&pci_dev_no,
			&pci_func_no);
		if (result == ERROR)
			break;

		PDEBUG("me1400_drv():Board %d:ME1400EA found\n", board_count);
		me1400_info_vec[board_count].version = ME1400EA;
		me1400_info_vec[board_count].board_count = board_count;

		result = me1400_init_board(&me1400_info_vec[board_count],
			pci_bus_no,
			pci_dev_no,
			pci_func_no);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_drv():Board %d:Can't init board !\n", board_count);
			return ERROR;
		}
		else {
			init_count ++;
			PDEBUG("me1400_drv():init_count = %d\n", init_count);
		}
	}

	/* Search for ME1400EB boards */
	for (sort_count = 0; sort_count < ME1400_MAX_DEVICES; sort_count++, board_count++) {
		result = pciFindDevice(PCI_VENDOR_ID_MEILHAUS,
			PCI_DEVICE_ID_MEILHAUS_ME1400EB,
			sort_count,
			&pci_bus_no,
			&pci_dev_no,
			&pci_func_no);
		if (result == ERROR)
			break;

		PDEBUG("me1400_drv():Board %d:me1400 found\n", board_count);
		me1400_info_vec[board_count].version = ME1400EB;
		me1400_info_vec[board_count].board_count = board_count;

		result = me1400_init_board(&me1400_info_vec[board_count],
			pci_bus_no,
			pci_dev_no,
			pci_func_no);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_drv():Board %d:Can't init board !\n", board_count);
			return ERROR;
		}
		else {
			init_count ++;
			PDEBUG("me1400_drv():init_count = %d\n", init_count);
		}
	}

	if (board_count == 0) {
		ERROR_PDEBUG("me1400_drv():No PCI-Devices found\n");
		errno = ENODEV;
		return ERROR;
	}

	me1400_board_count = board_count;
	PDEBUG("me1400_drv(): %d Boards found\n", me1400_board_count);

	/* init semaphores */
	me1400_sem_id_open = semBCreate(SEM_Q_PRIORITY, SEM_FULL);
	if (me1400_sem_id_open == NULL) {
		ERROR_PDEBUG("me1400_drv():Cannot get me1400_sem_id_open\n");
		return ERROR;
	}

	/* install the driver in the driver table */
	me1400_drv_no = iosDrvInstall(0, 0, me1400_open, me1400_close, 0, 0, me1400_ioctl);
	if (me1400_drv_no == ERROR) {
		ERROR_PDEBUG("me1400_drv():Can't install driver in driver table !\n");
		goto me1400_DRV_ERROR_0;
	}
	else {
		PDEBUG("me1400_drv():Driver number = %d\n", me1400_drv_no);
	}

	CALL_PDEBUG("me1400_drv() is leaved\n");
	return OK;

me1400_DRV_ERROR_0:
	/* remove the semaphore */
	semDelete(me1400_sem_id_open);

	return ERROR;
}



/*
 * Routine:
 *   me1400_init_board
 *
 * Description:
 *   This function initializes the detected boards. It gets the
 *   PLX-Register,the ME1400-Register and the interrupt number.
 *   All that will be saved in the global
 *   info_vec. In the end it executes the subroutine
 *   me1400_reset_board to download default values into the board.
 *
 * Parameter list:
 *   Name           Type                 Access    	Description
 *--------------------------------------------------------------------------
 *   *info          me1400_info_type     write	 		Global board context
 *   pci_bus_no     int                  read   		PCI bus number
 *   pci_dev_no     int                  read  	 		PCI device number
 *   pci_func_no    int                  read   		PCI function number
 *
 * Result:
 *   On success the return value is OK, else ERROR
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_init_board(me1400_info_type *info, int pci_bus_no, int pci_dev_no, int pci_func_no)
{
	STATUS result = OK;
	UINT32 plx_regbase_tmp = 0;
	UINT32 me1400_regbase_tmp = 0;
	UINT32 swap_regbase_tmp = 0;
	UINT32 regbase_tmp = 0;
	UINT32 size = 0;

	CALL_PDEBUG("me1400_init_board() is executed\n");

	result = pciConfigInLong(pci_bus_no,
		pci_dev_no,
		pci_func_no,
		PCI_CFG_BASE_ADDRESS_1,
		&plx_regbase_tmp);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_init_board():Board %d:Can't read base 1\n", info->board_count);
		return ERROR;
	}

	result = pciConfigInLong(pci_bus_no,
		pci_dev_no,
		pci_func_no,
		PCI_CFG_BASE_ADDRESS_5,
		&swap_regbase_tmp);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_init_board():Board %d:Can't read base 5\n", info->board_count);
		return ERROR;
	}

	if (!swap_regbase_tmp) {
		ERROR_PDEBUG("me1400_init_board():Board %d:Swap not present\n", info->board_count);
	}

	/* PLX bug workaround. If bit 7 is set in the plx_regbase, the registers */
	/* maybe not readable.                                                   */
	if (plx_regbase_tmp & 0x0080) {
		ERROR_PDEBUG("me1400_init_board():Board %d:PLX-BUG detected\n", info->board_count);

		if (PLX_WORKAROUND_ENABLE) {
			if (swap_regbase_tmp) {
				regbase_tmp = plx_regbase_tmp;
				plx_regbase_tmp = swap_regbase_tmp;
				swap_regbase_tmp = regbase_tmp;

				result = pciConfigOutLong(pci_bus_no,
					pci_dev_no,
					pci_func_no,
					PCI_CFG_BASE_ADDRESS_1,
					plx_regbase_tmp);
				if (result == ERROR) {
					ERROR_PDEBUG("me1400_init_board():Board %d:Can't write base 1\n", info->board_count);
					return ERROR;
				}

				result = pciConfigOutLong(pci_bus_no,
					pci_dev_no,
					pci_func_no,
					PCI_CFG_BASE_ADDRESS_5,
					swap_regbase_tmp);
				if (result == ERROR) {
					ERROR_PDEBUG("me1400_init_board():Board %d:Can't write base 5\n", info->board_count);
					return ERROR;
				}
			}
			else {
				plx_regbase_tmp = plx_regbase_tmp - 0x80;
				result = pciConfigOutLong(pci_bus_no,
					pci_dev_no,
					pci_func_no,
					PCI_CFG_BASE_ADDRESS_1,
					plx_regbase_tmp);
				if (result == ERROR) {
					ERROR_PDEBUG("me1400_init_board():Board %d:Can't write base 1\n", info->board_count);
					return ERROR;
				}
			}
		}
	}

	if (plx_regbase_tmp & PCI_BASE_IO) {
		result = pciConfigOutLong(pci_bus_no,
			pci_dev_no,
			pci_func_no,
			PCI_CFG_BASE_ADDRESS_1,
			~size);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_init_board():Board %d:Can't write base 1\n", info->board_count);
			return ERROR;
		}

		result = pciConfigInLong(pci_bus_no,
			pci_dev_no,
			pci_func_no,
			PCI_CFG_BASE_ADDRESS_1,
			&size);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_init_board():Board %d:Can't read base 1\n", info->board_count);
			return ERROR;
		}

		size &= PCI_IOBASE_MASK;
		size = (~size) + 1;
		PDEBUG("me1400_init_board():Board %d:PLX space is 0x%x\n", info->board_count, size);
		info->plx_regbase_size = size;

		result = pciConfigOutLong(pci_bus_no,
			pci_dev_no,
			pci_func_no,
			PCI_CFG_BASE_ADDRESS_1,
			plx_regbase_tmp);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_init_board():Board %d:Can't write base 1\n", info->board_count);
			return ERROR;
		}
		size = 0;

		info->plx_regbase = plx_regbase_tmp & PCI_IOBASE_MASK;
		PDEBUG("me1400_init_board():Board %d:PLX IO at 0x%08X\n",
			info->board_count,
			info->plx_regbase);
	}
	else {
		ERROR_PDEBUG("me1400_init_board():Board %d:PLX regbase is not an IO space\n", info->board_count);
		errno = EIO;
		return ERROR;
	}

	result = pciConfigInLong(pci_bus_no,
		pci_dev_no,
		pci_func_no,
		PCI_CFG_BASE_ADDRESS_2,
		&me1400_regbase_tmp);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_init_board():Board %d:Can't read base 2\n", info->board_count);
		return ERROR;
	}

	if (me1400_regbase_tmp & PCI_BASE_IO) {
		result = pciConfigOutLong(pci_bus_no,
			pci_dev_no,
			pci_func_no,
			PCI_CFG_BASE_ADDRESS_2,
			~size);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_init_board():Board %d:Can't write base 2\n", info->board_count);
			return ERROR;
		}

		result = pciConfigInLong(pci_bus_no,
			pci_dev_no,
			pci_func_no,
			PCI_CFG_BASE_ADDRESS_2,
			&size);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_init_board():Board %d:Can't read base 2\n", info->board_count);
			return ERROR;
		}

		size &= PCI_IOBASE_MASK;
		size = (~size) + 1;
		PDEBUG("me1400_init_board():Board %d:me1400 space is 0x%X\n", info->board_count, size);
		info->me1400_regbase_size = size;

		result = pciConfigOutLong(pci_bus_no,
			pci_dev_no,
			pci_func_no,
			PCI_CFG_BASE_ADDRESS_2,
			me1400_regbase_tmp);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_init_board():Board %d:Can't write base 2\n", info->board_count);
			return ERROR;
		}
		size = 0;

		info->me1400_regbase = me1400_regbase_tmp &= PCI_IOBASE_MASK;
		PDEBUG("me1400_init_board():Board %d:me1400 IO at 0x%08X\n",
			info->board_count,
			info->me1400_regbase);
	}
	else {
		ERROR_PDEBUG("me1400_init_board():Board %d:me1400 regbase is not an IO space \n", info->board_count);
		errno = EIO;
		return ERROR;
	}
	result = pciConfigInLong(pci_bus_no,
		pci_dev_no,
		pci_func_no,
		PCI_CFG_SUB_VENDER_ID,
		&info->serial_no);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_init_board():Board %d:Can't get serial no\n", info->board_count);
		return ERROR;
	}
	PDEBUG("me1400_init_board():Board %d:serial_no = 0x%08X\n",
		info->board_count,
		info->serial_no);

	result = pciConfigInByte(pci_bus_no,
		pci_dev_no,
		pci_func_no,
		PCI_CFG_REVISION,
		&info->hw_revision);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_init_board():Board %d:Can't get hw revision\n", info->board_count);
		return ERROR;
	}
	PDEBUG("me1400_init_board():Board %d:hw_revision = 0x%02X\n",
		info->board_count,
		info->hw_revision);

	info->vendor_id = 0x1402;
	PDEBUG("me1400_init_board():Board %d:vendor_id = 0x%04X\n",
		info->board_count,
		info->vendor_id);

	result = pciConfigInWord(pci_bus_no,
		pci_dev_no,
		pci_func_no,
		PCI_CFG_DEVICE_ID,
		&info->device_id);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_init_board():Board %d:Can't get device id\n", info->board_count);
		return ERROR;
	}
	PDEBUG("me1400_init_board():Board %d:device_id = 0x%04X\n",
		info->board_count,
		info->device_id);

	info->pci_bus_no = pci_bus_no;
	PDEBUG("me1400_init_board():Board %d:pci_bus_no = 0x%02X\n",
		info->board_count,
		info->pci_bus_no);

	info->pci_dev_no = pci_dev_no;
	PDEBUG("me1400_init_board():Board %d:pci_dev_no = 0x%02X\n",
		info->board_count,
		info->pci_dev_no);

	info->pci_func_no = pci_func_no;
	PDEBUG("me1400_init_board():Board %d:pci_func_no = 0x%02X\n",
		info->board_count,
		info->pci_func_no);

	result = pciConfigInByte(pci_bus_no,
		pci_dev_no,
		pci_func_no,
		PCI_CFG_BRG_INT_LINE,
		&info->int_line);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_init_board():Board %d:Can't get int_line\n", info->board_count);
		return ERROR;
	}
	PDEBUG("me1400_init_board():Board %d:int_line = %d\n",
		info->board_count,
		info->int_line);

	result = pciConfigInByte(pci_bus_no,
		pci_dev_no,
		pci_func_no,
		PCI_CFG_BRG_INT_PIN,
		&info->int_pin);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_init_board():Board %d:Can't get int_pin\n", info->board_count);
		return ERROR;
	}
	PDEBUG("me1400_init_board():Board %d:int_pin = %d\n",
		info->board_count,
		info->int_pin);

	info->board_in_use = 0;
	info->int_count = 0;

	result = me1400_reset_board(info);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_init_module:Board %d:Can't reset board\n", info->board_count);
		return ERROR;
	}

	CALL_PDEBUG("me1400_init_board() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_reset_board
 *
 * Description:
 *   This function resets the board refered with board_count. It sets the
 *   ports to input mode, connects all timer pins with the subd,
 *   sets the clock frequency to 1MHz, sets all timer values to 0xffff,
 *   sets the timer mode to binary, MSB/LSB, selects the interrupt mode
 *   and resets the interrupt.
 *   In addition it enables the interrupt logic of the PLX.
 *
 * Parameter list:
 *   Name       Type          			Access    Description
 *--------------------------------------------------------------------------
 *   *info      me1400_info_struct 	read    	Pointer to device specific
 *                                         		global data structure.
 *
 * Result:
 *   On success the return value is OK else ERROR
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */
static STATUS me1400_reset_board(me1400_info_type *info)
{
	unsigned char mode;

	CALL_PDEBUG("me1400_reset_board() is executed\n");

	/* Configure all ports as input */
	me1400OutByte(info->me1400_regbase + ME1400_PORT_A_CTRL, PORT_MODE_EEE);
	me1400OutByte(info->me1400_regbase + ME1400_PORT_B_CTRL, PORT_MODE_EEE);

	/* Connect all timer pins with subd, 1MHz clock */
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_CONNECT, 0x0);

	/* Interrupt mode, reset interrupt */
	me1400OutByte(info->me1400_regbase + ME1400_IRQ_CLOCKOUT, 0x0);

	/* Configure all timer as binary, LSB/MSB, mode 0 */
	mode = (TIMER_CTRL_SC0 | TIMER_CTRL_LM | TIMER_CTRL_M0 | TIMER_CTRL_BIN);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_CTRL, mode);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_1, 0xff);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_1, 0xff);

	mode = (TIMER_CTRL_SC1 | TIMER_CTRL_LM | TIMER_CTRL_M0 | TIMER_CTRL_BIN);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_CTRL, mode);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_2, 0xff);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_2, 0xff);

	mode = (TIMER_CTRL_SC2 | TIMER_CTRL_LM | TIMER_CTRL_M0 | TIMER_CTRL_BIN);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_CTRL, mode);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_3, 0xff);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_3, 0xff);

	mode = (TIMER_CTRL_SC0 | TIMER_CTRL_LM | TIMER_CTRL_M0 | TIMER_CTRL_BIN);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_CTRL, mode);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_1, 0xff);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_1, 0xff);

	mode = (TIMER_CTRL_SC1 | TIMER_CTRL_LM | TIMER_CTRL_M0 | TIMER_CTRL_BIN);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_CTRL, mode);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_2, 0xff);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_2, 0xff);

	mode = (TIMER_CTRL_SC2 | TIMER_CTRL_LM | TIMER_CTRL_M0 | TIMER_CTRL_BIN);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_CTRL, mode);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_3, 0xff);
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_3, 0xff);

	mode = (LOCAL_INT1_EN | LOCAL_INT1_POL | PCI_INT_EN);
	PDEBUG("me1400_reset_board(): plx_mode = %X\n", mode);
	me1400OutByte(info->plx_regbase + PLX_INTCSR, mode);

	CALL_PDEBUG("me1400_reset_board() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_create_dev
 *
 * Description:
 *   With this function you can add a device to the device list.
 *
 * Parameter list:
 *   Name       Type            Access    Description
 *--------------------------------------------------------------------------
 *   name       char *          read      Defines the name of the device.
 *                                        This name is added in the device
 *                                        list to indicate the device.
 *   index      int                       Specifies the index of the board.
 *
 * Result:
 *   On success the return value is OK else ERROR
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */
STATUS me1400_create_dev(char *name, int index, INT_HANDLER_PROTOTYPE funct)
{
	STATUS result;
	me1400_info_type *info;
	info = &me1400_info_vec[index];

	CALL_PDEBUG("me1400_create_dev() is executed\n");

	if (index >= me1400_board_count) {
		errno = EINVAL;
		ERROR_PDEBUG("me1400_create_dev(): No board available !\n");
		return ERROR;
	}

	PDEBUG("me1400_create_dev():Add %s with info pointer 0x%X and driver number %d\n",
		name, (int) info, me1400_drv_no);

	/* add device to device list */
	result = iosDevAdd((DEV_HDR *)info, name, me1400_drv_no);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_create_dev():Cannot add device to device list\n");
		return ERROR;
	}
	info->user_isr = funct;
	CALL_PDEBUG("me1400_create_dev() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_delete_dev
 *
 * Description:
 *   With this function you can remove the device from the device list.
 *
 * Parameter list:
 *   Name       Type            Access    Description
 *--------------------------------------------------------------------------
 *   char       *name           read      name of device in device list.
 *   index      int             read      Specifies the index of the board.
 *                                        First me1400 then me2000.
 *
 * Result:
 *   On success the return value is OK else ERROR
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */

STATUS me1400_delete_dev(char *name, int index)
{
	me1400_info_type *info;
	info = &me1400_info_vec[index];

	CALL_PDEBUG("me1400_delete_dev() is executed\n");

	/* remove device from device list */
	iosDevDelete((DEV_HDR *)info);
	CALL_PDEBUG("me1400_delete_dev() is leaved\n");

	return OK;
}



/*
 * Routine:
 *   me1400_delete_drv
 *
 * Description:
 *   With this function you can remove the driver from the driver table.
 *
 * Parameter list:
 *   Name       Type            Access    Description
 *--------------------------------------------------------------------------
 *
 * Result:
 *   On success the return value is OK else ERROR
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */

STATUS me1400_delete_drv(void)
{
	STATUS result;

	CALL_PDEBUG("me1400_delete_drv() is executed\n");

	/* remove semaphores allocated in me1400_drv() */
	result = semDelete(me1400_sem_id_open);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_delete():Cannot delete semaphore\n");
		goto ME1400_DELETE_DRV_ERROR_0;
	}

	/* remove driver from driver table */
	result = iosDrvRemove(me1400_drv_no, TRUE);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_delete():Cannot remove Driver");
		return ERROR;
	}

	CALL_PDEBUG("me1400_delete_drv() is leaved\n");
	return OK;

ME1400_DELETE_DRV_ERROR_0:
	result = iosDrvRemove(me1400_drv_no, TRUE);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_delete():Cannot remove Driver");
	}

	return ERROR;
}



/*
 * Routine:
 *   me1400_open
 *
 * Description:
 *   Function, which is executed, when a userprogramm makes the syscall
 *   open.
 *   It installs the drivers interrupt service routine me1400_isr in the
 *   system interrupt vector table and enables the interrupt on the PIC.
 *   It remarks the board as used in the global data structure.
 *
 * Parameter list:
 *   Name       Type             Access    Description
 *--------------------------------------------------------------------------
 *   *info      me1400_info_type read      pointer to device specific global
 *                                         data structure
 *   remainder  int              read      rest of device name in open.
 *   flags      int              read      not used
 *   mode       int              read      not used
 *
 * Result:
 *   On success the return value is the address of global board info
 *   else ERROR.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */
static int me1400_open(me1400_info_type* info, char *remainder, int flags, int mode)
{
	STATUS result;

	CALL_PDEBUG("me1400_open() is executed\n");

	semTake(me1400_sem_id_open, WAIT_FOREVER);
	if (info->board_in_use) {
		ERROR_PDEBUG("me1400_open():Board %d already in use !\n", info->board_count);
		errno = EBUSY;
		semGive(me1400_sem_id_open);
		return ERROR;
	}
	info->board_in_use = 1;
	semGive(me1400_sem_id_open);

	/*if ((info->version != ME1400) && (info->version != ME1400E)) {*/
		PDEBUG("me1400_open():Connect int_line %d to int_vec %d\n",
			info->int_line,
			info->int_line + 0x20);

		result = pciIntConnect(INUM_TO_IVEC(info->int_line + 0x20),
			me1400_isr,
			(int) info);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_open():Cannot connect to interrupt line\n");
			goto ME1400_OPEN_ERROR_1;
		}

		PDEBUG("me1400_open():Enable interrupt %d\n", info->int_line);
		result = sysIntEnablePIC(info->int_line);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_open():Cannot enable interrupt line\n");
			goto ME1400_OPEN_ERROR_0;
		}
	/*}*/

	CALL_PDEBUG("me1400_open() is leaved\n");
	return ((int) info);

ME1400_OPEN_ERROR_0:
	PDEBUG("me1400_open():Disconnect int_line %d from int_vec %d\n",
		info->int_line, info->int_line + 0x20);
	result = pciIntDisconnect2(INUM_TO_IVEC(info->int_line + 0x20), me1400_isr, (int)info);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_open():Cannot disconnect interrupt line\n");
	}

ME1400_OPEN_ERROR_1:
	semTake(me1400_sem_id_open, WAIT_FOREVER);
	info->board_in_use = 0;
	semGive(me1400_sem_id_open);

	return ERROR;
}



/*
 * Routine:
 *   me1400_close
 *
 * Description:
 *   Function, which is executed, when the userprogramm makes the syscall
 *   close. It disconnects the me1400_isr and disables the interrupt on the
 *   PIC. It resets the board and marks the board as unused in the
 *   global device data structure.
 *
 * Parameter list:
 *   Name       Type             Access    Description
 *--------------------------------------------------------------------------
 *   *info      me1400_info_struct read    pointer to device specific
 *                                         global data structure
 *   remainder  int              read      rest of device name in open.
 *
 * Result:
 *   On success the return value is the address of global board context
 *   info else ERROR.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */
static int me1400_close(me1400_info_type *info, char *remainder)
{
	STATUS result;

	CALL_PDEBUG("me1400_close() is executed\n");

	/*if ((info->version != ME1400) && (info->version != ME1400E)) {*/
		PDEBUG("me1400_close():Disconnect int_line %d from int_vec %d\n",
			info->int_line, info->int_line + 0x20);

		result = pciIntDisconnect2(INUM_TO_IVEC(info->int_line + 0x20), me1400_isr, (int)info);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_close():Cannot disconnect interrupt line\n");
			goto ME1400_CLOSE_ERROR_0;
		}

		PDEBUG("me1400_close():Disable interrupt %d\n", info->int_line);
		result = sysIntDisablePIC(info->int_line);
		if (result == ERROR) {
			ERROR_PDEBUG("me1400_close():Cannot disable interrupt line\n");
			goto ME1400_CLOSE_ERROR_1;
		}
	/*}*/

	result = me1400_reset_board(info);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_close():Board %d:Can't reset", info->board_count);
		goto ME1400_CLOSE_ERROR_2;
	}

	semTake(me1400_sem_id_open, WAIT_FOREVER);
	info->board_in_use = 0;
	semGive(me1400_sem_id_open);
	CALL_PDEBUG("me1400_close() is leaved\n");
	return ((int) info);

ME1400_CLOSE_ERROR_0:
	PDEBUG("me1400_close():Disable interrupt %d\n", info->int_line);
	result = sysIntDisablePIC(info->int_line);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_close():Cannot disable interrupt line\n");
	}

ME1400_CLOSE_ERROR_1:
	result = me1400_reset_board(info);
	if (result == ERROR) {
		ERROR_PDEBUG("me1400_close():Board %d:Can't reset", info->board_count);
	}

ME1400_CLOSE_ERROR_2:
	semTake(me1400_sem_id_open, WAIT_FOREVER);
	info->board_in_use = 0;
	semGive(me1400_sem_id_open);

	return ERROR;
}



/*
 * Routine:
 *   me1400_ioctl
 *
 * Description:
 *   Function, which provides the functionality of the ME1400 board. This
 *   function is executed, when a user program executes the systemcall
 *   ioctl. It checks if the service requested is valid and calls the
 *   appropriate routine to handle the request.
 *
 * Parameter list:
 *   Name       	Type            	Access    Description
 *--------------------------------------------------------------------------
 *   *info   		me1400_info_type	r		  Pointer to device specific
 *                                   			  global data structure.
 *   service    	unsigned int    	r		  Requested service.
 *   arg        	unsigned long   	r/w       Address of the structure with
 *                                        		  user data and parameters.
 *
 * Result:
 *   On success the return value is OK, else is failure.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_ioctl(me1400_info_type *info, int service, void *arg)
{

	CALL_PDEBUG("me1400_ioctl() is executed\n");

	switch (service) {
		case ME1400_GET_BOARD_VERSION:
			return me1400_get_board_version((me1400_version_enum_type *)arg, info);
		case ME1400_GET_BOARD_INFO:
			return me1400_get_board_info((me1400_info_type *)arg, info);
		case ME1400_READ_PORT:
			return me1400_read_port((me1400_rw_port_type *)arg, info);
		case ME1400_WRITE_PORT:
			return me1400_write_port((me1400_rw_port_type *)arg, info);
		case ME1400_SETUP_PORT:
			return me1400_setup_port((me1400_setup_port_type *)arg, info);
		case ME1400_READ_TIMER:
			return me1400_read_timer((me1400_rw_timer_type *)arg, info);
		case ME1400_WRITE_TIMER:
			return me1400_write_timer((me1400_rw_timer_type *)arg, info);
		case ME1400_SETUP_TIMER:
			return me1400_setup_timer((me1400_setup_timer_type *)arg, info);
		case ME1400_SETUP_TIMER_CONNECT:
			return me1400_setup_timer_connect((unsigned char *)arg, info);
		case ME1400_SETUP_IRQ_CLOCKOUT:
			return me1400_irq_clockout(arg, info);
		case ME1400_GET_INT_COUNT:
			return me1400_get_int_count((int *)arg, info);
		case ME1400_REG_TASK:
			return me1400_reg_task((int *) arg, info);
		default:
			ERROR_PDEBUG("me1400_ioctl():Invalid ioctl number\n");
			errno = EINVAL;
			return ERROR;
	}

	CALL_PDEBUG("me1400_ioctl() is leaved\n");
	return OK;
}



/*-----------------------------general purpose functions-----------------------------*/
/*
 * Routine:
 *   me1400OutByte
 *
 * Parameter list:
 *   Name       Type               Access    Description
 *--------------------------------------------------------------------------
 *   port       unsigned int       r         Port address.
 *   value      unsigned char      r         Byte you want to write.
 *
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */
static void __inline__ me1400OutByte(unsigned int port, unsigned char value)
{
	PORT_PDEBUG("--> 0x%02X port 0x%04X\n", value, port);
	sysOutByte(port, value);
}


/*
 * Routine:
 *   me1400OutWord
 *
 * Parameter list:
 *   Name       Type               Access    Description
 *--------------------------------------------------------------------------
 *   port       unsigned int       r         Port address.
 *   value      unsigned short     r         Word you want to write.
 *
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */
static void __inline__ me1400OutWord(unsigned int port, unsigned short value)
{
	PORT_PDEBUG("--> 0x%04X port 0x%04X\n", value, port);
	sysOutWord(port, value);
}



/*
 * Routine:
 *   me1400OutLong
 *
 * Parameter list:
 *   Name       Type               Access    Description
 *--------------------------------------------------------------------------
 *   port       unsigned int       r         Port address.
 *   value      unsigned int       r         Integer you want to write.
 *
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */
static void __inline__ me1400OutLong(unsigned int port, unsigned int value)
{
	PORT_PDEBUG("--> 0x%08X port 0x%04X\n", value, port);
	sysOutLong(port, value);
}



/*
 * Routine:
 *   me1400InByte
 *
 * Parameter list:
 *   Name       Type               Access    Description
 *--------------------------------------------------------------------------
 *   port       unsigned int       r         Port address.
 *
 * Result:
 *  Value from the port.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */
static unsigned char __inline__ me1400InByte(unsigned int port)
{
	unsigned char value;
	value = sysInByte(port);
	PORT_PDEBUG("<-- 0x%02X port 0x%04X\n", value, port);
	return value;
}



/*
 * Routine:
 *   me1400InWord
 *
 * Parameter list:
 *   Name       Type               Access    Description
 *--------------------------------------------------------------------------
 *   port       unsigned int       r         Port address.
 *
 * Result:
 *  Value from the port.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */
static unsigned short __inline__ me1400InWord(unsigned int port)
{
	unsigned short value;
	value = sysInWord(port);
	PORT_PDEBUG("<-- 0x%04X port 0x%04X\n", value, port);
	return value;
}



/*
 * Routine:
 *   me1400InLong
 *
 * Parameter list:
 *   Name       Type               Access    Description
 *--------------------------------------------------------------------------
 *   port       unsigned int       r         Port address.
 *
 * Result:
 *  Value from the port.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modification:
 */
static unsigned int __inline__ me1400InLong(unsigned int port)
{
	unsigned int value;
	value = sysInLong(port);
	PORT_PDEBUG("<-- 0x%08X port 0x%04X\n", value, port);
	return value;
}



/*----------------------- board specific functions -------------------------*/
/*
 * Routine:
 *   me1400_get_board_version
 * Description:
 *   This function is called by me1400_ioctl, in order to get the version
 *   of the ME1400 board. This could be a me1400, a me1400_a or a me1400_b.
 *
 * Parameter list:
 *   Name    Type                      	Access  Description
 *--------------------------------------------------------------------------
 *   arg     me1400_version_enum_type* 	w       Carries the value to user.
 *   *info   me1400_info_type		 	r		Pointer to device specific
 *                                         		global data structure.
 *
 * Result:
 *   On success the return value is 0, else is failure
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_get_board_version(me1400_version_enum_type *arg, me1400_info_type *info)
{

	CALL_PDEBUG("me1400_get_board_version() is executed\n");

	*arg = info->version;

	CALL_PDEBUG("me1400_get_board_version() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_get_board_info
 *
 * Description:
 *   This function is called by me1400_ioctl, in order to get the global
 *   variables for a specific board from the info_vec.
 * Parameter list:
 *   Name    Type                      	Access  Description
 *--------------------------------------------------------------------------
 *   arg     me1400_info_type *        	w       Carries the value to user.
 *   *info   me1400_info_type		        	Pointer to device specific
 *                                         		global data structure.
 *
 * Result:
 *   On success the return value is OK, else is failure.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_get_board_info(me1400_info_type *arg, me1400_info_type *info)
{
	CALL_PDEBUG("me1400_get_board_info() is executed\n");
	*arg = *info;
	CALL_PDEBUG("me1400_get_board_info() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_read_port
 *
 * Description:
 *   This function is called by me1400_ioctl, in order to read a port
 *   of the ME1400 board.
 *
 * Parameter list:
 *   Name    Type                      	Access  Description
 *--------------------------------------------------------------------------
 *   *arg    me1400_rw_port_type	   	r/w     specifies port and
 *                                             	carries the value to user
 *   *info   me1400_info_type		 	r		Pointer to device specific
 *                                         		global data structure.
 *
 * Result:
 *   On success the return value is OK, else is failure.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_read_port(me1400_rw_port_type *arg, me1400_info_type *info)
{
	CALL_PDEBUG("me1400_read_port() is executed\n");

	switch (arg->port) {
		case PORT_A_A:
			arg->value = me1400InByte(info->me1400_regbase + ME1400_PORT_A_A);
			break;
		case PORT_A_B:
			arg->value = me1400InByte(info->me1400_regbase + ME1400_PORT_A_B);
			break;
		case PORT_A_C:
			arg->value = me1400InByte(info->me1400_regbase + ME1400_PORT_A_C);
			break;
		case PORT_B_A:
			arg->value = me1400InByte(info->me1400_regbase + ME1400_PORT_B_A);
			break;
		case PORT_B_B:
			arg->value = me1400InByte(info->me1400_regbase + ME1400_PORT_B_B);
			break;
		case PORT_B_C:
			arg->value = me1400InByte(info->me1400_regbase + ME1400_PORT_B_C);
			break;
		default:
			ERROR_PDEBUG("me1400_read_port():Invalid port\n");
			errno = EINVAL;
			return ERROR;
	}

	PDEBUG("me1400_read_port() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_write_port
 *
 * Description:
 *
 *   This function is called by the me1400_ioctl, in order to write a value
 *   to a port.
 *
 * Parameter list:
 *   Name    Type                      	Access  Description
 *--------------------------------------------------------------------------
 *   *arg    me1400_rw_port_type	  	r/w     specifies port and
 *                                             	carries the value from user
 *   *info   me1400_info_type		 	r		Pointer to device specific
 *                                         		global data structure.
 * Result:
 *   On success the return value is 0, else is failure
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_write_port(me1400_rw_port_type *arg, me1400_info_type *info)
{

	CALL_PDEBUG("me1400_write_port() is executed\n");

	switch (arg->port) {
		case PORT_A_A:
			me1400OutByte(info->me1400_regbase + ME1400_PORT_A_A, arg->value);
			break;
		case PORT_A_B:
			me1400OutByte(info->me1400_regbase + ME1400_PORT_A_B, arg->value);
			break;
		case PORT_A_C:
			me1400OutByte(info->me1400_regbase + ME1400_PORT_A_C, arg->value);
			break;
		case PORT_B_A:
			me1400OutByte(info->me1400_regbase + ME1400_PORT_B_A, arg->value);
			break;
		case PORT_B_B:
			me1400OutByte(info->me1400_regbase + ME1400_PORT_B_B, arg->value);
			break;
		case PORT_B_C:
			me1400OutByte(info->me1400_regbase + ME1400_PORT_B_C, arg->value);
			break;
		default:
			ERROR_PDEBUG("me1400_write_port():Invalid port\n");
			errno = EINVAL;
			return ERROR;
	}

	CALL_PDEBUG("me1400_write_port() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_setup_port
 *
 * Description:
 *   This function is called by the me1400_ioctl, in order to setup a port
 *   as output or input.
 *
 * Parameter list:
 *   Name    Type                      	Access  Description
 *--------------------------------------------------------------------------
 *   *arg	 me1400_setup_port_type  	r       Specifies port and
 *                                             	carries the mode from user.
 *   *info   me1400_info_type		 	r		Pointer to device specific
 *                                         		global data structure.
 *
 * Result:
 *   On success the return value is OK, else is failure.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_setup_port(me1400_setup_port_type *arg, me1400_info_type *info)
{

	CALL_PDEBUG("me1400_setup_port() is executed\n");

	switch (arg->select) {
		case PORT_CTRL_A:
			me1400OutByte(info->me1400_regbase + ME1400_PORT_A_CTRL, arg->value);
			break;
		case PORT_CTRL_B:
			me1400OutByte(info->me1400_regbase + ME1400_PORT_B_CTRL, arg->value);
			break;
		default:
			ERROR_PDEBUG("me1400_setup_port():Invalid port control register\n");
			errno = EINVAL;
			return ERROR;
	}

	CALL_PDEBUG("me1400_setup_port() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_read_timer
 *
 * Description:
 *   This function is called by the me1400_ioctl, in order to read a value
 *   from a timer.
 *
 * Parameter list:
 *   Name    Type                      	Access  Description
 *--------------------------------------------------------------------------
 *   *arg    me1400_rw_timer_type	  	r/w     Specifies timer and
 *       	                                    carries the value to user.
 *   *info   me1400_info_type		 	r		Pointer to device specific
 *                                         		global data structure.
 *
 * Result:
 *   On success the return value is OK, else is failure.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_read_timer(me1400_rw_timer_type *arg, me1400_info_type *info)
{

	CALL_PDEBUG("me1400_read_timer() is executed\n");

	switch (arg->timer) {
		case TIMER_A_1:
			arg->value = me1400InByte(info->me1400_regbase + ME1400_TIMER_A_1);
			break;
		case TIMER_A_2:
			arg->value = me1400InByte(info->me1400_regbase + ME1400_TIMER_A_2);
			break;
		case TIMER_A_3:
			arg->value = me1400InByte(info->me1400_regbase + ME1400_TIMER_A_3);
			break;
		case TIMER_B_1:
			arg->value = me1400InByte(info->me1400_regbase + ME1400_TIMER_B_1);
			break;
		case TIMER_B_2:
			arg->value = me1400InByte(info->me1400_regbase + ME1400_TIMER_B_2);
			break;
		case TIMER_B_3:
			arg->value = me1400InByte(info->me1400_regbase + ME1400_TIMER_B_3);
			break;
		default:
			ERROR_PDEBUG("me1400_read_timer():Invalid timer\n");
			errno = EINVAL;
			return ERROR;
	}

	CALL_PDEBUG("me1400_read_timer() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_write_timer
 *
 * Description:
 *   This function is called by the me1400_ioctl, in order to write a value
 *   to a timer.
 *
 * Parameter list:
 *   Name		Type                  Access  Description
 *--------------------------------------------------------------------------
 *   *arg	 me1400_rw_timer_type	  r       Specifies timer and
 *                               			  carries the value from user.
 *   *info   me1400_info_type		  r		  Pointer to device specific
 *                                   		  global data structure.
 *
 * Result:
 *   On success the return value is OK, else is failure.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_write_timer(me1400_rw_timer_type *arg, me1400_info_type *info)
{

	CALL_PDEBUG("me1400_write_timer() is executed\n");

	switch (arg->timer) {
		case TIMER_A_1:
			me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_1, arg->value);
			break;
		case TIMER_A_2:
			me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_2, arg->value);
			break;
		case TIMER_A_3:
			me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_3, arg->value);
			break;
		case TIMER_B_1:
			me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_1, arg->value);
			break;
		case TIMER_B_2:
			me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_2, arg->value);
			break;
		case TIMER_B_3:
			me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_3, arg->value);
			break;
		default:
			ERROR_PDEBUG("me1400_write_timer():Invalid timer\n");
			errno = EINVAL;
			return ERROR;
	}

	CALL_PDEBUG("me1400_write_timer() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_setup_timer
 *
 * Description:
 *   This function is called by the me1400_ioctl, in order to setup a timer
 *   as BCD or binary counter, the read/write mode etc.
 *
 * Parameter list:
 *   Name		Type                       	Access  Description
 *--------------------------------------------------------------------------
 *   *arg		me1400_setup_timer_type  	r       Specifies timer and
 *                                                  carries the mode from user.
 *   *info  me1400_info_type		 		        Pointer to device specific
 *                                   				global data structure.
 * Result:
 *   On success the return value is OK, else is failure.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_setup_timer(me1400_setup_timer_type *arg, me1400_info_type *info)
{

	CALL_PDEBUG("me1400_setup_timer() is executed\n");

	switch (arg->select) {
		case TIMER_CTRL_A:
			me1400OutByte(info->me1400_regbase + ME1400_TIMER_A_CTRL, arg->value);
			break;
		case TIMER_CTRL_B:
			me1400OutByte(info->me1400_regbase + ME1400_TIMER_B_CTRL, arg->value);
			break;
		default:
			ERROR_PDEBUG("me1400_setup_timer():Invalid timer\n");
			errno = EINVAL;
			return ERROR;
	}

	CALL_PDEBUG("me1400_setup_timer() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_timer_connect
 *
 * Description:
 *   This function is called by the me1400_ioctl, in order to configure the
 *   clock frequency and the connections of the timer.
 *
 * Parameter list:
 *   Name		Type                   	Access  Description
 *--------------------------------------------------------------------------
 *   *arg		me1400_timer_mode_type	r       Carries the mode from user.
 *   *info  	me1400_info_type		r       Pointer to device specific
 *                                				global data structure.
 *
 * Result:
 *   On success the return value is OK, else is failure.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_setup_timer_connect(unsigned char *arg, me1400_info_type *info)
{

	CALL_PDEBUG("me1400_setup_timer_connect() is executed\n");
	me1400OutByte(info->me1400_regbase + ME1400_TIMER_CONNECT, *arg);

	CALL_PDEBUG("me1400_setup_timer_connect() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_irq_clockout
 *
 * Description:
 *   This function is called by the me1400_ioctl, in order to configure
 *   interrupt mode, or to give the clock out.
 *
 * Parameter list:
 *   Name    Type                       Access  Description
 *--------------------------------------------------------------------------
 *   *arg    me1400_irq_clockout_type 	r       carries the mode from user
 *   *info   me1400_info_type			r       Pointer to device specific
 *                                   			global data structure.
 *
 * Result:
 *   On success the return value is OK, else is failure.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_irq_clockout(unsigned char *arg, me1400_info_type *info)
{

	CALL_PDEBUG("me1400_setup_irq_clockout() is executed\n");

	me1400OutByte(info->me1400_regbase + ME1400_IRQ_CLOCKOUT, *arg);

	CALL_PDEBUG("me1400_setup_irq_clockout() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_get_int_count
 *
 * Description:
 *   This function is called by me1400_ioctl, in order to get the count of
 *   interrupts occoured since the module was loaded.
 *
 * Parameter list:
 *   Name    Type                      	Access  Description
 *--------------------------------------------------------------------------
 *   *arg    int	                    w       Carries the value to user.
 *   *info   me1400_info_type		 	r		Pointer to device specific
 *                                   			global data structure.
 *
 * Result:
 *   On success the return value is OK, else is failure.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_get_int_count(int *arg, me1400_info_type *info)
{

	CALL_PDEBUG("me1400_get_int_count() is executed\n");
	*arg = info->int_count;
	CALL_PDEBUG("me1400_get_int_count() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_reg_task
 *
 * Description:
 *   This function is called by me1400_ioctl, in order to register a task
 *   in the global board context, in order to get a signal from me1400_isr().
 *   This makes possible to execute a signal handler istalled from the
 *   user task. If the task id is 0, signalling is disabled.
 *
 * Parameter list:
 *   Name    Type                      	Access  Description
 *--------------------------------------------------------------------------
 *   *arg    int	                    r       The task id of the calling task.
 *   *info   me1400_info_type		 	r		Pointer to device specific
 *                                   			global data structure.
 *
 * Result:
 *   On success the return value is OK, else is failure.
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static STATUS me1400_reg_task(int *arg, me1400_info_type *info)
{

	CALL_PDEBUG("me1400_reg_task() is executed\n");
	info->tid = *arg;
	CALL_PDEBUG("me1400_reg_task() is leaved\n");
	return OK;
}



/*
 * Routine:
 *   me1400_isr
 *
 * Description:
 *   This is the interrupt service routine of the ME1400 board. This
 *   function is called, when the interrupt logic of the plx and the board
 *   is enabled and an external interrupt on the interrupt input of the
 *   subd occures. First it checks if the interrupt number is right and
 *   if this board rises the interrupt by reading the interrupt status
 *   register of the PLX. Then it clears the interrupt logic
 *   on the board and informs the process, probably remarked in the global
 *   data structure.
 *
 * Parameter list:
 *   Name    Type              	  Access  Description
 *--------------------------------------------------------------------------
 *   *info   me1400_info_type	  r		  Pointer to device specific
 *                                   	  global data structure.
 * Result:
 *--------------------------------------------------------------------------
 * Author: GG
 * Modufication:
 */
static void me1400_isr(me1400_info_type *info)
{
	/* unsigned char reg; */

	/* ISR_PDEBUG("me1400_isr() is executed\n"); */

	/* Check if this board rised the interrupt */
	/* reg = me1400InByte(info->plx_regbase + PLX_INTCSR);
	 if(!((reg & LOCAL_INT1_STATE)&&(reg & PCI_INT_EN))){
		ISR_PDEBUG("me1400_isr():not this board\n");
		return;
	 } */

	/* Reset the pending interrupt */
	/* ISR_PDEBUG("me1400_isr():reset interrupt\n"); */
	me1400OutByte(info->me1400_regbase + ME1400_IRQ_CLOCKOUT, IRQCLK_IRQMOD | IRQCLK_IRQRES);
	/* ISR_PDEBUG("me1400_isr():interrupt enabled\n"); */
	me1400OutByte(info->me1400_regbase + ME1400_IRQ_CLOCKOUT, IRQCLK_IRQMOD | IRQCLK_IRQENABLE);

	/* Increment the interrupt counter in the global data structure */
	/* info->int_count++; */
	info->user_isr();
	/* ISR_PDEBUG("me1400_isr() is leaved\n"); */
	return;
}

/*
 * Routine:                                                                 
 *   me1400                                                        
 *                                                                          
 * Description:                                                             
 *   With this function you can do the complete initialization process fo the ME1400B. 
 *   After initialization the device is ready for use in the application program.    
 *                                                                          
 * Parameter list:                                                          
 *   Name       Type            Access    Description                       
 *--------------------------------------------------------------------------
 *               
 *                                                                          
 * Result:                                                                  
 *   On success the return value is OK else ERROR                           
 *--------------------------------------------------------------------------
 * Author: AK                                                               
 * Modification:                                                            
 */
STATUS me1400()
{
	STATUS err = 0;

	printf("\n\n\n\n\nRegister driver in driver table.\n");
	err = me1400_drv();
	if (err == ERROR) {
		printf("Cannot register driver in driver table\n");
		return ERROR;
	}

	printf("Create me1400_0 device.\n");
	err = me1400_create_dev("me1400", 0, user_isr);
	if (err == ERROR) {
		printf("Cannot create device\n"
			"ERROR:Remove ME1400 driver from driver table\n");
		err = me1400_delete_drv();
		if (err == ERROR)
			printf("Cannot remove driver from driver table\n");
		return ERROR;
	}

	printf("Open path me1400.\n\n");
	file_handle = open("me1400", 0, 0644);

	if (file_handle == ERROR) {
		printf("Cannot open path !\n");
		return err;
	}

	return OK;
}

/*
 * Routine:                                                                 
 *   me1400dio                                                        
 *                                                                          
 * Description:                                                             
 *   With this function you can change the port modes of PIO_A and PIO_B.     
 *                                                                          
 * Parameter list:                                                          
 *   Name       Type            Access    Description                       
 *--------------------------------------------------------------------------
 *                  
 *                                                                          
 * Result:                                                                  
 *   On success the return value is OK else ERROR                           
 *--------------------------------------------------------------------------
 * Author: AK                                                               
 * Modification:                                                            
 */
STATUS me1400dio(void)
{
	STATUS err = 0;
	me1400_setup_port_type setup_a;

	setup_a.select = PORT_CTRL_A;
	setup_a.value = PORT_MODE_EAE;
	err = ioctl(file_handle, ME1400_SETUP_PORT, (int) &setup_a);
	if (err == ERROR) {
		printf("ERROR:Close path to me1400\n");
		err = close(file_handle);
		if (err == ERROR)
			printf("Kann Pfad nicht schliessen\n");
		return ERROR;
	}

	return OK;
}

STATUS me1400timer()
{
	STATUS err = 0;
	me1400_setup_timer_type setup_a;
	UCHAR connect;

	connect = IRQCLK_IRQRES;
	err = ioctl(file_handle, ME1400_SETUP_IRQ_CLOCKOUT, (int) &connect);
	if (err == ERROR) {
		printf("Cannot enable interrupt\n");
		goto ERROR_2;
	}

	/* Setup for the first timer */
	setup_a.select = TIMER_CTRL_A;		/* We want to setup one of the first three timer */
	setup_a.value = TIMER_CTRL_SC0 |	/* We want to setup the timer_a_1 */
		TIMER_CTRL_LM |					/* Write first lower byte, then higher byte */
		TIMER_CTRL_M2 |					/* Set to mode 2 */
		TIMER_CTRL_BIN;					/* Configure as binary counter */
	err = ioctl(file_handle, ME1400_SETUP_TIMER, (int) &setup_a);
	if (err == ERROR) {
		printf("Cannot setup first timer !\n");
		goto ERROR_2;
	}

	/* Setup for the second timer */
	setup_a.select = TIMER_CTRL_A;		/* We want to setup one of the first three timer */
	setup_a.value = TIMER_CTRL_SC1 |	/* We want to setup the timer_a_2 */
		TIMER_CTRL_LM |					/* Write first lower byte, then higher byte */
		TIMER_CTRL_M0 |					/* Set to mode 0 */
		TIMER_CTRL_BIN;					/* Configure as binary counter */
	err = ioctl(file_handle, ME1400_SETUP_TIMER, (int) &setup_a);
	if (err == ERROR) {
		printf("Cannot setup second timer !\n");
		goto ERROR_2;
	}

	if (MHz == 1000000)
		connect = TIMER_A_CONNECT_1MHZ;
	else if (MHz == 10000000)
		connect = TIMER_A_CONNECT_10MHZ;
	else {
		printf("Cannot setup timer clock !\n");
		goto ERROR_2;
	}
	connect |= (TIMER_A_CONNECT_QUARCLK0 | TIMER_A_CONNECT_OUT0CLK1);
	err = ioctl(file_handle, ME1400_SETUP_TIMER_CONNECT, (int) &connect);
	if (err == ERROR) {
		printf("Cannot setup timer connections !\n");
		goto ERROR_2;
	}

	connect = IRQCLK_IRQMOD | IRQCLK_IRQENABLE;
	err = ioctl(file_handle, ME1400_SETUP_IRQ_CLOCKOUT, (int) &connect);
	if (err == ERROR) {
		printf("Cannot enable interrupt\n");
		return err;
	}

	return OK;

ERROR_2:
	printf("ERROR:Close path to me1400\n");
	err = close(file_handle);
	if (err == ERROR)
		printf("Kann Pfad nicht schliessen\n");

	return ERROR;

}

/*
 * Routine:                                                                 
 *   stopDevice                                                        
 *                                                                          
 * Description:                                                             
 *   With this function you can stop the ME1400B.     
 *                                                                          
 * Parameter list:                                                          
 *   Name       Type            Access    Description                       
 *--------------------------------------------------------------------------
 *                  
 *                                                                          
 * Result:                                                                  
 *   On success the return value is OK else ERROR                           
 *--------------------------------------------------------------------------
 * Author: AK                                                               
 * Modification:                                                            
 */
STATUS stopDevice()
{
	STATUS err;

	printf ("\nClose path me1400.\n");
	err = close(file_handle);
	if (err == ERROR) {
		printf("Kann Pfad nicht schliessen"
			"ERROR:Remove device from device table\n");
		err = me1400_delete_dev("me1400", 0);
		if (err == ERROR)
			printf("Cannot remove device from device table\n");
		return ERROR;
	}

	printf ("Remove me1400_0 device.\n");
	err = me1400_delete_dev("me1400", 0);
	if (err == ERROR) {
		printf("Cannot remove device from device table\n");
		err = me1400_delete_drv();
		if (err == ERROR)
			printf("Cannot remove driver from driver table\n");
		return ERROR;
	}

	printf ("Remove driver from driver table.\n\n\n\n\n");
	err = me1400_delete_drv();
	if (err == ERROR) {
		printf("Cannot remove driver from driver table\n");
		return ERROR;
	}

	return OK;
}

/*
 * Routine:                                                                 
 *   timerLaden                                                        
 *                                                                          
 * Description:                                                             
 *   With this function you can prepare counter CINT1 to Interrupt.     
 *                                                                          
 * Parameter list:                                                          
 *   Name             Type            Access    Description                       
 *--------------------------------------------------------------------------
 *   wertTimerA2      UINT16          read      time to Interrupt   
 *                                                          
 *                                                                          
 * Result:                                                                  
 *   On success the return value is OK else ERROR                           
 *--------------------------------------------------------------------------
 * Author:                                                                
 * Modification:                                                            
 */
STATUS timerLaden(UINT16 wertTimerA2)
{
	STATUS err;
	UINT16 wertTimerA1 = ZEHN_kHz;
	me1400_rw_timer_type timer_a;

	timer_a.timer = TIMER_A_1;
	timer_a.value = wertTimerA1 & 0x00ff;			/* Lower byte */
	err = ioctl(file_handle, ME1400_WRITE_TIMER, (int) &timer_a);
	if (err == ERROR) {
		printf("Cannot write timerA1 !\n");
		return err;
	}
	timer_a.value = (wertTimerA1 & 0xff00) >> 8;	/* Higher byte */
	err = ioctl(file_handle, ME1400_WRITE_TIMER, (int) &timer_a);
	if (err == ERROR) {
		printf("Cannot write timerA1 !\n");
		return err;
	}

	timer_a.timer = TIMER_A_2;
	timer_a.value = wertTimerA2 & 0x00ff;			/* Lower byte */
	err = ioctl(file_handle, ME1400_WRITE_TIMER, (int) &timer_a);
	if (err == ERROR) {
		printf("Cannot write timerA2 !\n");
		return err;
	}
	timer_a.value = (wertTimerA2 & 0xff00) >> 8;	/* Higher byte */
	err = ioctl(file_handle, ME1400_WRITE_TIMER, (int) &timer_a);
	if (err == ERROR) {
		printf("Cannot write timerA2 !\n");
		return err;
	}

	return OK;
}

/*
 * Routine:                                                                 
 *   timerLesen                                                        
 *                                                                          
 * Description:                                                             
 *   With this function you can read the current time of a counter.     
 *                                                                          
 * Parameter list:                                                          
 *   Name             Type            Access    Description                       
 *--------------------------------------------------------------------------
 *   wertTimerA2      UINT16          write     current time of timer CINT1   
 *                                                          
 *                                                                          
 * Result:                                                                  
 *   On success the return value is OK else ERROR                           
 *--------------------------------------------------------------------------
 * Author:                                                                
 * Modification:                                                            
 */
STATUS timerLesen(UINT16* wertTimerA2)
{
	STATUS retValue = OK;
	UINT8 byte;
	me1400_rw_timer_type timer_a;

	timer_a.timer = TIMER_A_2;
	retValue = ioctl(file_handle, ME1400_READ_TIMER, (int) &timer_a);
	if (retValue == ERROR) {
		printf("Cannot read timerA2 !\n");
		return retValue;
	}
	byte = timer_a.value;
	retValue = ioctl(file_handle, ME1400_READ_TIMER, (int) &timer_a);
	if (retValue == ERROR) {
		printf("Cannot read timerA2 !\n");
		return retValue;
	}
	*wertTimerA2 = (timer_a.value << 8) + byte;

	return retValue;
}

/*
 * Routine:                                                                 
 *   portLesen                                                        
 *                                                                          
 * Description:                                                             
 *   With this function you can read from a port of the ME1400B.     
 *                                                                          
 * Parameter list:                                                          
 *   Name       Type            Access    Description                       
 *--------------------------------------------------------------------------
 *   port       UINT16          read      PIO-Port der ME1400B    
 *   value      UINT8*          write     eingelesenes Datenbyte
 *                                                          
 *                                                                          
 * Result:                                                                  
 *   On success the return value is OK else ERROR                           
 *--------------------------------------------------------------------------
 * Author: AK                                                               
 * Modification:                                                            
 */
STATUS portLesen(UINT16 port, UINT8* value)
{
	STATUS retValue;
	me1400_rw_port_type port_x;

	retValue = OK;
	port_x.port = port;

	retValue = ioctl(file_handle, ME1400_READ_PORT, (int) &port_x);
	if (retValue == ERROR)
		printf("Cannot read from port %d !\n",port);
	else
		*value = port_x.value;

	return retValue;
}

/*
 * Routine:                                                                 
 *   portSchreiben                                                        
 *                                                                          
 * Description:                                                             
 *   With this function you can write to a port of the ME1400B.     
 *                                                                          
 * Parameter list:                                                          
 *   Name       Type            Access    Description                       
 *--------------------------------------------------------------------------
 *   port       UINT16          read      PIO-Port der ME1400B     
 *   value      UINT8           read      zu schreibendes Datenbyte 
 *                                                       
 *                                                                          
 * Result:                                                                  
 *   On success the return value is OK else ERROR                           
 *--------------------------------------------------------------------------
 * Author: AK                                                               
 * Modification:                                                            
 */
STATUS portSchreiben(UINT16 port, UINT8 value)
{
	STATUS retValue;
	me1400_rw_port_type port_x;

	retValue = OK;
	port_x.port = port;
	port_x.value = value;

	retValue = ioctl(file_handle, ME1400_WRITE_PORT, (int) &port_x);
	if (retValue == ERROR)
		printf("Cannot write to port %d !\n",port);

	return retValue;
}
