/** @file sample_apci1710_82C54.c

   This demonstrates how to use counter/timer 8254 module in user mode.

   @par CREATION
   @author Addi-Data development team
   @date   30.05.06
   
   @par VERSION
   @verbatim
   $LastChangedRevision:$
   $LastChangedDate:$
   @endverbatim

   @par LICENCE
   @verbatim
    Copyright (C) 2009  ADDI-DATA GmbH for the source code of this module.

    ADDI-DATA GmbH
    Airpark Business Center
    Airport Boulevard B210
    77836 Rheinmünster
    Germany
    Tel: +49(0)7229/1847-0
    Fax: +49(0)7229/1847-200
    http://www.addi-data-com
    info@addi-data.com

   This program is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free Software
   Foundation; either version 2 of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   or FITNESS FOR A PARTICULAR PURPOSE.
   See the GNU General Public License for more details

   You should have received a copy of the GNU General Public License along with
   this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

   You shoud also find the complete GPL in the COPYING file
   accompanying this source code.
   @endverbatim
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

#include <apci1710.h>

#include "sample_apci1710_common.h"

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

// Defines
#define MODULE_NBR 4
#define TIMER_NBR_PER_MODULE 3

// Global data
int gi_fd;                                 // Global filehandle for the board to use
uint8_t gb_ModuleNbr = 0;                  // The module we'll use
volatile sig_atomic_t gb_CompareInterrupt; // Interrupt-Flag
volatile sig_atomic_t gb_Terminate;        // Terminate-Flag
uint32_t int_cnt = 0;
int trigger_on_change = 0;
int latency_measurement = 0;
int set_output_h_with_stueck = 0;
int test_interrupt = 0;
uint8_t interrupt_registered = 0;


/**
 * @brief User interrupt routine for 82C54 module
 * The mask given to the user interrupt routine is determinated as follow :
 *   -----------------------------------------------------------
 *   | Timer |                Mask                             |
 *   -----------------------------------------------------------
 *   |   0   | 0000 0000 0001 0000                             |
 *   |   1   | 0000 0000 0010 0000                             |
 *   |   2   | 0000 0000 0100 0000                             |
 *   -----------------------------------------------------------
 *
 * @param signo Signal number
 */

void interrupt_routine_82c54 (int signo)
{
  int i_ReturnValue = 0;
  uint32_t argArray[3];
  switch(signo)
  {
    case SIGINT: // Handle Ctrl+C
      gb_Terminate = 1;
      break;

    case SIGIO: // Handle 82C54 interrupt
      // Get interrupt information
      for(;;)
      {
        i_ReturnValue = ioctl(gi_fd, CMD_APCI1710_TestInterrupt, argArray);
        if (i_ReturnValue < 0)
        {
          perror("CMD_APCI1710_TestInterrupt");
          break;
        }

        /* Read while pending interrupt data (1: No interrupt, otherwise the interrupt number) */
        if (i_ReturnValue == 1)
          break;

        printf ("Got Interrupt on module %u mask %u value %u\n", argArray[0], argArray[1], argArray[2]);
        int_cnt++; // Increase interrupt counter
      }
  }
}

/**
 * @brief Check and print common error codes for 82C54 functions
 * @param errorCode Error code to check
 * @return 0 on success, error code otherwise.
 * 
 */
void common_82c54_errorCode_check(int errorCode)
{
  switch(errorCode)
  {
    case 1:
      printf ("Error = %d. The handle parameter of the board is wrong\n", errorCode);
      break;
    case 2: 
      printf("Error = %d. The module number is out of range\n",errorCode);
      break;
    case 3: 
      printf("Error = %d. The module is not configured with 82C54 function\n",errorCode);
      break;
    default:
      break;
  }
}

/**
 * @brief Check and print parameter error codes for 82C54 functions
 * @param errorCode Error code to check
 * @param param_error_offset Offset of the first parameter error code
 * @param numberOfParamError Number of parameter error codes
 * @param paramName Array of parameter names
 * 
 */
