added patch from Klaas Gadeyne, with my own modifications.
authorFrank Mori Hess <fmhess@speakeasy.net>
Sun, 23 Nov 2003 18:55:48 +0000 (18:55 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Sun, 23 Nov 2003 18:55:48 +0000 (18:55 +0000)
comedi/drivers/comedi_counter_unstable.h [new file with mode: 0644]
comedi/drivers/ni_660x.c

diff --git a/comedi/drivers/comedi_counter_unstable.h b/comedi/drivers/comedi_counter_unstable.h
new file mode 100644 (file)
index 0000000..7e802fa
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+    comedi_counter_unstable.h
+
+    This is for the development of comedi's counter API.  Any API
+    described here should be considered under development and
+    subject to change!  Eventually, this should get merged into
+    comedi.h when consensus is reached on a good API.
+
+    Copyright (C) 2003 Frank Mori Hess
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+************************************************************************/
+
+/* ALPHA counter stuff */
+/*
+
+ID=INSN_CONFIG_COUNTER_ALPHA: counter configuration
+
+  The subdevice has an accumulator which changes based on
+  triggers.
+
+  [0] - ID
+  [1] - trigger0 (comedi_counter_trigger_bits() is helpful)
+  [2] - action0 (action associated with trigger0, see comedi_counter_actions
+       for possibilities)
+  [3] - trigger1
+  [4] - action 1
+  ....
+
+  You can specify as many trigger/action pairs as you like.
+
+  Example Modes:
+    Up counter, counting falling edges on input 0
+
+       data[1] = comedi_counter_trigger_bits(0, CR_EDGE | CR_INVERT);
+       data[2] = COMEDI_INC_ACCUMULATOR;
+
+    Down counter, counting rising edges on input 0, and using input 1 as a gate
+    (input 1 must be high to count):
+
+       data[1] = comedi_counter_trigger_bits(0, CR_EDGE) | comedi_counter_trigger_bits(1, 0);
+       data[2] = COMEDI_DEC_ACCUMULATOR;
+
+    Quadrature x1 encoding:
+       data[1] = comedi_counter_trigger_bits(0, CR_EDGE) |
+               comedi_counter_trigger_bits(1, CR_INVERT);
+       data[2] = COMEDI_INC_ACCUMULATOR;
+       data[3] = comedi_counter_trigger_bits(0, CR_EDGE | CR_INVERT) |
+               comedi_counter_trigger_bits(1, CR_INVERT);
+       data[4] = COMEDI_DEC_ACCUMULATOR;
+
+  Notes:
+       Could add fields for specifying what inputs are connected to (input 0 connected
+       to external input pin, or internal oscillator?).
+
+       Could make triggers/actions more involved, for example "trigger on accumulator
+       reaches 0", or specify a value to reload the counter with for the
+       COMEDI_RESET_ACCUMULATOR action.  Could add an action that sets the counter's
+       output, like "pulse output when accumulator reaches 0".
+
+       Using this instruction is a bit of a pain.  On the user-end, the user has
+       to figure out exactly how to describe what her counter is doing in order to
+       program it, even if the counter only supports a few different modes of
+       operation.  This could be
+       eased by providing helper functions that fill out an insn with the
+       appropriate values for particular pieces of hardware.  On the driver end,
+       it might make things easier if we provided some functions for operations
+       on sets of trigger/action pairs.  For example, queries like "is A a subset
+       of B" where A and B are sets of trigger/action pairs.
+*/
+#define INSN_CONFIG_COUNTER_ALPHA              0x1000
+
+static inline int comedi_counter_trigger_bits(unsigned int input_num, int flags)
+{
+       static const int low_bit = 0x1;
+       static const int edge_bit = 0x2;
+       static const int valid_bit = 0x3;
+       static const int bits_per_channel = 3;
+       int bits = valid_bit;
+
+       if(flags & CR_EDGE)
+               bits |= edge_bit;
+       if(flags & CR_INVERT)
+               bits |= low_bit;
+       return bits << (input_num * bits_per_channel);
+}
+enum comedi_counter_actions
+{
+       COMEDI_INC_ACCUMULATOR,
+       COMEDI_DEC_ACCUMULATOR,
+       COMEDI_RESET_ACCUMULATOR,
+};
index 06e78ba400c9be7767ff075212b2bfbd0290de07..acc3f51892213fded49079769302c0e5b1f665d4 100644 (file)
 /*
-    comedi/drivers/ni_660x.c
-    Hardware driver for NI 660x devices
-
-    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+  comedi/drivers/ni_660x.c
+  Hardware driver for NI 660x devices
+
+  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /*
 Driver: ni_660x.o
-Description: National Instruments 660x
-Author: J.P. Mellor <jpmellor@rose-hulman.edu>
-Updated: 
-Devices: [National Instruments] PCI-6601 (ni_660x), PCI-6602
-Status: unknown
+Description: National Instruments 660x counter/timer boards
+Devices:
+[National Instruments] PCI-6601 (ni_660x), PCI-6602
+Author: J.P. Mellor <jpmellor@rose-hulman.edu>,
+       Herman.Bruyninckx@mech.kuleuven.ac.be,
+       Wim.Meeussen@mech.kuleuven.ac.be,
+       Klaas.Gadeyne@mech.kuleuven.ac.be,
+       Frank Mori Hess <fmhess@users.sourceforge.net>
+Updated: Sun Nov 16 18:46:11 UTC 2003
+Status: experimental
+
+Encoders work, but only with instructions, commands are not
+supported yet.  PulseGeneration (both single pulse and pulse train)
+works. DIO is experimental (8 channels only). Interrupts do not
+work.
+
+The counter API is unstable, see comedi_counter_unstable.h and
+the directory comedi/Documentation/comedi/ (the files counter-spec and
+insn_config).
+
+References:
+DAQ 660x Register-Level Programmer Manual  (NI 370505A-01)
+DAQ 6601/6602 User Manual (NI 322137B-01)
+
+Things to do:
+- Add DMA support (see mite.c and ni_pcidio.c for examples)
+- Add commands (copy from ni_pcidio.c ?)
+- Add interrupts
+- Extend "Application possibilities" for the GPCT subdevice (eg. Time
+Measurement, ...)
+*/
 
-Commands are not supported.
+#include <linux/comedidev.h>
+#include "mite.h"
+#include "comedi_counter_unstable.h"
+
+#define CTRS_PER_CHIP 4 // The number of counters per ni-tio chip
+#define DATA_1B 0x1 // 1 byte = 8 bits data
+#define DATA_2B 0x2 // 2 bytes = 16 bit data
+#define DATA_4B 0x4 // 4 bytes = 32 bit data
+
+/* See Register-Level Programmer Manual page 3.1 */
+typedef enum
+  {
+    G0InterruptAcknowledge,
+    G0StatusRegister,
+    G1InterruptAcknowledge,
+    G1StatusRegister,
+    G01StatusRegister,
+    G0CommandRegister,
+    G1CommandRegister,
+    G0HWSaveRegister,
+    G1HWSaveRegister,
+    G0SWSaveRegister,
+    G1SWSaveRegister,
+    G0ModeRegister,
+    G01JointStatus1Register,
+    G1ModeRegister,
+    G0LoadARegister,
+    G01JointStatus2Register,
+    G0LoadBRegister,
+    G1LoadARegister,
+    G1LoadBRegister,
+    G0InputSelectRegister,
+    G1InputSelectRegister,
+    G01JointResetRegister,
+    G0InterruptEnable,
+    G1InterruptEnable,
+    G0CountingModeRegister,
+    G1CountingModeRegister,
+    G0SecondGateRegister,
+    G1SecondGateRegister,
+    G0DMAConfigRegister,
+    G0DMAStatusRegister,
+    G1DMAConfigRegister,
+    G1DMAStatusRegister,
+    G2InterruptAcknowledge,
+    G2StatusRegister,
+    G3InterruptAcknowledge,
+    G3StatusRegister,
+    G23StatusRegister,
+    G2CommandRegister,
+    G3CommandRegister,
+    G2HWSaveRegister,
+    G3HWSaveRegister,
+    G2SWSaveRegister,
+    G3SWSaveRegister,
+    G2ModeRegister,
+    G23JointStatus1Register,
+    G3ModeRegister,
+    G2LoadARegister,
+    G23JointStatus2Register,
+    G2LoadBRegister,
+    G3LoadARegister,
+    G3LoadBRegister,
+    G2InputSelectRegister,
+    G3InputSelectRegister,
+    G23JointResetRegister,
+    G2InterruptEnable,
+    G3InterruptEnable,
+    G2CountingModeRegister,
+    G3CountingModeRegister,
+    G3SecondGateRegister,
+    G2SecondGateRegister,
+    G2DMAConfigRegister,
+    G2DMAStatusRegister,
+    G3DMAConfigRegister,
+    G3DMAStatusRegister,
+    ClockConfigRegister,
+    IOConfigReg0_3,
+    IOConfigReg4_7,
+    IOConfigReg8_11,
+    IOConfigReg12_15,
+    IOConfigReg16_19,
+    IOConfigReg20_23,
+    IOConfigReg24_27,
+    IOConfigReg28_31,
+    IOConfigReg32_35,
+    IOConfigReg36_39,
+    STCDIOParallelInput,
+    STCDIOOutput,
+    STCDIOControl,
+    STCDIOSerialInput,
+    NumRegisters,
+  } NI_660xRegisters;
+static inline int GxCommandRegister(int counter_channel)
+{
+       switch(counter_channel)
+       {
+       case 0:
+               return G0CommandRegister;
+               break;
+       case 1:
+               return G1CommandRegister;
+               break;
+       case 2:
+               return G2CommandRegister;
+               break;
+       case 3:
+               return G3CommandRegister;
+               break;
+       default:
+               DPRINTK("ni_660x: bug!, line %i\n", __LINE__);
+               break;
+       }
+       return 0;
+}
+static inline int GxCountingModeRegister(int counter_channel)
+{
+       switch(counter_channel)
+       {
+       case 0:
+               return G0CountingModeRegister;
+               break;
+       case 1:
+               return G1CountingModeRegister;
+               break;
+       case 2:
+               return G2CountingModeRegister;
+               break;
+       case 3:
+               return G3CountingModeRegister;
+               break;
+       default:
+               DPRINTK("ni_660x: bug!, line %i\n", __LINE__);
+               break;
+       }
+       return 0;
+}
+static inline int GxInputSelectRegister(int counter_channel)
+{
+       switch(counter_channel)
+       {
+       case 0:
+               return G0InputSelectRegister;
+               break;
+       case 1:
+               return G1InputSelectRegister;
+               break;
+       case 2:
+               return G2InputSelectRegister;
+               break;
+       case 3:
+               return G3InputSelectRegister;
+               break;
+       default:
+               DPRINTK("ni_660x: bug!, line %i\n", __LINE__);
+               break;
+       }
+       return 0;
+}
+static inline int GxxJointResetRegister(int counter_channel)
+{
+       switch(counter_channel)
+       {
+       case 0:
+       case 1:
+               return G01JointResetRegister;
+               break;
+       case 2:
+       case 3:
+               return G23JointResetRegister;
+               break;
+       default:
+               DPRINTK("ni_660x.c: bug!, line %i", __LINE__);
+               break;
+       }
+       return 0;
+}
+static inline int GxLoadARegister(int counter_channel)
+{
+       switch(counter_channel)
+       {
+       case 0:
+               return G0LoadARegister;
+               break;
+       case 1:
+               return G1LoadARegister;
+               break;
+       case 2:
+               return G2LoadARegister;
+               break;
+       case 3:
+               return G3LoadARegister;
+               break;
+       default:
+               DPRINTK("ni_660x: bug!, line %i\n", __LINE__);
+               break;
+       }
+       return 0;
+}
+static inline int GxLoadBRegister(int counter_channel)
+{
+       switch(counter_channel)
+       {
+       case 0:
+               return G0LoadBRegister;
+               break;
+       case 1:
+               return G1LoadBRegister;
+               break;
+       case 2:
+               return G2LoadBRegister;
+               break;
+       case 3:
+               return G3LoadBRegister;
+               break;
+       default:
+               DPRINTK("ni_660x: bug!, line %i\n", __LINE__);
+               break;
+       }
+       return 0;
+}
+static inline int GxModeRegister(int counter_channel)
+{
+       switch(counter_channel)
+       {
+       case 0:
+               return G0ModeRegister;
+               break;
+       case 1:
+               return G1ModeRegister;
+               break;
+       case 2:
+               return G2ModeRegister;
+               break;
+       case 3:
+               return G3ModeRegister;
+               break;
+       default:
+               DPRINTK("ni_660x: bug!, line %i\n", __LINE__);
+               break;
+       }
+       return 0;
+}
+static inline int GxSWSaveRegister(int counter_channel)
+{
+       switch(counter_channel)
+       {
+       case 0:
+               return G0SWSaveRegister;
+               break;
+       case 1:
+               return G1SWSaveRegister;
+               break;
+       case 2:
+               return G2SWSaveRegister;
+               break;
+       case 3:
+               return G3SWSaveRegister;
+               break;
+       default:
+               DPRINTK("ni_660x: bug!, line %i\n", __LINE__);
+               break;
+       }
+       return 0;
+}
+static inline int IOConfigReg(int chipset, int counter_channel)
+{
+       if(chipset == 0)
+       {
+               switch(counter_channel)
+               {
+               case 0:
+                       return IOConfigReg36_39;
+                       break;
+               case 1:
+                       return IOConfigReg32_35;
+                       break;
+               case 2:
+                       return IOConfigReg28_31;
+                       break;
+               case 3:
+                       return IOConfigReg24_27;
+                       break;
+               default:
+                       DPRINTK("ni_660x: bug!, line %i\n", __LINE__);
+                       break;
+               }
+       }else
+       {
+               switch(counter_channel)
+               {
+               case 0:
+                       return IOConfigReg20_23;
+                       break;
+               case 1:
+                       return IOConfigReg16_19;
+                       break;
+               case 2:
+                       return IOConfigReg12_15;
+                       break;
+               case 3:
+                       return IOConfigReg8_11;
+                       break;
+               default:
+                       DPRINTK("ni_660x: bug!, line %i\n", __LINE__);
+                       break;
+               }
+       }
+       return 0;
+}
 
+typedef struct
+{
+  char *name; // Register Name
+  int offset; // Offset from base address from GPCT chip
+  int direction; // read or write, ie INSN_READ or ...
+  int size; // 1 byte, 2 bytes, or 4 bytes
+} NI_660xRegisterData;
+
+#define INSN_RW 3 // Unused, could be used to check that register can
+                 // both be written or read
+
+const NI_660xRegisterData registerData[NumRegisters] =
+  {
+    {"G0 Interrupt Acknowledge", 0x004, INSN_WRITE, DATA_2B},
+    {"G0 Status Register", 0x004, INSN_READ, DATA_2B},
+    {"G1 Interrupt Acknowledge", 0x006, INSN_WRITE, DATA_2B},
+    {"G1 Status Register", 0x006, INSN_READ, DATA_2B},
+    {"G01 Status Register ", 0x008, INSN_READ, DATA_2B},
+    {"G0 Command Register", 0x00C, INSN_WRITE, DATA_2B},
+    {"G1 Command Register", 0x00E, INSN_WRITE, DATA_2B},
+    {"G0 HW Save Register", 0x010, INSN_READ, DATA_4B},
+    {"G1 HW Save Register", 0x014, INSN_READ, DATA_4B},
+    {"G0 SW Save Register", 0x018, INSN_READ, DATA_4B},
+    {"G1 SW Save Register", 0x01C, INSN_READ, DATA_4B},
+    {"G0 Mode Register", 0x034, INSN_WRITE, DATA_2B},
+    {"G01 Joint Status 1 Register", 0x036, INSN_READ, DATA_2B},
+    {"G1 Mode Register", 0x036, INSN_WRITE, DATA_2B},
+    {"G0 Load A Register", 0x038, INSN_WRITE, DATA_4B},
+    {"G01 Joint Status 2 Register", 0x03A, INSN_READ, DATA_2B},
+    {"G0 Load B Register", 0x03C, INSN_WRITE, DATA_4B},
+    {"G1 Load A Register", 0x040, INSN_WRITE, DATA_4B},
+    {"G1 Load B Register", 0x044, INSN_WRITE, DATA_4B},
+    {"G0 Input Select Register", 0x048, INSN_WRITE, DATA_2B},
+    {"G1 Input Select Register", 0x04A, INSN_WRITE, DATA_2B},
+    {"G01 Joint Reset Register", 0x090, INSN_WRITE, DATA_2B},
+    {"G0 Interrupt Enable", 0x092, INSN_WRITE, DATA_2B},
+    {"G1 Interrupt Enable", 0x096, INSN_WRITE, DATA_2B},
+    {"G0 Counting Mode Register", 0x0B0, INSN_WRITE, DATA_2B},
+    {"G1 Counting Mode Register", 0x0B2, INSN_WRITE, DATA_2B},
+    {"G0 Second Gate Register", 0x0B4, INSN_WRITE, DATA_2B},
+    {"G1 Second Gate Register", 0x0B6, INSN_WRITE, DATA_2B},
+    {"G0 DMA Config Register", 0x0B8, INSN_WRITE, DATA_2B},
+    {"G0 DMA Status Register", 0x0B8, INSN_READ, DATA_2B},
+    {"G1 DMA Config Register", 0x0BA, INSN_WRITE, DATA_2B},
+    {"G1 DMA Status Register", 0x0BA, INSN_READ, DATA_2B},
+    {"G2 Interrupt Acknowledge", 0x104, INSN_WRITE, DATA_2B},
+    {"G2 Status Register", 0x104, INSN_READ, DATA_2B},
+    {"G3 Interrupt Acknowledge", 0x106, INSN_WRITE, DATA_2B},
+    {"G3 Status Register", 0x106, INSN_READ, DATA_2B},
+    {"G23 Status Register", 0x108, INSN_READ, DATA_2B},
+    {"G2 Command Register", 0x10C, INSN_WRITE, DATA_2B},
+    {"G3 Command Register", 0x10E, INSN_WRITE, DATA_2B},
+    {"G2 HW Save Register", 0x110, INSN_READ, DATA_4B},
+    {"G3 HW Save Register", 0x114, INSN_READ, DATA_4B},
+    {"G2 SW Save Register", 0x118, INSN_READ, DATA_4B},
+    {"G3 SW Save Register", 0x11C, INSN_READ, DATA_4B},
+    {"G2 Mode Register", 0x134, INSN_WRITE, DATA_2B},
+    {"G23 Joint Status 1 Register", 0x136, INSN_READ, DATA_2B},
+    {"G3 Mode Register", 0x136, INSN_WRITE, DATA_2B},
+    {"G2 Load A Register", 0x138, INSN_WRITE, DATA_4B},
+    {"G23 Joint Status 2 Register", 0x13A, INSN_READ, DATA_2B},
+    {"G2 Load B Register", 0x13C, INSN_WRITE, DATA_4B},
+    {"G3 Load A Register", 0x140, INSN_WRITE, DATA_4B},
+    {"G3 Load B Register", 0x144, INSN_WRITE, DATA_4B},
+    {"G2 Input Select Register", 0x148, INSN_WRITE, DATA_2B},
+    {"G3 Input Select Register", 0x14A, INSN_WRITE, DATA_2B},
+    {"G23 Joint Reset Register", 0x190, INSN_WRITE, DATA_2B},
+    {"G2 Interrupt Enable", 0x192, INSN_WRITE, DATA_2B},
+    {"G3 Interrupt Enable", 0x196, INSN_WRITE, DATA_2B},
+    {"G2 Counting Mode Register", 0x1B0, INSN_WRITE, DATA_2B},
+    {"G3 Counting Mode Register", 0x1B2, INSN_WRITE, DATA_2B},
+    {"G3 Second Gate Register", 0x1B6, INSN_WRITE, DATA_2B},
+    {"G2 Second Gate Register", 0x1B4, INSN_WRITE, DATA_2B},
+
+    {"G2 DMA Config Register", 0x1B8, INSN_WRITE, DATA_2B},
+    {"G2 DMA Status Register", 0x1B8, INSN_READ, DATA_2B},
+    {"G3 DMA Config Register", 0x1BA, INSN_WRITE, DATA_2B},
+    {"G3 DMA Status Register", 0x1BA, INSN_READ, DATA_2B},
+    {"Clock Config Register", 0x73C, INSN_WRITE, DATA_4B},
+    {"IO Config Register 0-3", 0x77C, INSN_RW, DATA_4B}, // READWRITE
+    {"IO Config Register 4-7", 0x780, INSN_RW, DATA_4B},
+    {"IO Config Register 8-11", 0x784, INSN_RW, DATA_4B},
+    {"IO Config Register 12-15", 0x788, INSN_RW, DATA_4B},
+    {"IO Config Register 16-19", 0x78C, INSN_RW, DATA_4B},
+    {"IO Config Register 20-23", 0x790, INSN_RW, DATA_4B},
+    {"IO Config Register 24-27", 0x794, INSN_RW, DATA_4B},
+    {"IO Config Register 28-31", 0x798, INSN_RW, DATA_4B},
+    {"IO Config Register 32-35", 0x79C, INSN_RW, DATA_4B},
+    {"IO Config Register 36-39", 0x7A0, INSN_RW, DATA_4B},
+    {"STD DIO Parallel Input", 0x00E, INSN_READ, DATA_2B},
+    {"STD DIO Output", 0x014, INSN_WRITE, DATA_2B},
+    {"STD DIO Control", 0x016, INSN_WRITE, DATA_2B},
+    {"STD DIO Serial Input", 0x038, INSN_READ, DATA_2B},
+  };
+
+
+/* Different Application Classes for GPCT Subdevices */
+/* The list is not exhaustive and needs discussion! */
+typedef enum
+  {
+    CountingAndTimeMeasurement,
+    SinglePulseGeneration,
+    PulseTrainGeneration,
+    PositionMeasurement,
+    Miscellaneous
+  } NI_660x_GPCT_AppClass;
+
+
+/* Config struct for different GPCT subdevice Application Classes and
+   their options 
 */
+typedef struct
+{
+  NI_660x_GPCT_AppClass App;
+  /* Application dependent data, eg. for encoders, See mail Herman Bruyninckx
+  <https://cvs.comedi.org/pipermail/comedi/2003-April/004381.html>
+   and Adapted by Klaas Gadeyne with real-life experience :-)
+  */
+  int data[6];
+} NI_660x_GPCT_Config;
+
+#define NI_660X_GPCT_MAXCHANNELS 8 // To avoid dyn. mem. allocation
+NI_660x_GPCT_Config ni_660x_gpct_config[NI_660X_GPCT_MAXCHANNELS];
+
+/* Some bits to write in different registers */
+#define UpDownDown             0x0<<5 // always count down
+#define UpDownUp               0x1<<5 // always count up
+#define UpDownHardware         0x1<<6 // up/down depending on
+                                      // hardware pin
+#define UpDownGate             0x3<<5 // depending on hardware
+                                      // internal GATE
+#define Disarm                 0x1<<4
+#define Load                   0x1<<2
+#define Arm                    0x1<<0
+
+#define IndexPhaseLowLow       0x0<<5 // Index Pulse active when both
+                                      // A and B are low
+#define IndexPhaseLowHigh      0x1<<5 // ...
+#define IndexPhaseHighLow      0x2<<5
+#define IndexPhaseHighHigh     0x3<<5
+
+#define IndexMode              0x1<<4
+// For quadrature encoders
+#define CountingModeNormal     0x0<<0
+#define CountingModeQuadX1     0x1<<0
+#define CountingModeQuadX2     0x2<<0
+#define CountingModeQuadX4     0x3<<0
+
+// For 2-pulse encoders
+#define CountingModeTwoPulse   0x4<<0
+#define CountingModeSynchronous        0x6<<0
+
+#define GateSelectPin38                0x1<<8 // Take internal time-based 20
+                                      // MHz clockx
+#define SourceSelectTimebase1  0x0<<2
+#define SourceSelectTimebase2  0x12<<2
+#define SourceSelectTimebase3  0x1e<<2
+#define GateSelectSource        0x0<<7
+
+#define TriggerModeStartStop   0x0<<3
+#define TriggerModeStopStart   0x1<<3
+#define TriggerModeStart       0x2<<3
+#define TriggerModeNotUsed     0x3<<3
+#define GatingModeDisabled     0x0<<0
+#define GatingModeLevel                0x1<<0
+#define GatingModeRising       0x2<<0
+#define GatingModeFalling      0x3<<0
+#define G1Reset                        0x1<<3
+#define G0Reset                        0x1<<2
+#define G1Armed                        0x1<<9
+#define G0Armed                        0x1<<8
+// kind of ENABLE for the second counter
+#define CounterSwap             0x1<<21
+static inline int GxReset(int counter_channel)
+{
+       switch(counter_channel)
+       {
+       case 0:
+               return G0Reset;
+               break;
+       case 1:
+               return G1Reset;
+               break;
+       case 2:
+               return G0Reset;
+               break;
+       case 3:
+               return G1Reset;
+               break;
+       default:
+               DPRINTK("ni_660x: bug!, line %i\n", __LINE__);
+               break;
+       }
+       return 0;
+}
 
-/* Things to do:
+#define LoadOnTC                0x1<<12
 
-   Add General-Purpose Counter/Timer as a subdevice
-*/
+#define OutputIsTC              0x1<<8
+#define OutputTogglesOnTC       0x2<<8
+#define OutputTogglesOnTCorGate 0x3<<8
 
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/errno.h> 
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
+#define DisarmAtTCStopsCounting 0x1<<11
+#define NoHardwareDisarm        0x0<<11
+#define StopOn2ndTC             0x1<<6
+#define LoadSourceSelectA       0x0<<7
 
-#include <asm/io.h>
+#define SynchroniseGate         0x1<<8
 
-#include <linux/comedidev.h>
-#include "mite.h"
+// For pulse train generation
+#define BankSwitchEnable        0x1<<12
+#define BankSwitchOnGate        0x0<<11
+#define BankSwitchOnSoftware    0x1<<11
+#define BankSwitchStart         0x1<<10
+#define ReloadSourceSwitching   0x1<<15
 
-#define PCI_VENDOR_ID_NATINST  0x1093
+// ioconfigreg
+/*pin index 0 corresponds to pin A in manual, index 1 is pin B, etc*/
+static inline int pin_is_output(int pin_index)
+{
+       return 0x1 << (24 - 8 * pin_index);
+}
+static inline int pin_input_select(int pin_index, int input_selection)
+{
+       input_selection &= 0x7;
+       return input_selection << (28 - 8 * pin_index);
+}
+
+// For configuring input pins
+#define Digital_Filter_A_Is_Off             0x000<<28
+#define Digital_Filter_A_Is_Timebase3       0x001<<28
+#define Digital_Filter_A_Is_100             0x010<<28
+#define Digital_Filter_A_Is_20              0x011<<28
+#define Digital_Filter_A_Is_10              0x100<<28
+#define Digital_Filter_A_Is_2               0x101<<28
+#define Digital_Filter_A_Is_2_Timebase3     0x110<<28
 
-#define DATA_1B 0x1
-#define DATA_2B 0x2
-#define DATA_4B 0x4
 
-/* Board description*/
 
-typedef struct ni_660x_board_struct
+// Offset of the GPCT chips from the base-adress of the card
+const int GPCT_OFFSET[2] = {0x0,0x800}; /* First chip is at base-adress +
+                                          0x00, etc. */
+
+/* Board description*/
+typedef struct
 {
-       unsigned short dev_id;
-       char *name;
-}ni_660x_board;
+  unsigned short dev_id; /* `lspci` will show you this */
+  char *name;
+  int n_ctrs; /* total number of counters */
+  int cnt_bits; /* number of bits in each counter */
+} ni_660x_board;
 
 static ni_660x_board ni_660x_boards[] = 
