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

@endverbatim
*/
/*

  +-----------------------------------------------------------------------+
  | (C) ADDI-DATA GmbH          Dieselstraße 3       D-77833 Ottersweier  |
  +-----------------------------------------------------------------------+
  | Tel : +49 (0) 7223/9493-0     | email    : info@addi-data.com         |
  | Fax : +49 (0) 7223/9493-92    | Internet : http://www.addi-data.com   |
  +-----------------------------------------------------------------------+
  | Project     : API APCI1710    | Compiler : gcc                        |
  | Module name : CHRONO.C        | Version  : 2.96                       |
  +-------------------------------+---------------------------------------+
  | Project manager: Eric Stolz   | Date     :  02/12/2002                |
  +-----------------------------------------------------------------------+
  | Description :   APCI-1710 chronometer module                          |
  |                                                                       |
  |                                                                       |
  +-----------------------------------------------------------------------+
  |                             UPDATES                                   |
  +-----------------------------------------------------------------------+
  |   Date   |   Author  |          Description of updates                |
  +----------+-----------+------------------------------------------------+
  | 29/06/98 | S. Weber  | Digital input / output implementation          |
  |----------|-----------|------------------------------------------------|
  | 08/05/00 | Guinot C  | - 0400/0228 All Function in RING 0             |
  |          |           |   available                                    |
  +-----------------------------------------------------------------------+
  |          |           |                                                |
  |          |           |                                                |
  +-----------------------------------------------------------------------+
*/

/*
+----------------------------------------------------------------------------+
|                               Included files                               |
+----------------------------------------------------------------------------+
*/
#include "APCI1710_Chrono.h"

/*
+----------------------------------------------------------------------------+
| Function Name     : _INT_     i_APCI1710_InitChrono                        |
|                                       (unsigned char_     b_BoardHandle,            |
|                                        unsigned char_     b_ModulNbr,               |
|                                        unsigned char_     b_ChronoMode,             |
|                                        unsigned char_     b_PCIInputClock,          |
|                                        unsigned char_     b_TimingUnit,             |
|                                        ULONG_   ul_TimingInterval,         |
|                                        PULONG_ pul_RealTimingInterval)

+----------------------------------------------------------------------------+
| Task              : Configure the chronometer operating mode (b_ChronoMode)|
|                     from selected module (b_ModulNbr).                     |
|                     The ul_TimingInterval and ul_TimingUnit determine the  |
|                     timing base for the measurement.                       |
|                     The pul_RealTimingInterval return the real timing      |
|                     value. You must calling this function be for you call  |
|                     any other function witch access of the chronometer.    |
|                                                                            |
|                     Witch this functionality from the APCI-1710 you have   |
|                     the possibility to measure the timing witch two event. |
|                                                                            |
|                     The mode 0 and 1 is appropriate for period measurement.|
|                     The mode 2 and 3 is appropriate for frequent           |
|                     measurement.                                           |
|                     The mode 4 to 7 is appropriate for measuring the timing|
|                     between  two event.                                    |
+----------------------------------------------------------------------------+
| Input Parameters  : unsigned char_   b_BoardHandle    : Handle of board APCI-1710   |
| unsigned char_   b_ModulNbr  CR_AREF(insn->chanspec)  : Module number to configure  |
|                                                (0 to 3)                    |
| unsigned char_   b_ChronoMode				data[0]    : Chronometer action mode     |
|                                                (0 to 7).                   |
| unsigned char_   b_PCIInputClock			data[1] : Selection from PCI bus clock|
|                                                - APCI1710_30MHZ :          |
|                                                  The PC have a PCI bus     |
|                                                  clock from 30 MHz         |
|                                                - APCI1710_33MHZ :          |
|                                                  The PC have a PCI bus     |
|                                                  clock from 33 MHz         |
|                                                - APCI1710_40MHZ            |
|                                                  The APCI-1710 have a      |
|                                                  integrated 40Mhz          |
|                                                  quartz.                   |
|               unsigned char_   b_TimingUnit	data[2]    : Base timing unity (0 to 4) |
|                                                 0 : ns                     |
|                                                 1 : µs                     |
|                                                 2 : ms                     |
|                                                 3 : s                      |
|                                                 4 : mn                     |
|         ULONG_ ul_TimingInterval : data[3]	 Base timing value.          |
+----------------------------------------------------------------------------+
| Output Parameters : PULONG_  pul_RealTimingInterval : Real  base timing    |
|                                                       value.
|                     data[0]
+----------------------------------------------------------------------------+
| Return Value      :  0: No error                                           |
|                     -1: The handle parameter of the board is wrong         |
|                     -2: Module selection wrong                             |
|                     -3: The module is not a Chronometer module             |
|                     -4: Chronometer mode selection is wrong                |
|                     -5: The selected PCI input clock is wrong              |
|                     -6: Timing unity selection is wrong                    |
|                     -7: Base timing selection is wrong                     |
|                     -8: You can not used the 40MHz clock selection with    |
|                         this board                                         |
|                     -9: You can not used the 40MHz clock selection with    |
|                         this CHRONOS version                               |
+----------------------------------------------------------------------------+
*/

int i_APCI1710_InsnConfigInitChrono(struct comedi_device *dev, struct comedi_subdevice *s,
	struct comedi_insn *insn, unsigned int *data)
{
	int i_ReturnValue = 0;
	unsigned int ul_TimerValue = 0;
	unsigned int ul_TimingInterval = 0;
	unsigned int ul_RealTimingInterval = 0;
	double d_RealTimingInterval = 0;
	unsigned int dw_ModeArray[8] =
		{ 0x01, 0x05, 0x00, 0x04, 0x02, 0x0E, 0x0A, 0x06 };
	unsigned char b_ModulNbr, b_ChronoMode, b_PCIInputClock, b_TimingUnit;

	b_ModulNbr = CR_AREF(insn->chanspec);
	b_ChronoMode = (unsigned char) data[0];
	b_PCIInputClock = (unsigned char) data[1];
	b_TimingUnit = (unsigned char) data[2];
	ul_TimingInterval = (unsigned int) data[3];
	i_ReturnValue = insn->n;

	/**************************/
	/* Test the module number */
	/**************************/