void param_82c54_errorCode_check(int errorCode, int param_error_offset, int numberOfParamError, const char ** paramName)
{
  int errorParamNb = errorCode - param_error_offset;
  if(errorParamNb >= 0 && errorParamNb < numberOfParamError)
    fprintf(stderr,"Parameter %s is out of range",paramName[errorParamNb]);
}

// --------------------------------------------------------------------------------
/**
 * @brief Get the list of 82C54 modules programmed on the board
 * @param dev Device handle
 * @param module_count Pointer to store the number of 82C54 modules found
 * @param moduleIds Pointer to store the list of 82C54 module IDs found
 */

int get_82c54_modules(int dev, uint8_t *module_count, uint8_t *moduleIds)
{
  int i_ReturnValue = 0;
  uint32_t ul_ModuleIDs[4];
  uint8_t i;
  uint32_t ul_functionality;
  // Check if a correct firmware module is programmed
  i_ReturnValue = ioctl (dev, CMD_APCI1710_GetModulesId, ul_ModuleIDs);

  // Test the return value
  if(i_ReturnValue)
  {
      printf ("CMD_APCI1710_GetModulesId error\n");
      printf ("Error = %d\n", i_ReturnValue);
      perror ("ioctl\n");
      return i_ReturnValue;
  }
  printf ("CMD_APCI1710_GetModulesId OK\n");

  *module_count = 0;
  // Output functionality
  for (i = 0; i < 4; i++)
  {
    ul_functionality = (ul_ModuleIDs[i]>>16) & 0x0000FFFF;
    if(ul_functionality == APCI1710_82X54_TIMER) { 
      moduleIds[*module_count] = i;
      *module_count = *module_count + 1;
      printf("Module no%d is a 82C54 module_count : %d\n",i,*module_count);
    }
  }

  if (*module_count <= 0) 
  { 
    printf("No module is programmed with 82C54 firmware! Call set1710 command!\n");
    return -100;
  }
  return 0;
}
//--------------------------------------------------------------------------------
/**
 * @brief Initialize a 82C54 timer.
 *
 * This function configures one of the timers of an 82C54 module. It sets the timer
 * mode, selects the input clock source, defines signal polarity levels and loads
 * the initial reload value that determines the timing behavior.
 *
 * @param moduleId Module ID to initialize (0 to 3).
 * @param timerId Timer ID to initialize (0, 1 or 2).
 *
 * @param timerMode Timer mode to setup (0 to 5) :
 *
 *   0   Interrupt at the end of the counting process.
 *       At the beginning the output is low. At the end of the counting process the output is put high. 
 *       Then it begins a new counting process at the end of which the output will be switched low, and so on.
 *  
 *   1   Monoflop, retriggerable through hardware.
 *       This mode is the same as the first except that the GATE triggers the timer instead of enabling or disabling it.
 *  
 *   2   Pulse generator.
 *       The output is set to high after the initialization. 
 *       Then when the counter reaches 0 the output is set to low during one clock signal. 
 *       The signal is then put high again and this cycle is repeated.
 *       Time calculation = (dw_ReloadValue + 2) x input clock.
 *  
 *   3   Square-wave generator.
 *       This mode is similar to the mode 2 except that the output is not set to low for one clock signal. 
 *       The counter is reloaded and the output stays to low until it has reached 0 again. This cycle is automatically repeated.
 *  
 *   4   Strobe, Triggered through software.
 *       The signal is initialy put high. When the counter reaches 0 the output is set low for one clock period. 
 *       The signal is then reset to high. The counting sequence is triggered when a new value is written in.
 *       When a new value is written in during a counting cycle, this value will be loaded at the next clock pulse.
 *  
 *   5   Strobe, trigger through hardware (retriggerable).
 *       This mode is the same as the fourth except that the GATE is used to trigger the timer.
 *
 * @param inputClockSelection Input clock source:  
 *        0 → PCI bus clock  
 *        1 → External clock  
 *        2 → Internal 10 MHz clock  
 *        (Timer 0 only supports 0 and 2)
 *
 * @param inputClockLevel Input clock active level  
 *        0 → Active low  
 *        1 → Active high (inverted)
 *
 * @param outputLevel Output active level  
 *        0 → Active low  
 *        1 → Active high (inverted)
 *
 * @param hardwareGateLevel Hardware gate active level  
 *        0 → Active low  
 *        1 → Active high (inverted)  
 *        Must be 0 if the external gate is not used.
 *
 * @param reloadValue Timer reload value (0 to 4 294 967 295).
 *
 * @return 0 on success, error code otherwise.
 */