-{
-       {
-       dev_id          : 0x2c60,
-       name            : "PCI-6601",
-       },
-       {
-       dev_id          : 0x0,                  /* ????? */
-       name            : "PCI-6602",
-       },
-};
+  {
+    {
+      dev_id       : 0x2c60,
+      name         : "PCI-6601",
+      n_ctrs       : 1*CTRS_PER_CHIP,
+      cnt_bits     : 32,
+    },
+    {
+      dev_id       : 0x1310,
+      name         : "PCI-6602",
+      n_ctrs       : 2*CTRS_PER_CHIP,
+      cnt_bits     : 32,
+    },
+  };
 
 static struct pci_device_id ni_660x_pci_table[] __devinitdata = {
-       { PCI_VENDOR_ID_NATINST, 0x2c60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-       //{ PCI_VENDOR_ID_NATINST, 0x0000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-       { 0 }
+  { PCI_VENDOR_ID_NATINST, 0x2c60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+  { PCI_VENDOR_ID_NATINST, 0x1310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+  { 0 }
 };
 MODULE_DEVICE_TABLE(pci, ni_660x_pci_table);
 
 #define thisboard ((ni_660x_board *)dev->board_ptr)
+/* initialized in ni_660x_find_device() */
 
 typedef struct
 {
-       struct mite_struct *mite;
-       int boardtype;
+  struct mite_struct *mite;
+  int boardtype;
 }ni_660x_private;
 
 #define devpriv ((ni_660x_private *)dev->private)
@@ -97,62 +653,136 @@ static int ni_660x_attach(comedi_device *dev,comedi_devconfig *it);
 static int ni_660x_detach(comedi_device *dev);
 
 static comedi_driver driver_ni_660x=
-{
-       driver_name:    "ni_660x",
-       module:         THIS_MODULE,
-       attach:         ni_660x_attach,
-       detach:         ni_660x_detach,
-};
+  {
+    driver_name:       "ni_660x",
+    module:            THIS_MODULE,
+    attach:            ni_660x_attach,
+    detach:            ni_660x_detach,
+  };
 
 COMEDI_INITCLEANUP(driver_ni_660x);
 
 static int ni_660x_find_device(comedi_device *dev,int bus,int slot);
 
-static int ni_660x_tio_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
-static int ni_660x_tio_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
-static int ni_660x_tio_insn_bits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
-static int ni_660x_tio_insn_config(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
+/* Possible instructions for a GPCT */
+static int ni_660x_GPCT_rinsn(comedi_device *dev,
+                             comedi_subdevice *s,
+                             comedi_insn *insn,
+                             lsampl_t *data);
+static int ni_660x_GPCT_insn_config(comedi_device *dev,
+                                   comedi_subdevice *s,
+                                   comedi_insn *insn,
+                                   lsampl_t *data);
+static int ni_660x_GPCT_winsn(comedi_device *dev,
+                             comedi_subdevice *s,
+                             comedi_insn *insn,
+                             lsampl_t * data);
+// Internal triggering
+/* Currently only used to stop the pulsegenerator */
+static int ni_660x_GPCT_inttrig(comedi_device *dev,
+                               comedi_subdevice *subdev,
+                               unsigned int trig_num);
+
+// NYI
+static int ni_660x_GPCT_cmdtest(comedi_device *dev,comedi_subdevice *s,
+                               comedi_cmd *cmd);
+static int ni_660x_GPCT_cmd(comedi_device *dev,comedi_subdevice *s);
+
+/* Possible instructions for Digital IO: Not implemented yet! */
+static int ni_660x_dio_insn_config(comedi_device *dev,
+                                  comedi_subdevice *s,
+                                  comedi_insn *insn,
+                                  lsampl_t *data);
+static int ni_660x_dio_insn_bits(comedi_device *dev,
+                                comedi_subdevice *s,
+                                comedi_insn *insn,
+                                lsampl_t *data);
+
+static int ni_660x_GPCT_cmdtest(comedi_device *dev,comedi_subdevice *s,
+                               comedi_cmd *cmd)
+{
+  DPRINTK("NI_660X: COMMANDS not implemented yet for GPCT\n");
+  return -EINVAL;
+}
 
+static int ni_660x_GPCT_cmd(comedi_device *dev,comedi_subdevice *s)
+{
+  DPRINTK("NI_660X: COMMANDS not implemented yet for GPCT\n");
+  return -EINVAL;
+}
 
 static int ni_660x_attach(comedi_device *dev,comedi_devconfig *it)
 {
-  comedi_subdevice *s;
-  int ret;
-
-  printk("comedi%d: ni_660x: ",dev->minor);
-
-  if ((ret=alloc_private(dev,sizeof(ni_660x_private))) < 0) return ret;
-
-  ret = ni_660x_find_device(dev, it->options[0], it->options[1]);
-  if (ret<0) return ret;
-
-  ret = mite_setup(devpriv->mite);
-  if (ret < 0) {
-    printk("error setting up mite\n");
-    return ret;
-  }
-  dev->iobase = mite_iobase(devpriv->mite);
-  dev->board_name = thisboard->name;
-  dev->irq = mite_irq(devpriv->mite);
-  printk(" %s ", dev->board_name);
-
-  if (alloc_subdevices(dev, 1)<0) return -ENOMEM;
-
-  s=dev->subdevices+0;
-  /* general-purpose counter/time ASIC */
-  s->type                      =       COMEDI_SUBD_COUNTER;
-  s->subdev_flags              =       SDF_CMD|SDF_READABLE|SDF_WRITABLE;
-  s->n_chan                    =       8;
-  s->maxdata                   =       1;
-  s->range_table               =       &range_digital;
-  s->insn_write                =       &ni_660x_tio_winsn;
-  s->insn_read                 =       &ni_660x_tio_rinsn;
-  s->insn_bits                 =       ni_660x_tio_insn_bits;
-  s->insn_config               =       ni_660x_tio_insn_config;
-
-  printk("attached\n");
-
-  return 1;
+       comedi_subdevice *s;
+       int ret;
+
+       printk("comedi%d: ni_660x: ",dev->minor);
+
+       if ((ret=alloc_private(dev,sizeof(ni_660x_private))) < 0) return ret;
+
+       ret = ni_660x_find_device(dev, it->options[0], it->options[1]);
+       if (ret<0) return ret;
+
+       ret = mite_setup(devpriv->mite);
+       if (ret < 0) {
+       printk("error setting up mite\n");
+       return ret;
+       }
+       dev->iobase = mite_iobase(devpriv->mite);
+       dev->board_name = thisboard->name;
+/* we don't support the interrupt yet */
+//     dev->irq = mite_irq(devpriv->mite);
+
+       printk(" %s ", dev->board_name);
+
+       /* Currently there is 1 subdevice for the GPCT functionality,
+       and another subdevice for DIO */
+       dev->n_subdevices = 2;
+
+       if (alloc_subdevices(dev,dev->n_subdevices)<0) return -ENOMEM;
+
+       s=dev->subdevices+0;
+       /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
+       s->type         = COMEDI_SUBD_COUNTER;
+       s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
+       /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
+       s->n_chan       = thisboard->n_ctrs;
+       s->maxdata      = 0xffffffff; /* 32 bit counter */
+       s->insn_read    = ni_660x_GPCT_rinsn;
+       s->insn_config  = ni_660x_GPCT_insn_config;
+       s->insn_write   = ni_660x_GPCT_winsn;
+
+       /* Command are not implemented yet, however they are necessary to
+       allocate the necessary memory for the comedi_async struct (used
+       to trigger the GPCT in case of pulsegenerator function */
+       s->do_cmd = ni_660x_GPCT_cmd;
+       s->do_cmdtest = ni_660x_GPCT_cmdtest;
+       //s->cancel = ni_660x_gpct_cancel;
+
+       s=dev->subdevices+1;
+       /* DIGITAL I/O SUBDEVICE */
+       s->type   = COMEDI_SUBD_DIO;
+       s->subdev_flags = SDF_READABLE|SDF_WRITABLE;
+       s->n_chan       = 8; // Only using 8 bits for now, instead of 32!!
+       s->maxdata      = 1;
+       s->range_table  = &range_digital;
+       /* (Copied from skel.c) DIO devices are slightly special.  Although
+       * it is possible to implement the insn_read/insn_write interface,
+       * it is much more useful to applications if you implement the
+       * insn_bits interface.  This allows packed reading/writing of the
+       * DIO channels.  The comedi core can convert between insn_bits and
+       * insn_read/write */
+       // Not implemented yet
+       s->insn_bits    = ni_660x_dio_insn_bits;
+       s->insn_config  = ni_660x_dio_insn_config;
+       s->io_bits      = 0;     /* all bits default to input */
+
+       printk("attached\n");
+
+       /* What does this "return value" mean?  Is this fixed by API??
+       - skel_attach in skel.c returns 1;
+       - ni_E_init in ni_mio_common.c returns "0" ... */
+       return 1;
 }
 
 
@@ -164,147 +794,637 @@ ni_660x_detach(comedi_device *dev)
   if (dev->private && devpriv->mite)
     mite_unsetup(devpriv->mite);
 
-  if(dev->irq)
-    comedi_free_irq(dev->irq,dev);
+  /* Free irq */
 
+  if(dev->irq) comedi_free_irq(dev->irq,dev);
+
+  /* Same question as with attach ... */
   return 0;
 }
 
-static int
-ni_660x_tio_winsn(comedi_device *dev, comedi_subdevice *s,
-                 comedi_insn *insn, lsampl_t *data)
+// Help function: Check what chipset the counter channel is on
+static int GPCT_check_chipset_from_channel(comedi_device *dev, int channel)
 {
-  int i=0;
-
-  switch (insn->chanspec) {
-
-  case DATA_1B:
-    for (i=0; i<insn->n; i+=2) {
-      writeb(data[i+1], dev->iobase+data[i]);
+  int chipset;
+  if ( (channel >= 0) && (channel < CTRS_PER_CHIP) )
+    {
+      chipset = 0;
     }
-    break;
-
-  case DATA_2B:
-    for (i=0; i<insn->n; i+=2) {
-      writew(data[i+1], dev->iobase+data[i]);
+  else if ( (channel >= CTRS_PER_CHIP) && (channel < thisboard->n_ctrs) )
+    {
+      chipset = 1;
+      // DPRINTK("NI_660x: Moving to chipset 1\n");
     }
-    break;
-
-  case DATA_4B:
-    for (i=0; i<insn->n; i+=2) {
-      writel(data[i+1], dev->iobase+data[i]);
+  else
+    {
+      DPRINTK("NI_660x: Channel specification not between limits\n");
+      return -EINVAL;
     }
-    break;
-
-  default:
-    return insn->n;
-
-  }
-
-  return i;
+  return chipset;
+}
+int GPCT_check_counter_channel_from_subdev_channel(int channel)
+{
+       return channel % CTRS_PER_CHIP;
 }
 
 static int
-ni_660x_tio_rinsn(comedi_device *dev, comedi_subdevice *s,
-                 comedi_insn *insn, lsampl_t *data)
+ni_660x_GPCT_rinsn(comedi_device *dev, comedi_subdevice *s,
+                  comedi_insn *insn, lsampl_t *data)
 {
-  int i=0;
-
-  switch (insn->chanspec) {
-
-  case DATA_1B:
-    for (i=0; i<insn->n; i+=2) {
-      data[i+1] = readb(dev->iobase+data[i]);
-    }
-    break;
-
-  case DATA_2B:
-    for (i=0; i<insn->n; i+=2) {
-      data[i+1] = readw(dev->iobase+data[i]);
-    }
-    break;
-
-  case DATA_4B:
-    for (i=0; i<insn->n; i+=2) {
-      data[i+1] = readl(dev->iobase+data[i]);
-    }
-    break;
+       int i; // counts the Data
+       int subdev_channel = CR_CHAN(insn->chanspec);
+       int counter_channel = GPCT_check_counter_channel_from_subdev_channel(subdev_channel);// Unpack chanspec
+       int chipset = GPCT_check_chipset_from_channel(dev, subdev_channel);
+
+  /* See Chapter 2.2 Reading Counter Value of the NI Register Level
+     Programming Manual: "Reading counter values of armed counters".
+     We need to take several measurements to be sure what the counter
+     value is
+  */
+       int tmpdata[2];
+       int address;
+
+       /* ============================================================ */
+       /* 1 subdevice with 8 channels, differentation based on channel */
+       DPRINTK("NI_660x: INSN_READ on channel %d\n", subdev_channel);
+
+       // Check what Application of Counter this channel is configured for
+       switch(ni_660x_gpct_config[subdev_channel].App)
+       {
+       case PositionMeasurement:
+               // Check if (n > 0)
+               if ( insn->n <= 0 )
+               {
+                       DPRINTK("NI_660X: INSN_READ: n should be > 0\n");
+                       return -EINVAL;
+               }
+               // Now proceed with reading data
+               address = dev->iobase
+               + GPCT_OFFSET[chipset] +
+               registerData[GxSWSaveRegister(counter_channel)].offset;
+               for ( i=0 ; i < insn->n ; i++ )
+               {
+                       tmpdata[0] = readl(address);
+                       tmpdata[1] = readl(address);
+                       if (tmpdata[0] != tmpdata[1])
+                       {
+                               // In case they differ, the 3d measurement is the
+                               // correct value
+                               data[i] = readl(address);
+                       }
+                       // Otherwise, they are the same and the correct counter
+                       // value
+                       else data[i] = tmpdata[0];
+               }
+               return i;
+               break;
+       case SinglePulseGeneration: case PulseTrainGeneration:
+               DPRINTK("NI_660x: INSN_READ irrelevant for this application\n");
+               return -EINVAL;
+               break;
+       default: // The rest is not implemented yet :-)
+               DPRINTK("NI_660x: INSN_READ: Functionality not implemented\n");
+               return -EINVAL;
+               break;
+       }// End switch(ni_660x_gpct_config[channel].App)
+}
 
-  default:
-    return insn->n;
+static inline int z_reload_trigger(int a_state, int b_state)
+{
+       int bits = comedi_counter_trigger_bits(2, 0);
+
+       if(a_state)
+               bits |= comedi_counter_trigger_bits(0, 0);
+       else
+               bits |= comedi_counter_trigger_bits(0, CR_INVERT);
+       if(b_state)
+               bits |= comedi_counter_trigger_bits(1, 0);
+       else
+               bits |= comedi_counter_trigger_bits(1, CR_INVERT);
+       return bits;
+}
 
-  }
+static int ni_660x_find_counter_trigger(const comedi_insn *insn, const lsampl_t *data,
+       lsampl_t trigger, lsampl_t action)
+{
+       static const int first_trigger_index = 1;
+       int i;
 
-  return i;
+       for(i = first_trigger_index; i + 1 < insn->n; i += 2)
+       {
+               if(data[i] == trigger && data[i + 1] == action)
+               {
+                       return 1;
+               }
+       }
+       return 0;
 }
 
-// this is being called as part of opening the device
-static int
-ni_660x_tio_insn_bits(comedi_device *dev, comedi_subdevice *s,
-                     comedi_insn *insn, lsampl_t *data)
+static int find_z_reload_trigger(const comedi_insn *insn, const lsampl_t *data)
 {
-  int i=0;
+       int retval;
+
+       retval = ni_660x_find_counter_trigger(insn, data, z_reload_trigger(0, 0), COMEDI_RESET_ACCUMULATOR);
+       if(retval)
+               return 0x0;
+       retval = ni_660x_find_counter_trigger(insn, data, z_reload_trigger(0, 1), COMEDI_RESET_ACCUMULATOR);
+       if(retval)
+               return 0x1;
+       retval = ni_660x_find_counter_trigger(insn, data, z_reload_trigger(1, 0), COMEDI_RESET_ACCUMULATOR);
+       if(retval)
+               return 0x2;
+       retval = ni_660x_find_counter_trigger(insn, data, z_reload_trigger(1, 1), COMEDI_RESET_ACCUMULATOR);
+       if(retval)
+               return 0x3;
+       return -1;
+}
 
-  switch (insn->chanspec) {
+static int ni_660x_find_quad_encoding(comedi_device *dev, comedi_subdevice *s,
+       comedi_insn *insn, lsampl_t *data)
+{
+       int quad1_inc_trigger = comedi_counter_trigger_bits(0, CR_EDGE) |
+               comedi_counter_trigger_bits(1, CR_INVERT);
+       int quad1_dec_trigger = comedi_counter_trigger_bits(0, CR_EDGE | CR_INVERT) |
+               comedi_counter_trigger_bits(1, CR_INVERT);
+       int quad2_inc_trigger = comedi_counter_trigger_bits(0, CR_EDGE | CR_INVERT) |
+               comedi_counter_trigger_bits(1, 0);
+       int quad2_dec_trigger = comedi_counter_trigger_bits(0, CR_EDGE) |
+               comedi_counter_trigger_bits(1, 0);
+       int quad4_inc_trigger0 = comedi_counter_trigger_bits(1, CR_EDGE | CR_INVERT) |
+               comedi_counter_trigger_bits(0, CR_INVERT);
+       int quad4_dec_trigger0 = comedi_counter_trigger_bits(1, CR_EDGE) |
+               comedi_counter_trigger_bits(0, CR_INVERT);
+       int quad4_inc_trigger1 = comedi_counter_trigger_bits(1, CR_EDGE) |
+               comedi_counter_trigger_bits(0, 0);
+       int quad4_dec_trigger1 = comedi_counter_trigger_bits(1, CR_EDGE | CR_INVERT) |
+               comedi_counter_trigger_bits(0, 0);
+       static const int first_trigger_index = 1;
+       int triggers_remaining = insn->n - first_trigger_index;
+       int z_reload_phase;
+       int retval;
+
+       /* look for z index reload */
+       z_reload_phase = find_z_reload_trigger(insn, data);
+       if(z_reload_phase >= 0)
+       {
+               triggers_remaining--;
+       }
+       /* look for quad x1 triggers */
+       retval = ni_660x_find_counter_trigger(insn, data, quad1_inc_trigger, COMEDI_INC_ACCUMULATOR);
+       if(retval == 0)
+       {
+               DPRINTK("NI_660x: INSN_CONFIG: no good quad x1 inc trigger found\n");
+               return -EINVAL;
+       }
+       triggers_remaining--;
+       retval = ni_660x_find_counter_trigger(insn, data, quad1_dec_trigger, COMEDI_DEC_ACCUMULATOR);
+       if(retval == 0)
+       {
+               DPRINTK("NI_660x: INSN_CONFIG: no good quad x1 dec trigger found\n");
+               return -EINVAL;
+       }
+       triggers_remaining--;
+       if(triggers_remaining == 0)
+       {
+               return 1;
+       }
+       /* look for quad x2 triggers */
+       retval = ni_660x_find_counter_trigger(insn, data, quad2_inc_trigger, COMEDI_INC_ACCUMULATOR);
+       if(retval == 0)
+       {
+               DPRINTK("NI_660x: INSN_CONFIG: no good quad x2 inc trigger found\n");
+               return -EINVAL;
+       }
+       triggers_remaining--;
+       retval = ni_660x_find_counter_trigger(insn, data, quad2_dec_trigger, COMEDI_DEC_ACCUMULATOR);
+       if(retval == 0)
+       {
+               DPRINTK("NI_660x: INSN_CONFIG: no good quad x2 dec trigger found\n");
+               return -EINVAL;
+       }
+       triggers_remaining--;
+       if(triggers_remaining == 0)
+       {
+               return 2;
+       }
+       /* look for quad x4 triggers */
+       retval = ni_660x_find_counter_trigger(insn, data, quad4_inc_trigger0, COMEDI_INC_ACCUMULATOR);
+       if(retval == 0)
+       {
+               DPRINTK("NI_660x: INSN_CONFIG: no good quad x4 inc trigger found\n");
+               return -EINVAL;
+       }
+       triggers_remaining--;
+       retval = ni_660x_find_counter_trigger(insn, data, quad4_dec_trigger0, COMEDI_DEC_ACCUMULATOR);
+       if(retval == 0)
+       {
+               DPRINTK("NI_660x: INSN_CONFIG: no good quad x4 dec trigger found\n");
+               return -EINVAL;
+       }
+       triggers_remaining--;
+       retval = ni_660x_find_counter_trigger(insn, data, quad4_inc_trigger1, COMEDI_INC_ACCUMULATOR);
+       if(retval == 0)
+       {
+               DPRINTK("NI_660x: INSN_CONFIG: no good quad x4 inc trigger found\n");
+               return -EINVAL;
+       }
+       triggers_remaining--;
+       retval = ni_660x_find_counter_trigger(insn, data, quad4_dec_trigger1, COMEDI_DEC_ACCUMULATOR);
+       if(retval == 0)
+       {
+               DPRINTK("NI_660x: INSN_CONFIG: no good quad x4 dec trigger found\n");
+               return -EINVAL;
+       }
+       triggers_remaining--;
+       if(triggers_remaining == 0)
+       {
+               return 4;
+       }
+       return -EINVAL;
+}
 
-  case DATA_1B:
-    for (i=0; i<insn->n; i+=2) {
-      data[i+1] = readb(dev->iobase+data[i]);
-    }
-    break;
+static void ni_660x_program_quad_encoder(comedi_device *dev, int subdev_channel)
+{
+       int chipset = GPCT_check_chipset_from_channel(dev,subdev_channel);
+       int counter_channel = GPCT_check_counter_channel_from_subdev_channel(subdev_channel);
+
+       // Reset the counter
+       writew(GxReset(counter_channel),dev->iobase + GPCT_OFFSET[chipset]
+       + registerData[GxxJointResetRegister(counter_channel)].offset);
+       // Disarm
+       writew(Disarm,dev->iobase + GPCT_OFFSET[chipset]
+               + registerData[GxCommandRegister(counter_channel)].offset);
+       // Put 0 as initial counter value in the load register
+       writel(0x0,dev->iobase + GPCT_OFFSET[chipset]
+               + registerData[GxLoadARegister(counter_channel)].offset);
+       // Load (latch) this value into the counter
+       writew(Load,dev->iobase + GPCT_OFFSET[chipset]
+               + registerData[GxCommandRegister(counter_channel)].offset);
+       /* - Set Counting Mode into GPCT_X1 / 2 / 4 (as set by user
+               - Take into account Z pulse if set by user (index pulse)
+               - When to take into account the Z pulse
+               TODO: Make this latter an option
+               TODO: See p. 3.10 of the Register Level Programming
+               Manual.  Find out how bit 4 (Gi Index mode) really
+               works.  I think this bit determines if the counter is
+               reset
+       */
+       writew(((ni_660x_gpct_config[subdev_channel]).data[0] | (ni_660x_gpct_config[subdev_channel]).data[1] |
+               (ni_660x_gpct_config[subdev_channel]).data[1]), dev->iobase + GPCT_OFFSET[chipset]
+               + registerData[GxCountingModeRegister(counter_channel)].offset);
+       // Put counter in input mode
+       // Not necessary at this point, since this is the default ...
+
+/*       writel(0, dev->iobase
+       + GPCT_OFFSET[chipset]
+       + registerData[IOConfigReg(chipset, counter_channel)].offset);
+*/
+       // Arm the counter and put it into Hardware UpDown mode (depending
+       // on the UP/DOWN IO pin: 0 = down
+       writew(UpDownHardware | Arm, dev->iobase + GPCT_OFFSET[chipset]
+               + registerData[GxCommandRegister(counter_channel)].offset);
+}
 
-  case DATA_2B:
-    for (i=0; i<insn->n; i+=2) {
-      data[i+1] = readw(dev->iobase+data[i]);
-    }
-    break;
+static int ni_660x_setup_quad_encoder(comedi_device *dev, comedi_subdevice *s,
+       comedi_insn *insn, lsampl_t *data)
+{
+       int z_reload_phase;
+       int retval;
+       int channel = CR_CHAN(insn->chanspec);// Unpack chanspec
 
-  case DATA_4B:
-    for (i=0; i<insn->n; i+=2) {
-      data[i+1] = readl(dev->iobase+data[i]);
-    }
-    break;
+       DPRINTK("NI_660x: INSN_CONFIG: Configuring Encoder\n");
 
-  default:
-    return insn->n;
+       retval = ni_660x_find_quad_encoding(dev, s, insn, data);
+       switch(retval)
+       {
+       case 1:
+               /* quad x1 encoding*/
+               (ni_660x_gpct_config[channel]).data[0] = CountingModeQuadX1;
+               break;
+       case 2:
+               /* quad x2 encoding*/
+               (ni_660x_gpct_config[channel]).data[0] = CountingModeQuadX2;
+               break;
+       case 4:
+               /* quad x4 encoding*/
+               (ni_660x_gpct_config[channel]).data[0] = CountingModeQuadX4;
+               break;
+       default:
+               return -EINVAL;
+               break;
+       }
+
+       ni_660x_gpct_config[channel].App = PositionMeasurement;
+       /* look for z index reload */
+       z_reload_phase = find_z_reload_trigger(insn, data);
+       // When to take into account the indexpulse:
+       switch(z_reload_phase)
+       {
+       case 0x0:
+               (ni_660x_gpct_config[channel]).data[1] = IndexPhaseLowLow;
+               break;
+       case 0x1:
+               (ni_660x_gpct_config[channel]).data[1] = IndexPhaseLowHigh;
+               break;
+       case 0x2:
+               (ni_660x_gpct_config[channel]).data[1] = IndexPhaseHighLow;
+               break;
+       case 0x3:
+               (ni_660x_gpct_config[channel]).data[1] = IndexPhaseHighHigh;
+               break;
+       default:
+               break;
+       }
+       if(z_reload_phase >= 0)
+               (ni_660x_gpct_config[channel]).data[2] = IndexMode;
+       else
+               (ni_660x_gpct_config[channel]).data[2] = 0;
+
+       ni_660x_program_quad_encoder(dev, channel);
+       return 0;
+}
 
-  }
+static int ni_660x_setup_counter(comedi_device *dev, comedi_subdevice *s,
+       comedi_insn *insn, lsampl_t *data)
+{
+       int retval;
 
-  return i;
+       retval = ni_660x_setup_quad_encoder(dev, s, insn, data);
+       if(retval == 0) return 0;
+       comedi_error(dev, "unsupported INSN_CONFIG_COUNTER mode");
+       return -EINVAL;
 }
 
 static int
-ni_660x_tio_insn_config(comedi_device *dev, comedi_subdevice *s, 
-                       comedi_insn *insn, lsampl_t *data)
+ni_660x_GPCT_insn_config(comedi_device *dev, comedi_subdevice *s,
+        comedi_insn *insn, lsampl_t *data)
 {
-  int i=0;
-
-  switch (insn->chanspec) {
-
-  case DATA_1B:
-    for (i=0; i<insn->n; i+=2) {
-      writeb(data[i+1], dev->iobase+data[i]);
+       int subdev_channel = CR_CHAN(insn->chanspec);// Unpack chanspec
+       int chipset = GPCT_check_chipset_from_channel(dev, subdev_channel);
+       int counter_channel = GPCT_check_counter_channel_from_subdev_channel(subdev_channel);
+       int retval;
+
+       DPRINTK("NI_660x: INSN_CONFIG: Configuring Channel %d\n", subdev_channel);
+
+       /* See P. 3.5 of the Register-Level Programming manual.  This
+               bit has to be set, otherwise, you can't use the second chip.
+       */
+       /* XXX this should be done in attach */
+       if ( chipset == 1)
+       {
+               writel(CounterSwap,dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[ClockConfigRegister].offset);
     }
-    break;
+       else
+       {
+               writel(0x0,dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[ClockConfigRegister].offset);
+       }
 
-  case DATA_2B:
-    for (i=0; i<insn->n; i+=2) {
-      writew(data[i+1], dev->iobase+data[i]);
-    }
-    break;
+       switch(insn->data[0])
+       {
+       case INSN_CONFIG_COUNTER_ALPHA:
+               retval = ni_660x_setup_counter(dev, s, insn, data);
+               if(retval < 0) return retval;
+               break;
+#if 0
+/* XXX not sure about pulse generation API yet */
+#define GPCT_CONT_PULSE_OUT     0x0200
+#define GPCT_SINGLE_PULSE_OUT   0x0400
+#define GPCT_SINGLE_PULSE_GENERATOR 1 // Use CTR as single pulsegenerator
+#define GPCT_PULSE_TRAIN_GENERATOR 2 // Use CTR as pulsetraingenerator
+       case GPCT_SINGLE_PULSE_GENERATOR:
+               DPRINTK("NI_660x: INSN_CONFIG: Configuring SPG\n");
+               ni_660x_gpct_config[subdev_channel].App = SinglePulseGeneration;
+               /* data[1] contains the PULSE_WIDTH
+               data[2] contains the PULSE_DELAY
+               @pre PULSE_WIDTH > 0 && PULSE_DELAY > 0
+               The above periods must be expressed as a multiple of the
+               pulse frequency on the selected source, see the
+               Register-Level Programmer Manual p2-11 (pulse generation)
+               */
+               if(insn->data[2] > 1 && insn->data[1] > 1)
+               {
+                       (ni_660x_gpct_config[subdev_channel]).data[0] = insn->data[1];
+                       (ni_660x_gpct_config[subdev_channel]).data[1] = insn->data[2];
+               }
+               else
+               {
+                       DPRINTK("NI_660x: INSN_CONFIG: SPG: Problem with Pulse params\n");
+                       return -EINVAL;
+               }
+               // Reset the counter
+               writew(GxReset(counter_channel), dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxxJointResetRegister(counter_channel)].offset);
+               // Disarm
+               writew(Disarm, dev->iobase + GPCT_OFFSET[chipset]
+               + registerData[GxCommandRegister(counter_channel)].offset);
+               /* Put PULSE_DELAY as initial counter value into load
+               register A */
+               writel((ni_660x_gpct_config[subdev_channel]).data[1], dev->iobase
+               + GPCT_OFFSET[chipset]
+               + registerData[GxLoadARegister(counter_channel)].offset);
+               // Load (latch) this value into the counter
+               writew(Load,dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxCommandRegister(counter_channel)].offset);
+               // Now Put PULSE_WIDTH in the LOAD register A
+               writel((ni_660x_gpct_config[subdev_channel]).data[0],dev->iobase
+                       + GPCT_OFFSET[chipset]
+                       + registerData[GxLoadARegister(counter_channel)].offset);
+               // Put Source input to internal 20 MHz clock
+               /* ==================================================
+               TODO: MAKE THIS A DATA FIELD!! to allow different clocks
+                       (See TODO)
+               ================================================== */
+               writew(SourceSelectTimebase1, dev->iobase
+                       + GPCT_OFFSET[chipset]
+                       + registerData[GxInputSelectRegister(counter_channel)].offset);
+               /* Choose to Load on reaching TC and
+               Change State of G_OUT on TC (Terminal Count)
+               Stop counting after second TC
+               Choose Load register A to load from */
+               writew(LoadOnTC | OutputTogglesOnTC | StopOn2ndTC | LoadSourceSelectA,
+                       dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxModeRegister(counter_channel)].offset);
+               // Configure Counter for output
+               writel(pin_is_output(0), dev->iobase
+                       + GPCT_OFFSET[chipset]
+                       + registerData[IOConfigReg(chipset, counter_channel)].offset);
+       case GPCT_PULSE_TRAIN_GENERATOR:
+               DPRINTK("NI_660x: INSN_CONFIG: PTG linking inttrig\n");
+               s->async->inttrig = ni_660x_GPCT_inttrig;
+               DPRINTK("NI_660x: INSN_CONFIG: Configuring PTG\n");
+
+               ni_660x_gpct_config[subdev_channel].App = PulseTrainGeneration;
+
+               /* data[1] contains the PULSE_WIDTH
+               data[2] contains the PULSE_PERIOD
+               @pre PULSE_PERIOD > PULSE_WIDTH > 0
+               The above periods must be expressed as a multiple of the
+               pulse frequency on the selected source, see the
+               Register-Level Programmer Manual p2-11 (pulse generation)
+               */
+               if ( (insn->data[2] > insn->data[1]) && (insn->data[1] > 0 ) )
+               {
+                       (ni_660x_gpct_config[subdev_channel]).data[0] = insn->data[1];
+                       (ni_660x_gpct_config[subdev_channel]).data[1] = insn->data[2];
+               }
+               else
+               {
+                       DPRINTK("%d \t %d\n",insn->data[1],insn->data[2]);
+                       DPRINTK("NI_660x: INSN_CONFIG: PTG: Problem with Pulse params\n");
+                       return -EINVAL;
+               }
+               // Reset the counter
+               writew(GxReset(counter_channel),dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxxJointResetRegister(counter_channel)].offset);
+               // Disarm counter
+               writew(Disarm,dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxCommandRegister(counter_channel)].offset);
+               // Put PULSE_WIDTH as initial counter value into load register A
+               writel((ni_660x_gpct_config[subdev_channel]).data[0],dev->iobase
+                       + GPCT_OFFSET[chipset]
+                       + registerData[GxLoadARegister(counter_channel)].offset);
+               // Load (latch) this value into the counter
+               writew(Load,dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxCommandRegister(counter_channel)].offset);
+               // Now Put (PULSE_PERIOD - PULSE_WIDTH) in the load register B
+               writel((ni_660x_gpct_config[subdev_channel]).data[1]
+                       - (ni_660x_gpct_config[subdev_channel]).data[0],
+                       dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxLoadBRegister(counter_channel)].offset);
+               // Put Source input to internal 20 MHz clock
+               /* ==================================================
+               TODO: MAKE THIS A DATA FIELD!! to allow different clocks
+                       (See TODO)
+               ================================================== */
+               writew(SourceSelectTimebase1,dev->iobase
+                       + GPCT_OFFSET[chipset]
+                       + registerData[GxInputSelectRegister(counter_channel)].offset);
+               /* Switch between Load registers everytime
+               Choose to Load on reaching TC and
+               Change State of G_OUT on TC (Terminal Count)
+               Choose Load register A to load from */
+               writew(ReloadSourceSwitching|LoadOnTC|OutputTogglesOnTC,
+                       dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxModeRegister(counter_channel)].offset);
+               // Configure Counter for output
+               writel(pin_is_output(0), dev->iobase
+                       + GPCT_OFFSET[chipset]
+                       + registerData[IOConfigReg(chipset, counter_channel)].offset);
+               // Arm the counter and tell it to count down
+               writew(Arm|UpDownDown,dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxCommandRegister(counter_channel)].offset);
+               break;
+#endif
+       default:
+               DPRINTK("NI_660x: unsupported insn_config\n");
+               return -EINVAL;
+               break;
+       }
+
+       return insn->n;
+}
 