	if (b_ModulNbr < 4) {
	   /***********************/
		/* Test if chronometer */
	   /***********************/

		if ((devpriv->s_BoardInfos.
				dw_MolduleConfiguration[b_ModulNbr] &
				0xFFFF0000UL) == APCI1710_CHRONOMETER) {
	      /*****************************/
			/* Test the chronometer mode */
	      /*****************************/

			if (b_ChronoMode <= 7) {
		 /**************************/
				/* Test the PCI bus clock */
		 /**************************/

				if ((b_PCIInputClock == APCI1710_30MHZ) ||
					(b_PCIInputClock == APCI1710_33MHZ) ||
					(b_PCIInputClock == APCI1710_40MHZ)) {
		    /*************************/
					/* Test the timing unity */
		    /*************************/

					if (b_TimingUnit <= 4) {
		       /**********************************/
						/* Test the base timing selection */
		       /**********************************/

						if (((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 0) && (ul_TimingInterval >= 66) && (ul_TimingInterval <= 0xFFFFFFFFUL)) || ((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 1) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 143165576UL)) || ((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 2) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 143165UL)) || ((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 3) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 143UL)) || ((b_PCIInputClock == APCI1710_30MHZ) && (b_TimingUnit == 4) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 2UL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 0) && (ul_TimingInterval >= 60) && (ul_TimingInterval <= 0xFFFFFFFFUL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 1) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 130150240UL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 2) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 130150UL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 3) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 130UL)) || ((b_PCIInputClock == APCI1710_33MHZ) && (b_TimingUnit == 4) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 2UL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 0) && (ul_TimingInterval >= 50) && (ul_TimingInterval <= 0xFFFFFFFFUL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 1) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 107374182UL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 2) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 107374UL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 3) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 107UL)) || ((b_PCIInputClock == APCI1710_40MHZ) && (b_TimingUnit == 4) && (ul_TimingInterval >= 1) && (ul_TimingInterval <= 1UL))) {
			  /**************************/
							/* Test the board version */
			  /**************************/

							if (((b_PCIInputClock == APCI1710_40MHZ) && (devpriv->s_BoardInfos.b_BoardVersion > 0)) || (b_PCIInputClock != APCI1710_40MHZ)) {
			     /************************/
								/* Test the TOR version */
			     /************************/

								if (((b_PCIInputClock == APCI1710_40MHZ) && ((devpriv->s_BoardInfos.dw_MolduleConfiguration[b_ModulNbr] & 0xFFFF) >= 0x3131)) || (b_PCIInputClock != APCI1710_40MHZ)) {
									fpu_begin
										();

				/****************************************/
									/* Calculate the timer 0 division fator */
				/****************************************/

									switch (b_TimingUnit) {
				   /******/
										/* ns */
				   /******/

									case 0:

					   /******************/
										/* Timer 0 factor */
					   /******************/

										ul_TimerValue
											=
											(unsigned int)
											(ul_TimingInterval
											*
											(0.001 * b_PCIInputClock));

					   /*******************/
										/* Round the value */
					   /*******************/

										if ((double)((double)ul_TimingInterval * (0.001 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
											ul_TimerValue
												=
												ul_TimerValue
												+
												1;
										}

					   /*****************************/
										/* Calculate the real timing */
					   /*****************************/

										ul_RealTimingInterval
											=
											(unsigned int)
											(ul_TimerValue
											/
											(0.001 * (double)b_PCIInputClock));
										d_RealTimingInterval
											=
											(double)
											ul_TimerValue
											/
											(0.001
											*
											(double)
											b_PCIInputClock);

										if ((double)((double)ul_TimerValue / (0.001 * (double)b_PCIInputClock)) >= (double)((double)ul_RealTimingInterval + 0.5)) {
											ul_RealTimingInterval
												=
												ul_RealTimingInterval
												+
												1;
										}

										ul_TimingInterval
											=
											ul_TimingInterval
											-
											1;
										ul_TimerValue
											=
											ul_TimerValue
											-
											2;
										if (b_PCIInputClock != APCI1710_40MHZ) {
											ul_TimerValue
												=
												(unsigned int)
												(
												(double)
												(ul_TimerValue)
												*
												0.99392);
										}

										break;

				   /******/
										/* æs */
				   /******/

									case 1:

					   /******************/
										/* Timer 0 factor */
					   /******************/

										ul_TimerValue
											=
											(unsigned int)
											(ul_TimingInterval
											*
											(1.0 * b_PCIInputClock));

					   /*******************/
										/* Round the value */
					   /*******************/

										if ((double)((double)ul_TimingInterval * (1.0 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
											ul_TimerValue
												=
												ul_TimerValue
												+
												1;
										}

					   /*****************************/
										/* Calculate the real timing */
					   /*****************************/

										ul_RealTimingInterval
											=
											(unsigned int)
											(ul_TimerValue
											/
											(1.0 * (double)b_PCIInputClock));
										d_RealTimingInterval
											=
											(double)
											ul_TimerValue
											/
											(
											(double)
											1.0
											*
											(double)
											b_PCIInputClock);

										if ((double)((double)ul_TimerValue / (1.0 * (double)b_PCIInputClock)) >= (double)((double)ul_RealTimingInterval + 0.5)) {
											ul_RealTimingInterval
												=
												ul_RealTimingInterval
												+
												1;
										}

										ul_TimingInterval
											=
											ul_TimingInterval
											-
											1;
										ul_TimerValue
											=
											ul_TimerValue
											-
											2;
										if (b_PCIInputClock != APCI1710_40MHZ) {
											ul_TimerValue
												=
												(unsigned int)
												(
												(double)
												(ul_TimerValue)
												*
												0.99392);
										}

										break;

				   /******/
										/* ms */
				   /******/

									case 2:

					   /******************/
										/* Timer 0 factor */
					   /******************/

										ul_TimerValue
											=
											ul_TimingInterval
											*
											(1000
											*
											b_PCIInputClock);

					   /*******************/
										/* Round the value */
					   /*******************/

										if ((double)((double)ul_TimingInterval * (1000.0 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
											ul_TimerValue
												=
												ul_TimerValue
												+
												1;
										}

					   /*****************************/
										/* Calculate the real timing */
					   /*****************************/

										ul_RealTimingInterval
											=
											(unsigned int)
											(ul_TimerValue
											/
											(1000.0 * (double)b_PCIInputClock));
										d_RealTimingInterval
											=
											(double)
											ul_TimerValue
											/
											(1000.0
											*
											(double)
											b_PCIInputClock);

										if ((double)((double)ul_TimerValue / (1000.0 * (double)b_PCIInputClock)) >= (double)((double)ul_RealTimingInterval + 0.5)) {
											ul_RealTimingInterval
												=
												ul_RealTimingInterval
												+
												1;
										}

										ul_TimingInterval
											=
											ul_TimingInterval
											-
											1;
										ul_TimerValue
											=
											ul_TimerValue
											-
											2;
										if (b_PCIInputClock != APCI1710_40MHZ) {
											ul_TimerValue
												=
												(unsigned int)
												(
												(double)
												(ul_TimerValue)
												*
												0.99392);
										}

										break;

				   /*****/
										/* s */
				   /*****/

									case 3:

					   /******************/
										/* Timer 0 factor */
					   /******************/

										ul_TimerValue
											=
											(unsigned int)
											(ul_TimingInterval
											*
											(1000000.0
												*
												b_PCIInputClock));

					   /*******************/
										/* Round the value */
					   /*******************/

										if ((double)((double)ul_TimingInterval * (1000000.0 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
											ul_TimerValue
												=
												ul_TimerValue
												+
												1;
										}

					   /*****************************/
										/* Calculate the real timing */
					   /*****************************/

										ul_RealTimingInterval
											=
											(unsigned int)
											(ul_TimerValue
											/
											(1000000.0
												*
												(double)
												b_PCIInputClock));
										d_RealTimingInterval
											=
											(double)
											ul_TimerValue
											/
											(1000000.0
											*
											(double)
											b_PCIInputClock);

										if ((double)((double)ul_TimerValue / (1000000.0 * (double)b_PCIInputClock)) >= (double)((double)ul_RealTimingInterval + 0.5)) {
											ul_RealTimingInterval
												=
												ul_RealTimingInterval
												+
												1;
										}

										ul_TimingInterval
											=
											ul_TimingInterval
											-
											1;
										ul_TimerValue
											=
											ul_TimerValue
											-
											2;
										if (b_PCIInputClock != APCI1710_40MHZ) {
											ul_TimerValue
												=
												(unsigned int)
												(
												(double)
												(ul_TimerValue)
												*
												0.99392);
										}

										break;

				   /******/
										/* mn */
				   /******/

									case 4:

					   /******************/
										/* Timer 0 factor */
					   /******************/

										ul_TimerValue
											=
											(unsigned int)
											(
											(ul_TimingInterval
												*
												60)
											*
											(1000000.0
												*
												b_PCIInputClock));

					   /*******************/
										/* Round the value */
					   /*******************/

										if ((double)((double)(ul_TimingInterval * 60.0) * (1000000.0 * (double)b_PCIInputClock)) >= ((double)((double)ul_TimerValue + 0.5))) {
											ul_TimerValue
												=
												ul_TimerValue
												+
												1;
										}

					   /*****************************/
										/* Calculate the real timing */
					   /*****************************/

										ul_RealTimingInterval
											=
											(unsigned int)
											(ul_TimerValue
											/
											(1000000.0
												*
												(double)
												b_PCIInputClock))
											/
											60;
										d_RealTimingInterval
											=
											(
											(double)
											ul_TimerValue
											/
											(0.001 * (double)b_PCIInputClock)) / 60.0;

										if ((double)(((double)ul_TimerValue / (1000000.0 * (double)b_PCIInputClock)) / 60.0) >= (double)((double)ul_RealTimingInterval + 0.5)) {
											ul_RealTimingInterval
												=
												ul_RealTimingInterval
												+
												1;
										}

										ul_TimingInterval
											=
											ul_TimingInterval
											-
											1;
										ul_TimerValue
											=
											ul_TimerValue
											-
											2;
										if (b_PCIInputClock != APCI1710_40MHZ) {
											ul_TimerValue
												=
												(unsigned int)
												(
												(double)
												(ul_TimerValue)
												*
												0.99392);
										}

										break;
									}

									fpu_end();

				/****************************/
									/* Save the PCI input clock */
				/****************************/

									devpriv->
										s_ModuleInfo
										[b_ModulNbr].
										s_ChronoModuleInfo.
										b_PCIInputClock
										=
										b_PCIInputClock;

				/*************************/
									/* Save the timing unity */
				/*************************/

									devpriv->
										s_ModuleInfo
										[b_ModulNbr].
										s_ChronoModuleInfo.
										b_TimingUnit
										=
										b_TimingUnit;

				/************************/
									/* Save the base timing */
				/************************/

									devpriv->
										s_ModuleInfo
										[b_ModulNbr].
										s_ChronoModuleInfo.
										d_TimingInterval
										=
										d_RealTimingInterval;

				/****************************/
									/* Set the chronometer mode */
				/****************************/

									devpriv->
										s_ModuleInfo
										[b_ModulNbr].
										s_ChronoModuleInfo.
										dw_ConfigReg
										=
										dw_ModeArray
										[b_ChronoMode];

				/***********************/
									/* Test if 40 MHz used */
				/***********************/

									if (b_PCIInputClock == APCI1710_40MHZ) {
										devpriv->
											s_ModuleInfo
											[b_ModulNbr].
											s_ChronoModuleInfo.
											dw_ConfigReg
											=
											devpriv->
											s_ModuleInfo
											[b_ModulNbr].
											s_ChronoModuleInfo.
											dw_ConfigReg
											|
											0x80;
									}

									outl(devpriv->s_ModuleInfo[b_ModulNbr].s_ChronoModuleInfo.dw_ConfigReg, devpriv->s_BoardInfos.ui_Address + 16 + (64 * b_ModulNbr));

				/***********************/
									/* Write timer 0 value */
				/***********************/

									outl(ul_TimerValue, devpriv->s_BoardInfos.ui_Address + (64 * b_ModulNbr));

				/*********************/
									/* Chronometer init. */
				/*********************/

									devpriv->
										s_ModuleInfo
										[b_ModulNbr].
										s_ChronoModuleInfo.
										b_ChronoInit
										=
										1;
								} else {
				/***********************************************/
									/* TOR version error for 40MHz clock selection */
				/***********************************************/

									DPRINTK("TOR version error for 40MHz clock selection\n");
									i_ReturnValue
										=
										-9;
								}
							} else {
			     /**************************************************************/
								/* You can not use the 40MHz clock selection with this board */
			     /**************************************************************/

								DPRINTK("You can not used the 40MHz clock selection with this board\n");
								i_ReturnValue =
									-8;
							}
						} else {
			  /**********************************/
							/* Base timing selection is wrong */
			  /**********************************/

							DPRINTK("Base timing selection is wrong\n");
							i_ReturnValue = -7;
						}
					}	/*  if ((b_TimingUnit >= 0) && (b_TimingUnit <= 4)) */
					else {
		       /***********************************/
						/* Timing unity selection is wrong */
		       /***********************************/

						DPRINTK("Timing unity selection is wrong\n");
						i_ReturnValue = -6;
					}	/*  if ((b_TimingUnit >= 0) && (b_TimingUnit <= 4)) */
				}	/*  if ((b_PCIInputClock == APCI1710_30MHZ) || (b_PCIInputClock == APCI1710_33MHZ)) */
				else {
		    /*****************************************/
					/* The selected PCI input clock is wrong */
		    /*****************************************/

					DPRINTK("The selected PCI input clock is wrong\n");
					i_ReturnValue = -5;
				}	/*  if ((b_PCIInputClock == APCI1710_30MHZ) || (b_PCIInputClock == APCI1710_33MHZ)) */
			}	/*  if (b_ChronoMode >= 0 && b_ChronoMode <= 7) */
			else {
		 /***************************************/
				/* Chronometer mode selection is wrong */
		 /***************************************/

				DPRINTK("Chronometer mode selection is wrong\n");
				i_ReturnValue = -4;
			}	/*  if (b_ChronoMode >= 0 && b_ChronoMode <= 7) */
		} else {
	      /******************************************/
			/* The module is not a Chronometer module */
	      /******************************************/

			DPRINTK("The module is not a Chronometer module\n");
			i_ReturnValue = -3;
		}
	} else {
	   /***********************/
		/* Module number error */
	   /***********************/

		DPRINTK("Module number error\n");
		i_ReturnValue = -2;
	}
	data[0] = ul_RealTimingInterval;
	return i_ReturnValue;
}

/*
+----------------------------------------------------------------------------+
| Function Name     : _INT_ i_APCI1710_EnableChrono                          |
|                                               (unsigned char_ b_BoardHandle,        |
|                                                unsigned char_ b_ModulNbr,           |
|                                                unsigned char_ b_CycleMode,          |
|                                                unsigned char_ b_InterruptEnable)
int i_APCI1710_InsnWriteEnableDisableChrono(struct comedi_device *dev,
struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data)						 |
+----------------------------------------------------------------------------+
| Task              : Enable the chronometer from selected module            |
|                     (b_ModulNbr). You must calling the                     |
|                     "i_APCI1710_InitChrono" function be for you call this  |
|                     function.                                              |
|                     If you enable the chronometer interrupt, the           |
|                     chronometer generate a interrupt after the stop signal.|
|                     See function "i_APCI1710_SetBoardIntRoutineX" and the  |
|                     Interrupt mask description chapter from this manual.   |
|                     The b_CycleMode parameter determine if you will        |
|                     measured a single or more cycle.

|					  Disable the chronometer from selected module           |
|                     (b_ModulNbr). If you disable the chronometer after a   |
|                     start signal occur and you restart the chronometer     |
|                     witch the " i_APCI1710_EnableChrono" function, if no   |
|                     stop signal occur this start signal is ignored.
+----------------------------------------------------------------------------+
| Input Parameters  : unsigned char_ b_BoardHandle  : Handle of board APCI-1710       |
|                     unsigned char_ b_ModulNbr   CR_AREF(chanspec)  : Selected module number (0 to 3) |
                                  data[0]  ENABle/Disable chrono
|                     unsigned char_ b_CycleMode    : Selected the chronometer        |
|                                  data[1]           acquisition mode                |
|                     unsigned char_ b_InterruptEnable : Enable or disable the        |
|                                   data[2]            chronometer interrupt.       |
|                                               APCI1710_ENABLE:             |
|                                               Enable the chronometer       |
|                                               interrupt                    |
|                                               APCI1710_DISABLE:            |
|                                               Disable the chronometer      |
|                                               interrupt                    |
+----------------------------------------------------------------------------+
| Output Parameters : -                                                      |
+----------------------------------------------------------------------------+
| Return Value      :  0: No error                                           |
|                     -1: The handle parameter of the board is wrong         |
|                     -2: Module selection wrong                             |
|                     -3: The module is not a Chronometer module             |
|                     -4: Chronometer not initialised see function           |
|                         "i_APCI1710_InitChrono"                            |
|                     -5: Chronometer acquisition mode cycle is wrong        |
|                     -6: Interrupt parameter is wrong                       |
|                     -7: Interrupt function not initialised.                |
|                         See function "i_APCI1710_SetBoardIntRoutineX"
                      -8: data[0] wrong input    |
+----------------------------------------------------------------------------+
*/

int i_APCI1710_InsnWriteEnableDisableChrono(struct comedi_device *dev,
	struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
	int i_ReturnValue = 0;
	unsigned char b_ModulNbr, b_CycleMode, b_InterruptEnable, b_Action;
	b_ModulNbr = CR_AREF(insn->chanspec);
	b_Action = (unsigned char) data[0];
	b_CycleMode = (unsigned char) data[1];
	b_InterruptEnable = (unsigned char) data[2];
	i_ReturnValue = insn->n;

	/**************************/
	/* Test the module number */
	/**************************/

	if (b_ModulNbr < 4) {
	   /***********************/
		/* Test if chronometer */
	   /***********************/

		if ((devpriv->s_BoardInfos.
				dw_MolduleConfiguration[b_ModulNbr] &
				0xFFFF0000UL) == APCI1710_CHRONOMETER) {
	      /***********************************/
			/* Test if chronometer initialised */
	      /***********************************/

			if (devpriv->s_ModuleInfo[b_ModulNbr].
				s_ChronoModuleInfo.b_ChronoInit == 1) {

				switch (b_Action) {

				case APCI1710_ENABLE:

		 /*********************************/
					/* Test the cycle mode parameter */
		 /*********************************/

					if ((b_CycleMode == APCI1710_SINGLE)
						|| (b_CycleMode ==
							APCI1710_CONTINUOUS)) {
		    /***************************/
						/* Test the interrupt flag */
		    /***************************/

						if ((b_InterruptEnable ==
								APCI1710_ENABLE)
							|| (b_InterruptEnable ==
								APCI1710_DISABLE))
						{

			  /***************************/
							/* Save the interrupt flag */
			  /***************************/

							devpriv->
								s_ModuleInfo
								[b_ModulNbr].
								s_ChronoModuleInfo.
								b_InterruptMask
								=
								b_InterruptEnable;

			  /***********************/
							/* Save the cycle mode */
			  /***********************/

							devpriv->
								s_ModuleInfo
								[b_ModulNbr].
								s_ChronoModuleInfo.
								b_CycleMode =
								b_CycleMode;

							devpriv->
								s_ModuleInfo
								[b_ModulNbr].
								s_ChronoModuleInfo.
								dw_ConfigReg =
								(devpriv->
								s_ModuleInfo
								[b_ModulNbr].
								s_ChronoModuleInfo.
								dw_ConfigReg &
								0x8F) | ((1 &
									b_InterruptEnable)
								<< 5) | ((1 &
									b_CycleMode)
								<< 6) | 0x10;

			  /*****************************/
							/* Test if interrupt enabled */
			  /*****************************/

							if (b_InterruptEnable ==
								APCI1710_ENABLE)
							{
			     /****************************/
								/* Clear the interrupt flag */
			     /****************************/

								outl(devpriv->
									s_ModuleInfo
									[b_ModulNbr].
									s_ChronoModuleInfo.
									dw_ConfigReg,
									devpriv->
									s_BoardInfos.
									ui_Address
									+ 32 +
									(64 * b_ModulNbr));
								devpriv->tsk_Current = current;	/*  Save the current process task structure */
							}

			  /***********************************/
							/* Enable or disable the interrupt */
							/* Enable the chronometer          */
			  /***********************************/

							outl(devpriv->
								s_ModuleInfo
								[b_ModulNbr].
								s_ChronoModuleInfo.
								dw_ConfigReg,
								devpriv->
								s_BoardInfos.
								ui_Address +
								16 +
								(64 * b_ModulNbr));

			  /*************************/
							/* Clear status register */
			  /*************************/

							outl(0, devpriv->
								s_BoardInfos.
								ui_Address +
								36 +
								(64 * b_ModulNbr));

						}	/*  if ((b_InterruptEnable == APCI1710_ENABLE) || (b_InterruptEnable == APCI1710_DISABLE)) */
						else {
		       /********************************/
							/* Interrupt parameter is wrong */
		       /********************************/

							DPRINTK("Interrupt parameter is wrong\n");
							i_ReturnValue = -6;
						}	/*  if ((b_InterruptEnable == APCI1710_ENABLE) || (b_InterruptEnable == APCI1710_DISABLE)) */
					}	/*  if ((b_CycleMode == APCI1710_SINGLE) || (b_CycleMode == APCI1710_CONTINUOUS)) */
					else {
		    /***********************************************/
						/* Chronometer acquisition mode cycle is wrong */
		    /***********************************************/

						DPRINTK("Chronometer acquisition mode cycle is wrong\n");
						i_ReturnValue = -5;
					}	/*  if ((b_CycleMode == APCI1710_SINGLE) || (b_CycleMode == APCI1710_CONTINUOUS)) */
					break;

				case APCI1710_DISABLE:

					devpriv->s_ModuleInfo[b_ModulNbr].
						s_ChronoModuleInfo.
						b_InterruptMask = 0;

					devpriv->s_ModuleInfo[b_ModulNbr].
						s_ChronoModuleInfo.
						dw_ConfigReg =
						devpriv->
						s_ModuleInfo[b_ModulNbr].
						s_ChronoModuleInfo.
						dw_ConfigReg & 0x2F;

		 /***************************/
					/* Disable the interrupt   */
					/* Disable the chronometer */
		 /***************************/

					outl(devpriv->s_ModuleInfo[b_ModulNbr].
						s_ChronoModuleInfo.dw_ConfigReg,
						devpriv->s_BoardInfos.
						ui_Address + 16 +
						(64 * b_ModulNbr));

		 /***************************/
					/* Test if continuous mode */
		 /***************************/

					if (devpriv->s_ModuleInfo[b_ModulNbr].
						s_ChronoModuleInfo.
						b_CycleMode ==
						APCI1710_CONTINUOUS) {
		    /*************************/
						/* Clear status register */
		    /*************************/

						outl(0, devpriv->s_BoardInfos.
							ui_Address + 36 +
							(64 * b_ModulNbr));
					}
					break;

				default:
					DPRINTK("Inputs wrong! Enable or Disable chrono\n");
					i_ReturnValue = -8;
				}	/*  switch ENABLE/DISABLE */
			} else {
		 /*******************************/
				/* Chronometer not initialised */
		 /*******************************/

				DPRINTK("Chronometer not initialised\n");
				i_ReturnValue = -4;
			}
		} else {
	      /******************************************/
			/* The module is not a Chronometer module */
	      /******************************************/

			DPRINTK("The module is not a Chronometer module\n");
			i_ReturnValue = -3;
		}
	} else {
	   /***********************/
		/* Module number error */
	   /***********************/

		DPRINTK("Module number error\n");
		i_ReturnValue = -2;
	}

	return i_ReturnValue;
}

/*
+----------------------------------------------------------------------------+
| Function Name     :INT	i_APCI1710_InsnReadChrono(struct comedi_device *dev,struct comedi_subdevice *s,
struct comedi_insn *insn,unsigned int *data)                   |
+----------------------------------------------------------------------------+
| Task              : Read  functions for Timer                                     |
+----------------------------------------------------------------------------+
| Input Parameters  :
+----------------------------------------------------------------------------+
| Output Parameters : -                                                      |
+----------------------------------------------------------------------------+
| Return Value      :
+----------------------------------------------------------------------------+
*/

int i_APCI1710_InsnReadChrono(struct comedi_device *dev, struct comedi_subdevice *s,
	struct comedi_insn *insn, unsigned int *data)
{
	unsigned char b_ReadType;
	int i_ReturnValue = insn->n;

	b_ReadType = CR_CHAN(insn->chanspec);

	switch (b_ReadType) {
	case APCI1710_CHRONO_PROGRESS_STATUS:
		i_ReturnValue = i_APCI1710_GetChronoProgressStatus(dev,
			(unsigned char) CR_AREF(insn->chanspec), (unsigned char *) &data[0]);
		break;

	case APCI1710_CHRONO_READVALUE:
		i_ReturnValue = i_APCI1710_ReadChronoValue(dev,
			(unsigned char) CR_AREF(insn->chanspec),
			(unsigned int) insn->unused[0],
			(unsigned char *) &data[0], (unsigned int *) &data[1]);
		break;

	case APCI1710_CHRONO_CONVERTVALUE:
		i_ReturnValue = i_APCI1710_ConvertChronoValue(dev,
			(unsigned char) CR_AREF(insn->chanspec),
			(unsigned int) insn->unused[0],
			(unsigned int *) &data[0],
			(unsigned char *) &data[1],
			(unsigned char *) &data[2],
			(unsigned int *) &data[3],
			(unsigned int *) &data[4], (unsigned int *) &data[5]);
		break;

	case APCI1710_CHRONO_READINTERRUPT:
		printk("In Chrono Read Interrupt\n");

		data[0] = devpriv->s_InterruptParameters.
			s_FIFOInterruptParameters[devpriv->
			s_InterruptParameters.ui_Read].b_OldModuleMask;
		data[1] = devpriv->s_InterruptParameters.
			s_FIFOInterruptParameters[devpriv->
			s_InterruptParameters.ui_Read].ul_OldInterruptMask;
		data[2] = devpriv->s_InterruptParameters.
			s_FIFOInterruptParameters[devpriv->
			s_InterruptParameters.ui_Read].ul_OldCounterLatchValue;

			     /**************************/
		/* Increment the read FIFO */
			     /***************************/

		devpriv->
			s_InterruptParameters.
			ui_Read = (devpriv->
			s_InterruptParameters.
			ui_Read + 1) % APCI1710_SAVE_INTERRUPT;
		break;

	default:
		printk("ReadType Parameter wrong\n");
	}

	if (i_ReturnValue >= 0)
		i_ReturnValue = insn->n;
	return i_ReturnValue;

}

/*
+----------------------------------------------------------------------------+
| Function Name     : _INT_ i_APCI1710_GetChronoProgressStatus               |
|                               (unsigned char_    b_BoardHandle,                     |
|                                unsigned char_    b_ModulNbr,                        |
|                                unsigned char *_  pb_ChronoStatus)                    |
+----------------------------------------------------------------------------+
| Task              : Return the chronometer status (pb_ChronoStatus) from   |
|                     selected chronometer module (b_ModulNbr).              |
+----------------------------------------------------------------------------+
| Input Parameters  : unsigned char_ b_BoardHandle  : Handle of board APCI-1710       |
|                     unsigned char_ b_ModulNbr     : Selected module number (0 to 3) |
+----------------------------------------------------------------------------+
| Output Parameters : PULONG_  pb_ChronoStatus : Return the chronometer      |
|                                                status.                     |
|                                                0 : Measurement not started.|
|                                                    No start signal occur.  |
|                                                1 : Measurement started.    |
|                                                    A start signal occur.   |
|                                                2 : Measurement stopped.    |
|                                                    A stop signal occur.    |
|                                                    The measurement is      |
|                                                    terminate.              |
|                                                3: A overflow occur. You    |
|                                                   must change the base     |
|                                                   timing witch the         |
|                                                   function                 |
|                                                   "i_APCI1710_InitChrono"  |
+----------------------------------------------------------------------------+
| Return Value      :  0: No error                                           |
|                     -1: The handle parameter of the board is wrong         |
|                     -2: Module selection wrong                             |
|                     -3: The module is not a Chronometer module             |
|                     -4: Chronometer not initialised see function           |
|                         "i_APCI1710_InitChrono"                            |
+----------------------------------------------------------------------------+
*/

int i_APCI1710_GetChronoProgressStatus(struct comedi_device *dev,
	unsigned char b_ModulNbr, unsigned char *pb_ChronoStatus)
{
	int i_ReturnValue = 0;
	unsigned int dw_Status;

	/**************************/
	/* Test the module number */
	/**************************/

	if (b_ModulNbr < 4) {
	   /***********************/
		/* Test if chronometer */
	   /***********************/

		if ((devpriv->s_BoardInfos.
				dw_MolduleConfiguration[b_ModulNbr] &
				0xFFFF0000UL) == APCI1710_CHRONOMETER) {
	      /***********************************/
			/* Test if chronometer initialised */
	      /***********************************/

			if (devpriv->
				s_ModuleInfo[b_ModulNbr].
				s_ChronoModuleInfo.b_ChronoInit == 1) {

				dw_Status = inl(devpriv->s_BoardInfos.
					ui_Address + 8 + (64 * b_ModulNbr));

		 /********************/
				/* Test if overflow */
		 /********************/

				if ((dw_Status & 8) == 8) {
		    /******************/
					/* Overflow occur */
		    /******************/

					*pb_ChronoStatus = 3;
				}	/*  if ((dw_Status & 8) == 8) */
				else {
		    /*******************************/
					/* Test if measurement stopped */
		    /*******************************/

					if ((dw_Status & 2) == 2) {
		       /***********************/
						/* A stop signal occur */
		       /***********************/

						*pb_ChronoStatus = 2;
					}	/*  if ((dw_Status & 2) == 2) */
					else {
		       /*******************************/
						/* Test if measurement started */
		       /*******************************/

						if ((dw_Status & 1) == 1) {
			  /************************/
							/* A start signal occur */
			  /************************/

							*pb_ChronoStatus = 1;
						}	/*  if ((dw_Status & 1) == 1) */
						else {
			  /***************************/
							/* Measurement not started */
			  /***************************/

							*pb_ChronoStatus = 0;
						}	/*  if ((dw_Status & 1) == 1) */
					}	/*  if ((dw_Status & 2) == 2) */
				}	/*  if ((dw_Status & 8) == 8) */
			} else {
		 /*******************************/
				/* Chronometer not initialised */
		 /*******************************/
				DPRINTK("Chronometer not initialised\n");
				i_ReturnValue = -4;
			}
		} else {
	      /******************************************/
			/* The module is not a Chronometer module */
	      /******************************************/
			DPRINTK("The module is not a Chronometer module\n");
			i_ReturnValue = -3;
		}
	} else {
	   /***********************/
		/* Module number error */
	   /***********************/
		DPRINTK("Module number error\n");
		i_ReturnValue = -2;
	}

	return i_ReturnValue;
}

/*
+----------------------------------------------------------------------------+
| Function Name     : _INT_ i_APCI1710_ReadChronoValue                       |
|                               (unsigned char_     b_BoardHandle,                    |
|                                unsigned char_     b_ModulNbr,                       |
|                                unsigned int_    ui_TimeOut,                        |
|                                unsigned char *_   pb_ChronoStatus,                   |
|                                PULONG_ pul_ChronoValue)                    |
+----------------------------------------------------------------------------+
| Task              : Return the chronometer status (pb_ChronoStatus) and the|
|                     timing value (pul_ChronoValue) after a stop signal     |
|                     occur from selected chronometer module (b_ModulNbr).   |
|                     This function are only avaible if you have disabled    |
|                     the interrupt functionality. See function              |
|                     "i_APCI1710_EnableChrono" and the Interrupt mask       |
|                     description chapter.                                   |
|                     You can test the chronometer status witch the          |
|                     "i_APCI1710_GetChronoProgressStatus" function.         |
|                                                                            |
|                     The returned value from pul_ChronoValue parameter is   |
|                     not real measured timing.                              |
|                     You must used the "i_APCI1710_ConvertChronoValue"      |
|                     function or make this operation for calculate the      |
|                     timing:                                                |
|                                                                            |
|                     Timing = pul_ChronoValue * pul_RealTimingInterval.     |
|                                                                            |
|                     pul_RealTimingInterval is the returned parameter from  |
|                     "i_APCI1710_InitChrono" function and the time unity is |
|                     the b_TimingUnit from "i_APCI1710_InitChrono" function|
+----------------------------------------------------------------------------+
| Input Parameters  : unsigned char_ b_BoardHandle  : Handle of board APCI-1710       |
|                     unsigned char_ b_ModulNbr     : Selected module number (0 to 3) |
+----------------------------------------------------------------------------+
| Output Parameters : PULONG_  pb_ChronoStatus : Return the chronometer      |
|                                                status.                     |
|                                                0 : Measurement not started.|
|                                                    No start signal occur.  |
|                                                1 : Measurement started.    |
|                                                    A start signal occur.   |
|                                                2 : Measurement stopped.    |
|                                                    A stop signal occur.    |
|                                                    The measurement is      |
|                                                    terminate.              |
|                                                3: A overflow occur. You    |
|                                                   must change the base     |
|                                                   timing witch the         |
|                                                   function                 |
|                                                   "i_APCI1710_InitChrono"  |
|                     unsigned int *  pul_ChronoValue  : Chronometer timing value.   |
+----------------------------------------------------------------------------+
| Return Value      :  0: No error                                           |
|                     -1: The handle parameter of the board is wrong         |
|                     -2: Module selection wrong                             |
|                     -3: The module is not a Chronometer module             |
|                     -4: Chronometer not initialised see function           |
|                         "i_APCI1710_InitChrono"                            |
|                     -5: Timeout parameter is wrong (0 to 65535)            |
|                     -6: Interrupt routine installed. You can not read      |
|                         directly the chronometer measured timing.          |
+----------------------------------------------------------------------------+
*/

int i_APCI1710_ReadChronoValue(struct comedi_device *dev,
	unsigned char b_ModulNbr,
	unsigned int ui_TimeOut, unsigned char *pb_ChronoStatus, unsigned int *pul_ChronoValue)
{
	int i_ReturnValue = 0;
	unsigned int dw_Status;
	unsigned int dw_TimeOut = 0;

	/**************************/
	/* Test the module number */
	/**************************/

	if (b_ModulNbr < 4) {
	   /***********************/
		/* Test if chronometer */
	   /***********************/

		if ((devpriv->s_BoardInfos.
				dw_MolduleConfiguration[b_ModulNbr] &
				0xFFFF0000UL) == APCI1710_CHRONOMETER) {
	      /***********************************/
			/* Test if chronometer initialised */
	      /***********************************/

			if (devpriv->
				s_ModuleInfo[b_ModulNbr].
				s_ChronoModuleInfo.b_ChronoInit == 1) {
		 /*****************************/
				/* Test the timout parameter */
		 /*****************************/

				if (ui_TimeOut <= 65535UL) {

					for (;;) {
			  /*******************/
						/* Read the status */
			  /*******************/

						dw_Status =
							inl(devpriv->
							s_BoardInfos.
							ui_Address + 8 +
							(64 * b_ModulNbr));

			  /********************/
						/* Test if overflow */
			  /********************/

						if ((dw_Status & 8) == 8) {
			     /******************/
							/* Overflow occur */
			     /******************/

							*pb_ChronoStatus = 3;

			     /***************************/
							/* Test if continuous mode */
			     /***************************/

							if (devpriv->
								s_ModuleInfo
								[b_ModulNbr].
								s_ChronoModuleInfo.
								b_CycleMode ==
								APCI1710_CONTINUOUS)
							{
				/*************************/
								/* Clear status register */
				/*************************/

								outl(0, devpriv->s_BoardInfos.ui_Address + 36 + (64 * b_ModulNbr));
							}

							break;
						}	/*  if ((dw_Status & 8) == 8) */
						else {
			     /*******************************/
							/* Test if measurement stopped */
			     /*******************************/

							if ((dw_Status & 2) ==
								2) {
				/***********************/
								/* A stop signal occur */
				/***********************/

								*pb_ChronoStatus
									= 2;

				/***************************/
								/* Test if continnous mode */
				/***************************/

								if (devpriv->
									s_ModuleInfo
									[b_ModulNbr].
									s_ChronoModuleInfo.
									b_CycleMode
									==
									APCI1710_CONTINUOUS)
								{
				   /*************************/
									/* Clear status register */
				   /*************************/

									outl(0, devpriv->s_BoardInfos.ui_Address + 36 + (64 * b_ModulNbr));
								}
								break;
							}	/*  if ((dw_Status & 2) == 2) */
							else {
				/*******************************/
								/* Test if measurement started */
				/*******************************/

								if ((dw_Status & 1) == 1) {
				   /************************/
									/* A start signal occur */
				   /************************/

									*pb_ChronoStatus
										=
										1;
								}	/*  if ((dw_Status & 1) == 1) */
								else {
				   /***************************/
									/* Measurement not started */
				   /***************************/

									*pb_ChronoStatus
										=
										0;
								}	/*  if ((dw_Status & 1) == 1) */
							}	/*  if ((dw_Status & 2) == 2) */
						}	/*  if ((dw_Status & 8) == 8) */

						if (dw_TimeOut == ui_TimeOut) {
			     /*****************/
							/* Timeout occur */
			     /*****************/

							break;
						} else {
			     /*************************/
							/* Increment the timeout */
			     /*************************/

							dw_TimeOut =
								dw_TimeOut + 1;
							mdelay(1000);

						}
					}	/*  for (;;) */

		       /*****************************/
					/* Test if stop signal occur */
		       /*****************************/

					if (*pb_ChronoStatus == 2) {
			  /**********************************/
						/* Read the measured timing value */
			  /**********************************/

						*pul_ChronoValue =
							inl(devpriv->
							s_BoardInfos.
							ui_Address + 4 +
							(64 * b_ModulNbr));

						if (*pul_ChronoValue != 0) {
							*pul_ChronoValue =
								*pul_ChronoValue
								- 1;
						}
					} else {
			  /*************************/
						/* Test if timeout occur */
			  /*************************/

						if ((*pb_ChronoStatus != 3)
							&& (dw_TimeOut ==
								ui_TimeOut)
							&& (ui_TimeOut != 0)) {
			     /*****************/
							/* Timeout occur */
			     /*****************/

							*pb_ChronoStatus = 4;
						}
					}

				} else {
		    /******************************/
					/* Timeout parameter is wrong */
		    /******************************/
					DPRINTK("Timeout parameter is wrong\n");
					i_ReturnValue = -5;
				}
			} else {
		 /*******************************/
				/* Chronometer not initialised */
		 /*******************************/
				DPRINTK("Chronometer not initialised\n");
				i_ReturnValue = -4;
			}
		} else {
	      /******************************************/
			/* The module is not a Chronometer module */
	      /******************************************/
			DPRINTK("The module is not a Chronometer module\n");
			i_ReturnValue = -3;
		}
	} else {
	   /***********************/
		/* Module number error */
	   /***********************/
		DPRINTK("Module number error\n");
		i_ReturnValue = -2;
	}

	return i_ReturnValue;
}

/*
+----------------------------------------------------------------------------+
| Function Name     : _INT_ i_APCI1710_ConvertChronoValue                    |
|                               (unsigned char_     b_BoardHandle,                    |
|                                unsigned char_     b_ModulNbr,                       |
|                                ULONG_   ul_ChronoValue,                    |
|                                PULONG_ pul_Hour,                           |
|                                unsigned char *_   pb_Minute,                         |
|                                unsigned char *_   pb_Second,                         |
|                                unsigned int *_  pui_MilliSecond,                    |
|                                unsigned int *_  pui_MicroSecond,                    |
|                                unsigned int *_  pui_NanoSecond)                     |
+----------------------------------------------------------------------------+
| Task              : Convert the chronometer measured timing                |
|                     (ul_ChronoValue) in to h, mn, s, ms, µs, ns.           |
+----------------------------------------------------------------------------+
| Input Parameters  : unsigned char_   b_BoardHandle : Handle of board APCI-1710      |
|                     unsigned char_   b_ModulNbr    : Selected module number (0 to 3)|
|                     ULONG_ ul_ChronoValue : Measured chronometer timing    |
|                                             value.                         |
|                                             See"i_APCI1710_ReadChronoValue"|
+----------------------------------------------------------------------------+
| Output Parameters : PULONG_   pul_Hour        : Chronometer timing hour    |
|                     unsigned char *_     pb_Minute      : Chronometer timing minute  |
|                     unsigned char *_     pb_Second      : Chronometer timing second  |
|                     unsigned int *_    pui_MilliSecond  : Chronometer timing mini   |
|                                                 second                     |
|                     unsigned int *_    pui_MicroSecond : Chronometer timing micro   |
|                                                 second                     |
|                     unsigned int *_    pui_NanoSecond  : Chronometer timing nano    |
|                                                 second                     |
+----------------------------------------------------------------------------+
| Return Value      :  0: No error                                           |
|                     -1: The handle parameter of the board is wrong         |
|                     -2: Module selection wrong                             |
|                     -3: The module is not a Chronometer module             |
|                     -4: Chronometer not initialised see function           |
|                         "i_APCI1710_InitChrono"                            |
+----------------------------------------------------------------------------+
*/

int i_APCI1710_ConvertChronoValue(struct comedi_device *dev,
	unsigned char b_ModulNbr,
	unsigned int ul_ChronoValue,
	unsigned int *pul_Hour,
	unsigned char *pb_Minute,
	unsigned char *pb_Second,
	unsigned int *pui_MilliSecond, unsigned int *pui_MicroSecond, unsigned int *pui_NanoSecond)
{
	int i_ReturnValue = 0;
	double d_Hour;
	double d_Minute;
	double d_Second;
	double d_MilliSecond;
	double d_MicroSecond;
	double d_NanoSecond;

	/**************************/
	/* Test the module number */
	/**************************/

	if (b_ModulNbr < 4) {
	   /***********************/
		/* Test if chronometer */
	   /***********************/

		if ((devpriv->s_BoardInfos.
				dw_MolduleConfiguration[b_ModulNbr] &
				0xFFFF0000UL) == APCI1710_CHRONOMETER) {
	      /***********************************/
			/* Test if chronometer initialised */
	      /***********************************/

			if (devpriv->
				s_ModuleInfo[b_ModulNbr].
				s_ChronoModuleInfo.b_ChronoInit == 1) {
				fpu_begin();

				d_Hour = (double)ul_ChronoValue *(double)
					devpriv->s_ModuleInfo[b_ModulNbr].
					s_ChronoModuleInfo.d_TimingInterval;

				switch (devpriv->
					s_ModuleInfo[b_ModulNbr].
					s_ChronoModuleInfo.b_TimingUnit) {
				case 0:
					d_Hour = d_Hour / (double)1000.0;

				case 1:
					d_Hour = d_Hour / (double)1000.0;

				case 2:
					d_Hour = d_Hour / (double)1000.0;

				case 3:
					d_Hour = d_Hour / (double)60.0;

				case 4:
			    /**********************/
					/* Calculate the hour */
			    /**********************/

					d_Hour = d_Hour / (double)60.0;
					*pul_Hour = (unsigned int) d_Hour;

			    /************************/
					/* Calculate the minute */
			    /************************/

					d_Minute = d_Hour - *pul_Hour;
					d_Minute = d_Minute * 60;
					*pb_Minute = (unsigned char) d_Minute;

			    /************************/
					/* Calculate the second */
			    /************************/

					d_Second = d_Minute - *pb_Minute;
					d_Second = d_Second * 60;
					*pb_Second = (unsigned char) d_Second;

			    /*****************************/
					/* Calculate the mini second */
			    /*****************************/

					d_MilliSecond = d_Second - *pb_Second;
					d_MilliSecond = d_MilliSecond * 1000;
					*pui_MilliSecond = (unsigned int) d_MilliSecond;

			    /******************************/
					/* Calculate the micro second */
			    /******************************/

					d_MicroSecond =
						d_MilliSecond -
						*pui_MilliSecond;
					d_MicroSecond = d_MicroSecond * 1000;
					*pui_MicroSecond = (unsigned int) d_MicroSecond;

			    /******************************/
					/* Calculate the micro second */
			    /******************************/

					d_NanoSecond =
						d_MicroSecond -
						*pui_MicroSecond;
					d_NanoSecond = d_NanoSecond * 1000;
					*pui_NanoSecond = (unsigned int) d_NanoSecond;
					break;
				}

				fpu_end();
			} else {
		 /*******************************/
				/* Chronometer not initialised */
		 /*******************************/
				DPRINTK("Chronometer not initialised\n");
				i_ReturnValue = -4;
			}
		} else {
	      /******************************************/
			/* The module is not a Chronometer module */
	      /******************************************/
			DPRINTK("The module is not a Chronometer module\n");
			i_ReturnValue = -3;
		}
	} else {
	   /***********************/
		/* Module number error */
	   /***********************/
		DPRINTK("Module number error\n");
		i_ReturnValue = -2;
	}

	return i_ReturnValue;
}

/*
+----------------------------------------------------------------------------+
| Function Name     : int i_APCI1710_InsnBitsChronoDigitalIO(struct comedi_device *dev,struct comedi_subdevice *s,
	struct comedi_insn *insn,unsigned int *data)                    |
+----------------------------------------------------------------------------+
| Task              : Sets the output witch has been passed with the         |
|                     parameter b_Channel. Setting an output means setting an|
|                     output high.                                           |
+----------------------------------------------------------------------------+
| Input Parameters  : unsigned char_ b_BoardHandle   : Handle of board APCI-1710      |
|                     unsigned char_ b_ModulNbr      : Selected module number (0 to 3)|
|                     unsigned char_ b_OutputChannel : Selection from digital output  |
|                           CR_CHAN()                  channel (0 to 2)               |
|                                              0 : Channel H                 |
|                                              1 : Channel A                 |
|                                              2 : Channel B                 |
+----------------------------------------------------------------------------+
| Output Parameters : -                                                      |
+----------------------------------------------------------------------------+
| Return Value      :  0: No error                                           |
|                     -1: The handle parameter of the board is wrong         |
|                     -2: Module selection wrong                             |
|                     -3: The module is not a Chronometer module             |
|                     -4: The selected digital output is wrong               |
|                     -5: Chronometer not initialised see function           |
|                         "i_APCI1710_InitChrono"                            |
+----------------------------------------------------------------------------+
*/

/*
+----------------------------------------------------------------------------+
| Function Name     : _INT_ i_APCI1710_SetChronoChlOff                       |
|                               (unsigned char_  b_BoardHandle,                       |
|                                unsigned char_  b_ModulNbr,                          |
|                                unsigned char_  b_OutputChannel)                     |
+----------------------------------------------------------------------------+
| Task              : Resets the output witch has been passed with the       |
|                     parameter b_Channel. Resetting an output means setting |
|                     an output low.                                         |
+----------------------------------------------------------------------------+
| Input Parameters  : unsigned char_ b_BoardHandle   : Handle of board APCI-1710
                        data[0] : Chl ON, Chl OFF , Chl Read , Port Read

|                     unsigned char_ b_ModulNbr  CR_AREF    : Selected module number (0 to 3)|
|                     unsigned char_ b_OutputChannel CR_CHAN : Selection from digital output  |
|                                             channel (0 to 2)               |
|                                              0 : Channel H                 |
|                                              1 : Channel A                 |
|                                              2 : Channel B                 |
+----------------------------------------------------------------------------+
| Output Parameters : -                                                      |
+----------------------------------------------------------------------------+
| Return Value      :  0: No error                                           |
|                     -1: The handle parameter of the board is wrong         |
|                     -2: Module selection wrong                             |
|                     -3: The module is not a Chronometer module             |
|                     -4: The selected digital output is wrong               |
|                     -5: Chronometer not initialised see function           |
|                         "i_APCI1710_InitChrono"                            |
+----------------------------------------------------------------------------+
*/

/*
+----------------------------------------------------------------------------+
| Function Name     : _INT_ i_APCI1710_ReadChronoChlValue                    |
|                               (unsigned char_   b_BoardHandle,                      |
|                                unsigned char_   b_ModulNbr,                         |
|                                unsigned char_   b_InputChannel,                     |
|                                unsigned char *_ pb_ChannelStatus)                    |
+----------------------------------------------------------------------------+
| Task              : Return the status from selected digital input          |
|                     (b_InputChannel) from selected chronometer             |
|                     module (b_ModulNbr).                                   |
+----------------------------------------------------------------------------+
| Input Parameters  : unsigned char_ b_BoardHandle   : Handle of board APCI-1710      |
|                     unsigned char_ b_ModulNbr      : Selected module number (0 to 3)|
|                     unsigned char_ b_InputChannel  : Selection from digital input   |
|                                             channel (0 to 2)               |
|                                   CR_CHAN()             0 : Channel E               |
|                                                1 : Channel F               |
|                                                2 : Channel G               |
+----------------------------------------------------------------------------+
| Output Parameters : unsigned char *_ pb_ChannelStatus : Digital input channel status.|
|                                data[0]                0 : Channel is not active   |
|                                                1 : Channel is active       |
+----------------------------------------------------------------------------+
| Return Value      :  0: No error                                           |
|                     -1: The handle parameter of the board is wrong         |
|                     -2: Module selection wrong                             |
|                     -3: The module is not a Chronometer module             |
|                     -4: The selected digital input is wrong                |
|                     -5: Chronometer not initialised see function           |
|                         "i_APCI1710_InitChrono"                            |
+----------------------------------------------------------------------------+
*/

/*
+----------------------------------------------------------------------------+
| Function Name     : _INT_ i_APCI1710_ReadChronoPortValue                   |
|                               (unsigned char_   b_BoardHandle,                      |
|                                unsigned char_   b_ModulNbr,                         |
|                                unsigned char *_ pb_PortValue)                        |
+----------------------------------------------------------------------------+
| Task              : Return the status from digital inputs port from        |
|                     selected  (b_ModulNbr) chronometer module.             |
+----------------------------------------------------------------------------+
| Input Parameters  : unsigned char_ b_BoardHandle   : Handle of board APCI-1710      |
|                     unsigned char_ b_ModulNbr      : Selected module number (0 to 3)|
+----------------------------------------------------------------------------+
| Output Parameters : unsigned char *_ pb_PortValue   : Digital inputs port status.
|                     data[0]
+----------------------------------------------------------------------------+
| Return Value      :  0: No error                                           |
|                     -1: The handle parameter of the board is wrong         |
|                     -2: Module selection wrong                             |
|                     -3: The module is not a Chronometer module             |
|                     -4: Chronometer not initialised see function           |
|                         "i_APCI1710_InitChrono"                            |
+----------------------------------------------------------------------------+
*/

int i_APCI1710_InsnBitsChronoDigitalIO(struct comedi_device *dev,
	struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data)
{
	int i_ReturnValue = 0;
	unsigned char b_ModulNbr, b_OutputChannel, b_InputChannel, b_IOType;
	unsigned int dw_Status;
	unsigned char *pb_ChannelStatus;
	unsigned char *pb_PortValue;

	b_ModulNbr = CR_AREF(insn->chanspec);
	i_ReturnValue = insn->n;
	b_IOType = (unsigned char) data[0];

	/**************************/
	/* Test the module number */
	/**************************/

	if (b_ModulNbr < 4) {
	   /***********************/
		/* Test if chronometer */
	   /***********************/

		if ((devpriv->s_BoardInfos.
				dw_MolduleConfiguration[b_ModulNbr] &
				0xFFFF0000UL) == APCI1710_CHRONOMETER) {
	      /***********************************/
			/* Test if chronometer initialised */
	      /***********************************/

			if (devpriv->s_ModuleInfo[b_ModulNbr].
				s_ChronoModuleInfo.b_ChronoInit == 1) {
		 /***********************************/
				/* Test the digital output channel */
		 /***********************************/
				switch (b_IOType) {

				case APCI1710_CHRONO_SET_CHANNELOFF:

					b_OutputChannel =
						(unsigned char) CR_CHAN(insn->chanspec);
					if (b_OutputChannel <= 2) {

						outl(0, devpriv->s_BoardInfos.
							ui_Address + 20 +
							(b_OutputChannel * 4) +
							(64 * b_ModulNbr));
					}	/*  if ((b_OutputChannel >= 0) && (b_OutputChannel <= 2)) */
					else {
		    /****************************************/
						/* The selected digital output is wrong */
		    /****************************************/

						DPRINTK("The selected digital output is wrong\n");
						i_ReturnValue = -4;

					}	/*  if ((b_OutputChannel >= 0) && (b_OutputChannel <= 2)) */

					break;

				case APCI1710_CHRONO_SET_CHANNELON:

					b_OutputChannel =
						(unsigned char) CR_CHAN(insn->chanspec);
					if (b_OutputChannel <= 2) {

						outl(1, devpriv->s_BoardInfos.
							ui_Address + 20 +
							(b_OutputChannel * 4) +
							(64 * b_ModulNbr));
					}	/*  if ((b_OutputChannel >= 0) && (b_OutputChannel <= 2)) */
					else {
		    /****************************************/
						/* The selected digital output is wrong */
		    /****************************************/

						DPRINTK("The selected digital output is wrong\n");
						i_ReturnValue = -4;

					}	/*  if ((b_OutputChannel >= 0) && (b_OutputChannel <= 2)) */

					break;

				case APCI1710_CHRONO_READ_CHANNEL:
		 /**********************************/
					/* Test the digital input channel */
		 /**********************************/
					pb_ChannelStatus = (unsigned char *) &data[0];
					b_InputChannel =
						(unsigned char) CR_CHAN(insn->chanspec);

					if (b_InputChannel <= 2) {

						dw_Status =
							inl(devpriv->
							s_BoardInfos.
							ui_Address + 12 +
							(64 * b_ModulNbr));

						*pb_ChannelStatus =
							(unsigned char) (((dw_Status >>
									b_InputChannel)
								& 1) ^ 1);
					}	/*  if ((b_InputChannel >= 0) && (b_InputChannel <= 2)) */
					else {
		    /***************************************/
						/* The selected digital input is wrong */
		    /***************************************/

						DPRINTK("The selected digital input is wrong\n");
						i_ReturnValue = -4;
					}	/*  if ((b_InputChannel >= 0) && (b_InputChannel <= 2)) */

					break;

				case APCI1710_CHRONO_READ_PORT:

					pb_PortValue = (unsigned char *) &data[0];

					dw_Status =
						inl(devpriv->s_BoardInfos.
						ui_Address + 12 +
						(64 * b_ModulNbr));

					*pb_PortValue =
						(unsigned char) ((dw_Status & 0x7) ^ 7);
					break;
				}
			} else {
		 /*******************************/
				/* Chronometer not initialised */
		 /*******************************/

				DPRINTK("Chronometer not initialised\n");
				i_ReturnValue = -5;
			}
		} else {
	      /******************************************/
			/* The module is not a Chronometer module */
	      /******************************************/

			DPRINTK("The module is not a Chronometer module\n");
			i_ReturnValue = -3;
		}
	} else {
	   /***********************/
		/* Module number error */
	   /***********************/

		DPRINTK("Module number error\n");
		i_ReturnValue = -2;
	}

	return i_ReturnValue;
}