int init_82c54 (uint8_t moduleId,                
                uint8_t timerId,                
                uint8_t timerMode,
                uint8_t inputClockSelection,
                uint8_t inputClockLevel,        
                uint8_t outputLevel,            
                uint8_t hardwareGateLevel,      
                uint32_t reloadValue)           
{
  int i_ReturnValue = 0;
  const char *errorParamString[] = {"timerId",
                              "timerMode",
                              "inputClk",
                              "inputClkLevel",
                              "outputLevel",
                              "hardGate"};
  uint32_t dw_ArgArray[8];

  gb_CompareInterrupt = 0;
  gb_Terminate = 0;

  // Initialise the IDV module
  dw_ArgArray[0] = (uint32_t)moduleId;
  dw_ArgArray[1] = (uint32_t)timerId;
  dw_ArgArray[2] = (uint32_t)timerMode;
  dw_ArgArray[3] = (uint32_t)inputClockSelection;
  dw_ArgArray[4] = (uint32_t)inputClockLevel;
  dw_ArgArray[5] = (uint32_t)outputLevel;
  dw_ArgArray[6] = ((dw_ArgArray[2]&0x3) == 1)?(uint32_t)hardwareGateLevel : 0;
  dw_ArgArray[7] = reloadValue;

  if(dw_ArgArray[1]==0 && dw_ArgArray[3] == 1)
  {
    fprintf(stderr,"Init configuration error : timer 0 cannot use external clock\n");
    exit(1);
  }

  i_ReturnValue = ioctl (gi_fd, CMD_APCI1710_InitTimer, dw_ArgArray);
  if(i_ReturnValue)
  {
    if(i_ReturnValue == -1)
    {
      perror("ioctl\n");
      return -1;
    }
    fprintf(stderr,"CMD_APCI1710_InitTimer return with error code %d\n",i_ReturnValue);
    common_82c54_errorCode_check(i_ReturnValue);
    param_82c54_errorCode_check(i_ReturnValue,4,6,errorParamString);
    return i_ReturnValue;
  }
  else
  {
    printf ("CMD_APCI1710_InitTimer OK\n");
  }

  return 0;
}

/**
 * @brief Enable a 82C54 timer.
 * This function starts the counting process of a previously initialized timer.
 * 
 * @param moduleId Module ID to initialize (0 to 3).
 * @param timerId Timer ID to initialize (0, 1 or 2).
 * @param interruptEnable Enable/Disable interrupt (0 or 1).
 */