-  case DATA_4B:
-    for (i=0; i<insn->n; i+=2) {
-      writel(data[i+1], dev->iobase+data[i]);
-    }
-    break;
+static int ni_660x_GPCT_winsn(comedi_device *dev,
+                             comedi_subdevice *s,
+                             comedi_insn *insn,
+                             lsampl_t * data)
+{
+       int subdev_channel = CR_CHAN(insn->chanspec);// Unpack chanspec
+       int chipset = GPCT_check_chipset_from_channel(dev, subdev_channel);
+       int counter_channel = GPCT_check_counter_channel_from_subdev_channel(subdev_channel);
 
-  default:
-    return insn->n;
+       DPRINTK("NI_660X: INSN_WRITE on channel %d\n", subdev_channel);
+       // Check what Application of Counter this channel is configured for
+       switch(ni_660x_gpct_config[subdev_channel].App)
+       {
+       case PositionMeasurement:
+               // Disarm the counter
+               writew(Disarm, dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxCommandRegister(counter_channel)].offset);
+               // Write the value into the load register
+               writel(*data, dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxLoadARegister(counter_channel)].offset);
+               // Latch the value into the counter
+               writew(Load, dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxCommandRegister(counter_channel)].offset);
+               // Arm the counter again and put UpDownHardware in!
+               writew(UpDownHardware|Arm, dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxCommandRegister(counter_channel)].offset);
+               break;
+       case SinglePulseGeneration:
+               DPRINTK("NI_660X: INSN_WRITE: SPG: Arming the counter\n");
+               // Tell the counter to count down and arm
+               writew(Arm|UpDownDown, dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxCommandRegister(counter_channel)].offset);
+               break;
+       case PulseTrainGeneration:
+       /* data[0] contains the PULSE_WIDTH
+        data[1] contains the PULSE_PERIOD
+        @pre PULSE_PERIOD > PULSE_WIDTH > 0
+        The above periods must be expressed as a multiple of the
+        pulse frequency on the selected source, see the
+        Register-Level Programmer Manual p2-11 (pulse generation)
+       */
+               if ( (insn->data[1] > insn->data[0]) && (insn->data[0] > 0 ) )
+               {
+                       (ni_660x_gpct_config[subdev_channel]).data[0] = insn->data[0];
+                       (ni_660x_gpct_config[subdev_channel]).data[1] = insn->data[1];
+               }
+               else
+               {
+                       DPRINTK("%d \t %d\n",insn->data[1],insn->data[2]);
+                       DPRINTK("NI_660x: INSN_WRITE: PTG: Problem with Pulse params\n");
+                       return -EINVAL;
+               }
+               // Put PULSE_WIDTH as initial counter value into load register A
+               writel((ni_660x_gpct_config[subdev_channel]).data[0],dev->iobase
+                       + GPCT_OFFSET[chipset]
+                       + registerData[GxLoadARegister(counter_channel)].offset);
+               // Put (PULSE_PERIOD - PULSE_WIDTH) in the load register B
+               writel(  (ni_660x_gpct_config[subdev_channel]).data[1]
+                       - (ni_660x_gpct_config[subdev_channel]).data[0],
+                       dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[GxLoadBRegister(counter_channel)].offset);
+               break;
+       default: // Impossible
+               DPRINTK("NI_660X: INSN_WRITE: Functionality %d not implemented yet\n",
+               ni_660x_gpct_config[subdev_channel].App);
+               return -EINVAL;
+               break;
+       }
+  // return the number of samples written
+  return insn->n ;
+}
 