int enableTimer_82C54(uint8_t moduleId,
                        uint8_t timerId,
                        uint8_t interruptEnable)
{
  int i_ReturnValue = 0;
  const char *errorParamString[] = {"timerId"};
  uint8_t argArray[3]={ moduleId, timerId, interruptEnable};
  uint8_t oflags = 0;
  //Enable IRQ if necessary
  if(interruptEnable == 1 && interrupt_registered == 0)
  {
    // Install the user interrupt routine
    i_ReturnValue = ioctl (gi_fd, CMD_APCI1710_SetBoardIntRoutine);
    // Test the return value
    if(i_ReturnValue)
    {
      printf ("CMD_APCI1710_SetBoardIntRoutine error\n");

      switch(i_ReturnValue)
      {
        case 1:
          printf ("Error = %d. The handle parameter of the board is wrong\n", i_ReturnValue);
        break;

        case 2:
          printf ("Error = %d. No IDV module found\n", i_ReturnValue);
        break;

        case 3:
          printf ("Error = %d. IDV not initialised - see function CMD_APCI1710_InitIdv\n", i_ReturnValue);
        break;

        default:
          printf ("Error = %d\n", i_ReturnValue);
          perror ("ioctl\n");
        break;
      }
      return 100+i_ReturnValue;
    }
    else
    {
      interrupt_registered = 1;
      printf ("CMD_APCI1710_SetBoardIntRoutine OK\n");
    }
    // Request asynchronous SIGIO
    if (signal(SIGIO, &interrupt_routine_82c54) == SIG_ERR) 
    {
      perror ("signal\n");
      return 200;
    }

    if (fcntl(gi_fd, F_SETOWN, getpid()) == -1) 
    {
      perror ("fcntl\n");
      return 201;
    }

    oflags = fcntl(gi_fd, F_GETFL);
    if ( fcntl(gi_fd, F_SETFL, oflags | FASYNC) == -1) 
    {
      perror ("fcntl\n");
      return 202;
    }
  }

  i_ReturnValue = ioctl (gi_fd, CMD_APCI1710_EnableTimer, argArray);
  if(i_ReturnValue)
  {
    fprintf(stderr,"CMD_APCI1710_EnableTimer return with error code %d\n",i_ReturnValue);
    common_82c54_errorCode_check(i_ReturnValue);
    if(i_ReturnValue == 4)
      fprintf(stderr,"timer %d of module no.%d  cannot be enabled because it has not been initialized\n", timerId, moduleId);
    param_82c54_errorCode_check(i_ReturnValue,5,1,errorParamString);
    return i_ReturnValue;
  }
  else
  {
    printf ("CMD_APCI1710_EnableTimer OK\n");
  }
  return 0;
}
//--------------------------------------------------------------------------------

/**
 * @brief Read the current value of a 82C54 timer.
 * 
 * @param moduleId Module ID to initialize (0 to 3).
 * @param timerId Timer ID to initialize (0, 1 or 2).
 * @param value Pointer to store the timer value.
 * @return 0 on success, error code otherwise.
 */
int readTimer_82C54(uint8_t moduleId,
                    uint8_t timerId,          
                    uint32_t *value)             
{
  int i_ReturnValue = 0;
  const char *errorParamString[] = {"timerId"};
  uint32_t argArray[3]={(uint32_t)moduleId,(uint32_t) timerId, -1};
  i_ReturnValue = ioctl (gi_fd, CMD_APCI1710_ReadTimerValue, argArray);
  if(i_ReturnValue)
  {
    fprintf(stderr,"CMD_APCI1710_ReadTimerValue return with error code %d\n",i_ReturnValue);
    common_82c54_errorCode_check(i_ReturnValue);
    param_82c54_errorCode_check(i_ReturnValue,4,1,errorParamString);
    if(i_ReturnValue == 5)
      fprintf(stderr,"timer %d of module no.%d  cannot be enabled because it has not been initialized\n", timerId, moduleId);
    return i_ReturnValue;
  }
  else
  {
    printf ("CMD_APCI1710_ReadTimer OK\n");
  }
  *value = argArray[2];
  return 0;
}
//--------------------------------------------------------------------------------

/**
 * @brief Get the progress status of a 82C54 timer.
 * @param moduleId Module ID to initialize (0 to 3).
 * @param timerId Timer ID to initialize (0, 1 or 2).
 * @param status Pointer to store the timer progress status.
 * @return 0 on success, error code otherwise.
 */
int getTimerProgressStatus_82C54(uint8_t moduleId,
                                 uint8_t timerId,       
                                 uint8_t *status)               
{
  int i_ReturnValue = 0;
  const char *errorParamString[] = {"timerId"};
  uint8_t argArray[3]={moduleId, timerId, -1};
  i_ReturnValue = ioctl (gi_fd, CMD_APCI1710_GetTimerProgressStatus, argArray);
  if(i_ReturnValue)
  {
    fprintf(stderr,"CMD_APCI1710_GetTimerProgressStatus return with error code %d\n",i_ReturnValue);
    common_82c54_errorCode_check(i_ReturnValue);
    param_82c54_errorCode_check(i_ReturnValue,4,1,errorParamString);
    if(i_ReturnValue == 5)
      fprintf(stderr,"timer %d of module no.%d  cannot be enabled because it has not been initialized\n", timerId, moduleId);
    return i_ReturnValue;
  }
  else
  {
    printf ("CMD_APCI1710_GetTimerProgressStatus OK\n");
  }
  *status = argArray[2];
  return 0;
}
//--------------------------------------------------------------------------------

/**
 * @brief Get the progress status (GATE) of a 82C54 timer.
 * @param moduleId Module ID to initialize (0 to 3).
 * @param timerId Timer ID to initialize (0, 1 or 2).
 * @param status Pointer to store the timer progress status.
 * @return 0 on success, error code otherwise.
 */
int getTimerProgressStatusEx_82C54(uint8_t moduleId,               
                                   uint8_t timerId,               
                                   uint8_t *status)   
{
  int i_ReturnValue = 0;
  const char *errorParamString[] = {"timerId"};
  uint8_t argArray[3]={moduleId, timerId, -1};
  i_ReturnValue = ioctl (gi_fd, CMD_APCI1710_GetTimerProgressStatusEx, argArray);
  if(i_ReturnValue)
  {
    fprintf(stderr,"CMD_APCI1710_GetTimerProgressStatusEx return with error code %d\n",i_ReturnValue);
    common_82c54_errorCode_check(i_ReturnValue);
    param_82c54_errorCode_check(i_ReturnValue,4,1,errorParamString);
    if(i_ReturnValue == 5)
      fprintf(stderr,"timer %d of module no.%d  cannot be enabled because it has not been initialized\n", timerId, moduleId);
    return i_ReturnValue;
  }
  else
  {
    printf ("CMD_APCI1710_GetTimerProgressStatuEx OK\n");
  }
  *status = argArray[2];
  return 0;
}
//--------------------------------------------------------------------------------

/**
 * @brief Close the 82C54 module.
 * This function disables all timers of the specified 82C54 module and resets the board interrupt routine if it was registered.
 * @param moduleId Module ID to close (0 to 3).
 * @return 0 on success, error code otherwise.
 */
int close_82C54(uint8_t moduleId)
{
  int i_ReturnValue = 0;
  int i_ReturnValueBis = 0;
  int i=0;
  uint8_t argArray[2]={moduleId,0};

  for(i = 0;i < TIMER_NBR_PER_MODULE; i++) // Disable all the Timer of the module
  {
    argArray[1]=i;
    i_ReturnValue = ioctl (gi_fd, CMD_APCI1710_DisableTimer, argArray);
    if(!i_ReturnValue)
    {
      printf("Module no.%d Timer no.%d has been stopped \n",moduleId, i);
    }
    i_ReturnValue = (i_ReturnValue == 5) ? 0 : i_ReturnValue; // ignore uninitialized mnodule since we have not checked which were intialize

    if(i_ReturnValue)
    {
      fprintf(stderr,"CMD_APCI1710_DisableTimer has returned an error no%d :module no%d timer no%d\n", i_ReturnValue, moduleId, i);
      common_82c54_errorCode_check(i_ReturnValue);
    }
  } 
  // Reset the board interrupt routine
  if(!interrupt_registered)
  {
    return 0;
  }

  i_ReturnValueBis = ioctl (gi_fd, CMD_APCI1710_ResetBoardIntRoutine);

  // Test the return value
  switch (i_ReturnValueBis)
  {
    case 0:
      printf ("CMD_APCI1710_ResetBoardIntRoutine OK\n");
      return i_ReturnValue;
    break;

    case 1:
      printf ("CMD_APCI1710_ResetBoardIntRoutine error\n");
      printf ("Error = %d. The handle parameter of the board is wrong\n", i_ReturnValueBis);
      return i_ReturnValueBis;
    break;

    default:
      printf ("CMD_APCI1710_ResetBoardIntRoutine error\n");
      printf ("Error = %d\n", i_ReturnValueBis);
      perror ("ioctl\n");
      return i_ReturnValueBis;
    break;
  }
}

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