-  }
+/* Trigger instruction is currently only used to STOP the
+   pulsegenerator
+*/
+static int ni_660x_GPCT_inttrig(comedi_device *dev,
+                               comedi_subdevice *subdev,
+                               unsigned int trig_num)
+{
+       int subdev_channel = trig_num;
+       int chipset = GPCT_check_chipset_from_channel(dev, subdev_channel);
+       int counter_channel = GPCT_check_counter_channel_from_subdev_channel(subdev_channel);
 
-  return i;
+       DPRINTK("Triggering channel %d\n", subdev_channel);
+       if ( chipset == 1)
+       {
+               writel(CounterSwap,dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[ClockConfigRegister].offset);
+       }
+       else
+       {
+               writel(0x0,dev->iobase + GPCT_OFFSET[chipset]
+                       + registerData[ClockConfigRegister].offset);
+       }
+
+       // Reset the counter
+       writew(GxReset(counter_channel),dev->iobase + GPCT_OFFSET[chipset]
+               + registerData[GxxJointResetRegister(counter_channel)].offset);
+       return 0;
 }
 
 static int
@@ -332,3 +1452,63 @@ ni_660x_find_device(comedi_device *dev, int bus, int slot)
   mite_list_devices();
   return -EIO;
 }
+
+
+static int ni_660x_dio_insn_bits(comedi_device *dev,
+                                comedi_subdevice *s,
+                                comedi_insn *insn,
+                                lsampl_t *data)
+{
+  if(insn->n!=2)return -EINVAL;
+  /* The insn data is a write_mask in data[0] and the new data
+   * in data[1], each channel corresponding to a bit. */
+
+  // Check if we have to write some bits
+  if(data[0])
+    {
+      // Copied from skel.c, unverified
+      s->state &= ~data[0];
+      s->state |= data[0]&data[1];
+      /* Write out the new digital output lines */
+      /* Check if data < n_chan ?? */
+      writew(s->state,dev->iobase + registerData[STCDIOOutput].offset);
+    }
+  /* on return, data[1] contains the value of the digital
+   * input and output lines. */
+  data[1]=readw(dev->iobase + registerData[STCDIOParallelInput].offset);
+  return 2;
+}
+
+static int ni_660x_dio_insn_config(comedi_device *dev,
+                                  comedi_subdevice *s,
+                                  comedi_insn *insn,
+                                  lsampl_t *data)
+{
+  int chan=CR_CHAN(insn->chanspec);
+
+  if(insn->n!=1)return -EINVAL;
+
+  /* The input or output configuration of each digital line is
+   * configured by a special insn_config instruction.  chanspec
+   * contains the channel to be changed, and data[0] contains the
+   * value COMEDI_INPUT or COMEDI_OUTPUT. */
+
+  if(data[0]==COMEDI_OUTPUT)
+    {
+      s->io_bits |= 1<<chan;
+    }
+  else
+    {
+      s->io_bits &= ~(1<<chan);
+    }
+  // No GPCT_OFFSET[chipset] offset here??
+  writew(s->io_bits,dev->iobase + registerData[STCDIOControl].offset);
+  /* Should we do also something with the IO configuration registers,
+     see p 3-38 of register level prog. manual
+  */
+
+  return 1;
+  return -EINVAL;
+}
+
+