int main(int argc, char **argv)
{
  int *apci1710_card_fd = NULL;
  unsigned int apci1710_card_number = 0;

  uint8_t interrupt_enable = 0;
  uint8_t deviceId = 0;
  uint8_t moduleMask = 1;
  uint8_t timerMask = 1;
  uint8_t timerMode = 0;
  uint8_t clkInput = 0;
  uint8_t logicLevels = 0;
  uint32_t reloadValue = 1E7;//10M

  uint32_t value = 0;//10M
  uint8_t  status= 0;

  uint8_t module_82c54_count  = 0;
  uint8_t module_82c54[MODULE_NBR]     = {0,0,0,0};

  int i,j,k; 
  // Read in options
  int option;
  int arg;
  while((option = getopt (argc, argv, "i::m:d:t:r:c:l:M:h")) >=0) {
    switch (option) {
      // -i [interruptEnable]
      case 'i':
        if(optarg)
          interrupt_enable = atoi(optarg) ? 0 : 1;
        else
          interrupt_enable = 1;
        break;
      // -d deviceId
      case 'd':
        if(optarg == 0)
          goto opt_help;
        arg = atoi(optarg);
        deviceId = (uint8_t) arg;
        break;
      // -m moduleMask
      case 'm':
        if(optarg == 0)
          goto opt_help;
        arg = atoi(optarg);
        moduleMask = (uint8_t) (arg & ((1<<(MODULE_NBR+1))-1));
        break;
      // -t timerMask
      case 't':
        if(optarg == 0)
          goto opt_help;
        arg = atoi(optarg);
        timerMask = (uint8_t) (arg & ((1<<(TIMER_NBR_PER_MODULE+1))-1));
        break;
      // -r reloadValue
      case 'r':
        if(optarg == 0)
          goto opt_help;
        arg = atoi(optarg);
        reloadValue = (uint32_t) arg;
        break;
      // -c clkInput
      case 'c':
        if(optarg == 0)
          goto opt_help;
        arg = atoi(optarg);
        clkInput = (uint8_t) (arg & 0x3);
        break;
      // -l logicLevels
      case 'l':
        if(optarg == 0)
          goto opt_help;
        arg = atoi(optarg);
        logicLevels = (uint8_t) (arg & 0x7);
        break;
      // -M timerMode
      case 'M':
        if(optarg == 0)
          goto opt_help;
        arg = atoi(optarg);
        if(arg > 5 || arg < 0)
        {
          fprintf(stderr,"timer mode should be a integer in 0-5 range\n");
          exit(EXIT_FAILURE);
        }
        timerMode = (uint8_t) arg; 
        break;
      case '?':
        fprintf (stderr, "Unknown Option -%c or it needs an argument\n", optopt);
      case 'h':
      default:
opt_help:
        fprintf(stdout, "Usage: %s -[i[interruptEnable]] [h] [m moduleMask] [t timerMask] [r reloadValue] [c clkInput] [l logicLevels]\n", argv[0]);
        fprintf(stdout, "-i [interrutEnable]  : If arg is defined at 0 disable interrupt otherwise enable it (even if no arg is defined)\n");
        fprintf(stdout, "-h                   : Help\n");
        fprintf(stdout, "-m moduleMask        : If an arg is defined use it as mask applied to the 82C54 found modules\n");
        for(i=0;i < MODULE_NBR ;i++)
          fprintf(stdout, "                         Bit%d: Use Module no%d\n", i, i);
        fprintf(stdout, "                       If no arg is defined sample will use only the first found 82C54 module(Default option)\n");
        fprintf(stdout, "-d deviceId          : By default board apci1710 with id 0 is used. It could be changed it by indicating deviceId with this opt.\n");
        fprintf(stdout, "-t timerMask         : If an arg is defined use it as mask applied to the timer(all used module will have same timer used)\n");
        for(i=0;i < TIMER_NBR_PER_MODULE ;i++)
          fprintf(stdout, "                         Bit%d: Use Timer no%d\n", i, i);
        fprintf(stdout, "                       If no arg is defined sample will use only the first timer of each enabled module\n");
        fprintf(stdout, "-r reloadValue       : Set reloadValue to all Timer enabled(default is 10 000 000) in uint32_t format\n");
        fprintf(stdout, "-M timerMode         : Timer mode to used for all timers for all enabled modules (default is 0)\n");
        fprintf(stdout, "                       - 0   Interrupt at the end of the counting process.\n");
        fprintf(stdout, "                           At the beginning the output is low. At the end of the counting process the output is put high.\n");
        fprintf(stdout, "                           Then it begins a new counting process at the end of which the output will be switched low, and so on.\n");
        fprintf(stdout, "\n");
        fprintf(stdout, "                       - 1   Monoflop, retriggerable through hardware.\n");
        fprintf(stdout, "                           This mode is the same as the first except that the GATE triggers the timer instead of enabling or disabling it.\n");
        fprintf(stdout, "\n");
        fprintf(stdout, "                       - 2   Pulse generator.\n");
        fprintf(stdout, "                           The output is set to high after the initialization. \n");
        fprintf(stdout, "                           Then when the counter reaches 0 the output is set to low during one clock signal. \n");
        fprintf(stdout, "                           The signal is then put high again and this cycle is repeated.\n");
        fprintf(stdout, "                           Time calculation = (dw_ReloadValue + 2) x input clock.\n");
        fprintf(stdout, "\n");
        fprintf(stdout, "                       - 3   Square-wave generator.\n");
        fprintf(stdout, "                           This mode is similar to the mode 2 except that the output is not set to low for one clock signal. \n");
        fprintf(stdout, "                           The counter is reloaded and the output stays to low until it has reached 0 again. This cycle is automatically repeated.\n");
        fprintf(stdout, "\n");
        fprintf(stdout, "                       - 4   Strobe, Triggered through software.\n");
        fprintf(stdout, "                           The signal is initialy put high. When the counter reaches 0 the output is set low for one clock period. \n");
        fprintf(stdout, "                           The signal is then reset to high. The counting sequence is triggered when a new value is written in.\n");
        fprintf(stdout, "                           When a new value is written in during a counting cycle, this value will be loaded at the next clock pulse.\n");
        fprintf(stdout, "\n");
        fprintf(stdout, "                       - 5   Strobe, trigger through hardware (retriggerable).\n");
        fprintf(stdout, "                           This mode is the same as the fourth except that the GATE is used to trigger the timer. \n");
        fprintf(stdout, "\n");
        fprintf(stdout, "-c clkInput          : Clock Input to use for all timers for all enabled modules\n");
        fprintf(stdout, "                         0-> PCI bus Clock(default option) 1->external clock 2-> internal 10MHz clock).\n");
        fprintf(stdout, "-l logicLevels       : logic levels to use (by default all are at 0):\n");
        fprintf(stdout, "                       - Bit0: inputClockLevel,        (0-> active at low level 1->active at High level (input inverted))\n");
        fprintf(stdout, "                       - Bit1: outputLevel,            (0-> active at low level 1->active at High level (input inverted))\n");
        fprintf(stdout, "                       - Bit2: hardwareGateLevel,      (0-> active at low level (input inverted) 1->active at High level )\n");
        (option == 'h')? exit(EXIT_SUCCESS) : exit(EXIT_FAILURE);
    }
  }

  // Before we do anything we have to ensure, that Ctrl+C is handled to terminate this program the right way
  if (signal(SIGTERM, &interrupt_routine_82c54) == SIG_ERR) {
    perror ("signal\n");
  }

  signal(SIGINT, &interrupt_routine_82c54);

  apci1710_card_number = apci1710_find_cards (&apci1710_card_fd);

  printf("total: %d cards\n", apci1710_card_number);

  if(deviceId+1 > apci1710_card_number)
    exit(EXIT_FAILURE);

  gi_fd = apci1710_card_fd[deviceId];

  if(!gi_fd)
    exit(EXIT_FAILURE);


  if(get_82c54_modules(gi_fd, &module_82c54_count, module_82c54))
  {
    exit(EXIT_FAILURE);
  }
  for(i = 0;i < MODULE_NBR; i++)
  {
    if(((1<<i) & moduleMask)==0)
      continue;
    for(j = 0;j < module_82c54_count+1; j++)
    {
      if( j == module_82c54_count)
      {
        fprintf(stdout,"Warning module no.%d was chosen but it is not programmed as 82C54 module\n",i);
        moduleMask &= ~(1<<i); //remove it from the mask
      }

      if(((1<<module_82c54[j]) & moduleMask) != 0)
      {
        break;
      }
    }
  }

  for(i = 0;i < MODULE_NBR;i++)
  {
    if(((1<<i) & moduleMask)==0)
      continue;
    for(k = 0;k < TIMER_NBR_PER_MODULE; k++) 
    {
      if(((1<<k) & timerMask)==0)
        continue;

      if(init_82c54(i,
                    k, 
                    timerMode, 
                    clkInput,
                    logicLevels &1, 
                    (logicLevels &(1<<1))>>1, 
                    (logicLevels &(1<<2))>>2, 
                     reloadValue))
      {
        fprintf(stderr,"Error Init Module no.%d timer no.%d\n",i,k);
        goto clean_up;
      }
      if(enableTimer_82C54( i, k, interrupt_enable))
      {
        fprintf(stderr,"Error Read Module no.%d timer no.%d\n",i,k);
        goto clean_up;
      }
      else
      {
        printf("Module no.%d timer no.%d has been just started\n",i,k);
      }
      if(readTimer_82C54(i, k, &value))
      {
        fprintf(stderr,"Error Read Module no.%d timer no.%d\n",i,k);
        goto clean_up;
      }
      else
      {
        printf("Module no.%d timer no.%d value read back reload value : %u\n",i,k,value);
      }
    }
  }   
  // Run the program (stop with Ctrl+C)
  while ( !gb_Terminate ) // Alternatively we can use pselect() or something else to wait for a signal
  { 
    if(interrupt_registered == 0)
    {
      for(i = 0;i < MODULE_NBR;i++)
      {
        if(((1<<i) & moduleMask)==0)
          continue;
        for(k = 0;k < TIMER_NBR_PER_MODULE; k++) 
        {
          if(((1<<k) & timerMask)==0)
            continue;

          if(readTimer_82C54( i, k, &value))
          {
            fprintf(stderr,"Error Read Module no.%d timer no.%d\n",i,k);
            goto clean_up;
          }
          else
          {
            printf("Module no.%d timer no.%d value red back for reload value is %u\n",i,k,value);
          }
          
          if(getTimerProgressStatus_82C54( i, k, &status))
          {
            fprintf(stderr,"Error Getting progress status Module no.%d timer no.%d\n",i,k);
            goto clean_up;
          }
          else
          {
            printf("Module no.%d timer no.%d progress status value is %u\n",i,k,status);
          }

          if(getTimerProgressStatusEx_82C54( i, k, &status))
          {
            fprintf(stderr,"Error Getting progress status (GATE) Module no.%d timer no.%d\n",i,k);
            goto clean_up;
          }
          else
          {
            printf("Module no.%d timer no.%d progress status (GATE) value is %u\n",i,k,status);
          }
        }
      }   
    }
    if (gb_CompareInterrupt)
    {
      // Let's do something

      gb_CompareInterrupt = 0;
    }
    else
    {
      sleep(10);
    }
  }

clean_up:
  // Close
  for(i = 0;i < MODULE_NBR;i++)
  {
    if(((1<<i) & moduleMask)==0)
      continue;
    if(close_82C54(i))
      fprintf(stderr,"Error disable Module no%d \n",i);
  }
  printf ("\nProgram concluded.\n");

  return 0;
}
