First pass at consistent support for NI general-purpose counters.
authorFrank Mori Hess <fmhess@speakeasy.net>
Fri, 8 Dec 2006 16:53:38 +0000 (16:53 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Fri, 8 Dec 2006 16:53:38 +0000 (16:53 +0000)
Will replace counter support in ni_mio_common.c and ni_660x.c.
Note quite done yet, but it compiles.

comedi/comedi_fops.c
comedi/drivers/Kbuild
comedi/drivers/Makefile.am
comedi/drivers/das08.c
comedi/drivers/ni_mio_common.c
comedi/drivers/ni_tio.c [new file with mode: 0644]
comedi/drivers/ni_tio.h [new file with mode: 0644]
include/linux/comedi.h

index 6c9a3009778ab6567ddece37044871817952c9fe..59cd89556aae9fe1f2d8eb8916cf863ef567fb55 100644 (file)
@@ -630,6 +630,8 @@ static int check_insn_config_length(comedi_insn *insn, lsampl_t *data)
        {
        case INSN_CONFIG_DIO_OUTPUT:
        case INSN_CONFIG_DIO_INPUT:
+       case INSN_CONFIG_ARM:
+       case INSN_CONFIG_DISARM:
                if(insn->n == 1) return 0;
                break;
        case INSN_CONFIG_DIO_QUERY:
@@ -637,16 +639,17 @@ static int check_insn_config_length(comedi_insn *insn, lsampl_t *data)
        case INSN_CONFIG_SERIAL_CLOCK:
        case INSN_CONFIG_BIDIRECTIONAL_DATA:
        case INSN_CONFIG_ALT_SOURCE:
-       case INSN_CONFIG_8254_SET_MODE:
+       case INSN_CONFIG_SET_COUNTER_MODE:
        case INSN_CONFIG_8254_READ_STATUS:
-       case INSN_CONFIG_SET_GATE_SRC:
-       case INSN_CONFIG_GET_GATE_SRC:
        case INSN_CONFIG_SET_ROUTING:
        case INSN_CONFIG_GET_ROUTING:
                if(insn->n == 2) return 0;
                break;
+       case INSN_CONFIG_SET_GATE_SRC:
+       case INSN_CONFIG_GET_GATE_SRC:
        case INSN_CONFIG_SET_CLOCK_SRC:
        case INSN_CONFIG_GET_CLOCK_SRC:
+       case INSN_CONFIG_GET_COUNTER_STATUS:
                if(insn->n == 3) return 0;
                break;
        case INSN_CONFIG_PWM_OUTPUT:
index e8d50aa30ea0583def887ba4f87a4dbd53cbeb0e..b09ffeda84114510a194f7d7f485eb96f6397fb4 100644 (file)
@@ -69,16 +69,17 @@ obj-m += me_daq.o
 obj-m += me4000.o
 obj-m += mpc624.o
 obj-m += multiq3.o
+obj-m += ni_6527.o
+obj-m += ni_65xx.o
 obj-m += ni_660x.o
 obj-m += ni_670x.o
+obj-m += ni_at_a2150.o
+obj-m += ni_at_ao.o
 obj-m += ni_atmio.o
+obj-m += ni_atmio16d.o
 obj-m += ni_pcimio.o
 obj-m += ni_pcidio.o
-obj-m += ni_6527.o
-obj-m += ni_65xx.o
-obj-m += ni_atmio16d.o
-obj-m += ni_at_a2150.o
-obj-m += ni_at_ao.o
+obj-m += ni_tio.o
 obj-m += pcm3724.o
 obj-m += pcm3730.o
 obj-m += pcmad.o
index 4a82d442375bcf1774c880cee815fcb3ce003eb8..01a8a94b34be7a6e05cf07da70ba99e3c9122b4f 100644 (file)
@@ -8,7 +8,8 @@ LINK = $(top_builddir)/modtool --link -o $@ \
        -i .mods/comedi_fc.o.symvers \
        -i .mods/das08.o.symvers \
        -i .mods/mite.o.symvers \
-       -i .mods/ni_labpc.o.symvers
+       -i .mods/ni_labpc.o.symvers \
+       -i .mods/ni_tio.o.symvers
 
 8255_ko_LINK = $(top_builddir)/modtool --link -o $@ \
        -i ../.mods/comedi.o.symvers
@@ -20,13 +21,15 @@ mite_ko_LINK = $(top_builddir)/modtool --link -o $@ \
        -i ../.mods/comedi.o.symvers
 ni_labpc_ko_LINK = $(top_builddir)/modtool --link -o $@ \
        -i ../.mods/comedi.o.symvers
+ni_tio_ko_LINK = $(top_builddir)/modtool --link -o $@ \
+       -i ../.mods/comedi.o.symvers
 
 8255_ko_CFLAGS = $(AM_CFLAGS) -DEXPORT_SYMTAB
 comedi_fc_ko_CFLAGS = $(AM_CFLAGS) -DEXPORT_SYMTAB
 das08_ko_CFLAGS = $(AM_CFLAGS) -DEXPORT_SYMTAB
 mite_ko_CFLAGS = $(AM_CFLAGS) -DEXPORT_SYMTAB
 ni_labpc_ko_CFLAGS = $(AM_CFLAGS) -DEXPORT_SYMTAB
-
+ni_tio_ko_CFLAGS = $(AM_CFLAGS) -DEXPORT_SYMTAB
 
 
 EXTRA_DIST = \
@@ -44,6 +47,7 @@ EXTRA_DIST = \
  ni_labpc.h \
  ni_mio_common.c \
  ni_stc.h \
+ ni_tio.h \
  plx9052.h \
  plx9080.h \
  rtd520.h \
@@ -144,6 +148,7 @@ module_PROGRAMS = \
  ni_atmio16d.ko \
  ni_at_a2150.ko \
  ni_at_ao.ko \
+ ni_tio.ko \
  pcm3724.ko \
  pcm3730.ko \
  pcmad.ko \
@@ -241,6 +246,7 @@ ni_labpc_ko_SOURCES = ni_labpc.c
 ni_labpc_cs_ko_SOURCES = ni_labpc_cs.c
 ni_daq_dio24_ko_SOURCES = ni_daq_dio24.c
 ni_at_ao_ko_SOURCES = ni_at_ao.c
+ni_tio_ko_SOURCES = ni_tio.c
 pcm3724_ko_SOURCES = pcm3724.c
 pcm3730_ko_SOURCES = pcm3730.c
 pcmad_ko_SOURCES = pcmad.c
index bbd5c0aaaec7c93a431759b1c4f126a556b85ece..0f2b967cb808a2c0295278f9684bf01ab26a00a9 100644 (file)
@@ -780,7 +780,7 @@ static int das08_counter_config(comedi_device *dev,comedi_subdevice *s,comedi_in
        if(insn->n != 2)return -EINVAL;
 
        switch (data[0]){
-       case INSN_CONFIG_8254_SET_MODE:
+       case INSN_CONFIG_SET_COUNTER_MODE:
                i8254_set_mode(&devpriv->i8254,chan,data[1]);
                break;
        case INSN_CONFIG_8254_READ_STATUS:
index cde29e4cf6eef1e714e6443d38a9cdf62a28e83e..df285d5ec1db36b2b45b24927f332eef12bb649e 100644 (file)
@@ -3803,8 +3803,6 @@ static int pack_ad8842(int addr,int val,int *bitstring)
  * Low level stuff...Each STC counter has two 24 bit load registers
  * (A&B).  Just make it easier to access them.
  *
- * These are inlined _only_ because they are used once in subsequent
- * code.  Otherwise they should not be inlined.
  */
 static inline void GPCT_Load_A(comedi_device *dev, int chan, unsigned int value)
 {
diff --git a/comedi/drivers/ni_tio.c b/comedi/drivers/ni_tio.c
new file mode 100644 (file)
index 0000000..bb545be
--- /dev/null
@@ -0,0 +1,858 @@
+/*
+  comedi/drivers/ni_tio.c
+  Support for NI general purpose counters
+
+  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_tio.o
+Description: National Instruments general purpose counters
+Devices:
+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: Thu Nov 16 09:50:32 EST 2006
+Status: experimental
+
+This module is not used directly by end-users.  Rather, it
+is used by other drivers (for example ni_660x and ni_pcimio)
+to provide support for NI's general purpose counters.  It was
+originally based on the counter code from ni_660x.c and
+ni_mio_common.c.
+
+References:
+DAQ 660x Register-Level Programmer Manual  (NI 370505A-01)
+DAQ 6601/6602 User Manual (NI 322137B-01)
+340934b.pdf  DAQ-STC reference manual
+
+*/
+
+/* TODO:
+need to provide init function(s) that will init hardware and
+gpct_counter struct
+
+ */
+
+#include "ni_tio.h"
+
+MODULE_AUTHOR("Comedi <comedi@comedi.org>");
+MODULE_DESCRIPTION("Comedi support for NI general-purpose counters");
+MODULE_LICENSE("GPL");
+
+static inline enum ni_gpct_register NITIO_Gi_Command_Reg(unsigned counter_index)
+{
+       switch(counter_index)
+       {
+       case 0:
+               return NITIO_G0_Command_Reg;
+               break;
+       case 1:
+               return NITIO_G1_Command_Reg;
+               break;
+       case 2:
+               return NITIO_G2_Command_Reg;
+               break;
+       case 3:
+               return NITIO_G3_Command_Reg;
+               break;
+       default:
+               BUG();
+               break;
+       }
+       return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_Counting_Mode_Reg(unsigned counter_index)
+{
+       switch(counter_index)
+       {
+       case 0:
+               return NITIO_G0_Counting_Mode_Reg;
+               break;
+       case 1:
+               return NITIO_G1_Counting_Mode_Reg;
+               break;
+       case 2:
+               return NITIO_G2_Counting_Mode_Reg;
+               break;
+       case 3:
+               return NITIO_G3_Counting_Mode_Reg;
+               break;
+       default:
+               BUG();
+               break;
+       }
+       return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_Input_Select_Reg(unsigned counter_index)
+{
+       switch(counter_index)
+       {
+       case 0:
+               return NITIO_G0_Input_Select_Reg;
+               break;
+       case 1:
+               return NITIO_G1_Input_Select_Reg;
+               break;
+       case 2:
+               return NITIO_G2_Input_Select_Reg;
+               break;
+       case 3:
+               return NITIO_G3_Input_Select_Reg;
+               break;
+       default:
+               BUG();
+               break;
+       }
+       return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gxx_Joint_Reset_Reg(unsigned counter_index)
+{
+       switch(counter_index)
+       {
+       case 0:
+       case 1:
+               return NITIO_G01_Joint_Reset_Reg;
+               break;
+       case 2:
+       case 3:
+               return NITIO_G23_Joint_Reset_Reg;
+               break;
+       default:
+               BUG();
+               break;
+       }
+       return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gxx_Status_Reg(unsigned counter_index)
+{
+       switch(counter_index)
+       {
+       case 0:
+       case 1:
+               return NITIO_G01_Status_Reg;
+               break;
+       case 2:
+       case 3:
+               return NITIO_G23_Status_Reg;
+               break;
+       default:
+               BUG();
+               break;
+       }
+       return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_LoadA_Reg(unsigned counter_index)
+{
+       switch(counter_index)
+       {
+       case 0:
+               return NITIO_G0_LoadA_Reg;
+               break;
+       case 1:
+               return NITIO_G1_LoadA_Reg;
+               break;
+       case 2:
+               return NITIO_G2_LoadA_Reg;
+               break;
+       case 3:
+               return NITIO_G3_LoadA_Reg;
+               break;
+       default:
+               BUG();
+               break;
+       }
+       return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_LoadB_Reg(unsigned counter_index)
+{
+       switch(counter_index)
+       {
+       case 0:
+               return NITIO_G0_LoadB_Reg;
+               break;
+       case 1:
+               return NITIO_G1_LoadB_Reg;
+               break;
+       case 2:
+               return NITIO_G2_LoadB_Reg;
+               break;
+       case 3:
+               return NITIO_G3_LoadB_Reg;
+               break;
+       default:
+               BUG();
+               break;
+       }
+       return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_Mode_Reg(unsigned counter_index)
+{
+       switch(counter_index)
+       {
+       case 0:
+               return NITIO_G0_Mode_Reg;
+               break;
+       case 1:
+               return NITIO_G1_Mode_Reg;
+               break;
+       case 2:
+               return NITIO_G2_Mode_Reg;
+               break;
+       case 3:
+               return NITIO_G3_Mode_Reg;
+               break;
+       default:
+               BUG();
+               break;
+       }
+       return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_SW_Save_Reg(int counter_index)
+{
+       switch(counter_index)
+       {
+       case 0:
+               return NITIO_G0_SW_Save_Reg;
+               break;
+       case 1:
+               return NITIO_G1_SW_Save_Reg;
+               break;
+       case 2:
+               return NITIO_G2_SW_Save_Reg;
+               break;
+       case 3:
+               return NITIO_G3_SW_Save_Reg;
+               break;
+       default:
+               BUG();
+               break;
+       }
+       return 0;
+}
+
+static inline enum ni_gpct_register NITIO_Gi_Second_Gate_Reg(int counter_index)
+{
+       switch(counter_index)
+       {
+       case 0:
+               return NITIO_G0_Second_Gate_Reg;
+               break;
+       case 1:
+               return NITIO_G1_Second_Gate_Reg;
+               break;
+       case 2:
+               return NITIO_G2_Second_Gate_Reg;
+               break;
+       case 3:
+               return NITIO_G3_Second_Gate_Reg;
+               break;
+       default:
+               BUG();
+               break;
+       }
+       return 0;
+}
+
+enum Gi_Auto_Increment_Reg_Bits
+{
+       Gi_Auto_Increment_Mask = 0xff
+};
+
+#define Gi_Up_Down_Shift 5
+enum Gi_Command_Reg_Bits
+{
+       Gi_Arm_Bit = 0x1,
+       Gi_Save_Trace_Bit = 0x2,
+       Gi_Load_Bit = 0x4,
+       Gi_Disarm_Bit = 0x10,
+       Gi_Up_Down_Mask = 0x3 << Gi_Up_Down_Shift,
+       Gi_Always_Down_Bits = 0x0 << Gi_Up_Down_Shift,
+       Gi_Always_Up_Bits = 0x1 << Gi_Up_Down_Shift,
+       Gi_Up_Down_Hardware_IO_Bits = 0x2 << Gi_Up_Down_Shift,
+       Gi_Up_Down_Hardware_Gate_Bits = 0x3  << Gi_Up_Down_Shift,
+       Gi_Write_Switch_Bit = 0x80,
+       Gi_Synchronize_Gate_Bit = 0x100,
+       Gi_Little_Big_Endian_Bit = 0x200,
+       Gi_Bank_Switch_Start_Bit = 0x400,
+       Gi_Bank_Switch_Mode_Bit = 0x800,
+       Gi_Bank_Switch_Enable_Bit = 0x1000,
+       Gi_Arm_Copy_Bit = 0x2000,
+       Gi_Save_Trace_Copy_Bit = 0x4000,
+       Gi_Disarm_Copy_Bit = 0x8000
+};
+
+#define Gi_Index_Phase_Bitshift 5
+enum Gi_Counting_Mode_Reg_Bits
+{
+       Gi_Counting_Mode_Mask = 0x7,
+       Gi_Counting_Mode_Normal_Bits = 0x0,
+       Gi_Counting_Mode_QuadratureX1_Bits = 0x1,
+       Gi_Counting_Mode_QuadratureX2_Bits = 0x2,
+       Gi_Counting_Mode_QuadratureX4_Bits = 0x3,
+       Gi_Counting_Mode_Two_Pulse_Bits = 0x4,
+       Gi_Counting_Mode_Sync_Source_Bits = 0x6,
+       Gi_Index_Mode_Bit = 0x10,
+       Gi_Index_Phase_Mask = 0x3 << Gi_Index_Phase_Bitshift,
+       Gi_Index_Phase_LowA_LowB = 0x0 << Gi_Index_Phase_Bitshift,
+       Gi_Index_Phase_LowA_HighB = 0x1 << Gi_Index_Phase_Bitshift,
+       Gi_Index_Phase_HighA_LowB = 0x2 << Gi_Index_Phase_Bitshift,
+       Gi_Index_Phase_HighA_HighB = 0x3 << Gi_Index_Phase_Bitshift,
+       Gi_Prescale_Bit = 0x1000,
+       /* must be set for clocks over 40MHz, which includes synchronous counting and quadrature modes */
+       Gi_Alternate_Sync_Bit = 0x2000
+};
+
+/*FIXME: these gates/sources are 660x specific.  See m-series example code for its gates/sources */
+#define Gi_Source_Select_Shift 2
+#define Gi_Gate_Select_Shift 7
+enum Gi_Input_Select_Bits
+{
+       Gi_Source_Select_Mask = 0x7c,
+       Gi_Source_Select_Timebase_1_Bits = 0x0 << Gi_Source_Select_Shift,       /* 20MHz */
+       Gi_Source_Select_Source_Pin_i_Bits = 0x1 << Gi_Source_Select_Shift,
+       Gi_Source_Select_Next_Gate_Bits = 0xa << Gi_Source_Select_Shift,
+       Gi_Source_Select_Timebase_2_Bits = 0x12 << Gi_Source_Select_Shift,      /* 100KHz */
+       Gi_Source_Select_Next_TC_Bits = 0x13 << Gi_Source_Select_Shift,
+       Gi_Source_Select_Timebase_3_Bits = 0x1e << Gi_Source_Select_Shift,      /* 80MHz */
+       Gi_Source_Select_Logic_Low_Bits = 0x1f << Gi_Source_Select_Shift,
+       Gi_Gate_Select_Mask = 0xf80,
+       Gi_Gate_Select_Source_Pin_Bits = 0x0 << Gi_Gate_Select_Shift,
+       Gi_Gate_Select_Gate_Pin_i_Bits = 0x1 << Gi_Gate_Select_Shift,
+       Gi_Gate_Select_Next_SRC_Bits = 0xa << Gi_Gate_Select_Shift,
+       Gi_Gate_Select_Next_Out_Bits = 0x14 << Gi_Gate_Select_Shift,
+       Gi_Gate_Select_Logic_Low_Bits = 0x1e << Gi_Gate_Select_Shift,
+       Gi_Gate_Select_Load_Source_Bit = 0x1000,
+       Gi_Or_Gate_Bit = 0x2000,
+       Gi_Output_Polarity_Bit = 0x4000,        /* set to invert */
+       Gi_Source_Polarity_Bit = 0x8000 /* set to invert */
+};
+static inline unsigned Gi_Source_Select_RTSI_Bits(unsigned n)
+{
+       BUG_ON(n > 6);
+       return (0xb + n) << Gi_Source_Select_Shift;
+}
+static inline unsigned Gi_Source_Select_Source_Pin_Bits(unsigned n)
+{
+       BUG_ON(n > 7);
+       return (0x2 + n) << Gi_Source_Select_Shift;
+}
+static inline unsigned Gi_Gate_Select_Gate_Pin_Bits(unsigned n)
+{
+       BUG_ON(n > 7);
+       return (0x2 + n) << Gi_Gate_Select_Shift;
+}
+static inline unsigned Gi_Gate_Select_RTSI_Bits(unsigned n)
+{
+       BUG_ON(n > 6);
+       return (0xb + n) << Gi_Gate_Select_Shift;
+}
+
+enum Gi_Mode_Bits
+{
+       Gi_Gating_Mode_Mask = 0x3,
+       Gi_Gating_Disabled_Bits = 0x0,
+       Gi_Level_Gating_Bits = 0x1,
+       Gi_Rising_Edge_Gating_Bits = 0x2,
+       Gi_Falling_Edge_Gating_Bits = 0x2,
+       Gi_Gate_On_Both_Edges_Bit = 0x4, /* used in conjunction with rising edge gating mode */
+       Gi_Trigger_Mode_for_Edge_Gate_Mask = 0x18,
+       Gi_Edge_Gate_Starts_Stops_Bits = 0x0,
+       Gi_Edge_Gate_Stops_Starts_Bits = 0x8,
+       Gi_Edge_Gate_Starts_Bits = 0x10,
+       Gi_Edge_Gate_No_Starts_or_Stops_Bits = 0x18,
+       Gi_Stop_Mode_Mask = 0x60,
+       Gi_Stop_on_Gate_Bits = 0x00,
+       Gi_Stop_on_Gate_or_TC_Bits = 0x20,
+       Gi_Stop_on_Gate_or_Second_TC_Bits = 0x40,
+       Gi_Load_Source_Select_Bit = 0x80,
+       Gi_Output_Mode_Mask = 0x300,
+       Gi_Output_TC_Pulse_Bits = 0x100,
+       Gi_Output_TC_Toggle_Bits = 0x200,
+       Gi_Output_TC_or_Gate_Toggle_Bits = 0x300,
+       Gi_Counting_Once_Mask = 0xc00,
+       Gi_No_Hardware_Disarm_Bits = 0x000,
+       Gi_Disarm_at_TC_Bits = 0x400,
+       Gi_Disarm_at_Gate_Bits = 0x800,
+       Gi_Disarm_at_TC_or_Gate_Bits = 0xc00,
+       Gi_Loading_On_TC_Bit = 0x1000,
+       Gi_Gate_Polarity_Bit = 0x2000,
+       Gi_Loading_On_Gate_Bit = 0x4000,
+       Gi_Reload_Source_Switching_Bit = 0x8000
+};
+#define Gi_Second_Gate_Select_Shift 7
+/*FIXME: these gates are 660x specific.  See m-series example code for its gates/sources*/
+enum Gi_Second_Gate_Bits
+{
+       Gi_Second_Gate_Mode_Bit = 0x1,
+       Gi_Second_Gate_Select_Mask = 0xf80,
+       Gi_Second_Gate_Select_Source_Pin_i_Bits = 0x0 << Gi_Second_Gate_Select_Shift,
+       Gi_Second_Gate_Select_Up_Down_Pin_i_Bits = 0x1 << Gi_Second_Gate_Select_Shift,
+       Gi_Second_Gate_Select_Next_SRC_Bits = 0xa << Gi_Second_Gate_Select_Shift,
+       Gi_Second_Gate_Select_Next_Out_Bits = 0x14 << Gi_Second_Gate_Select_Shift,
+       Gi_Second_Gate_Select_Selected_Gate_Bits = 0x1e << Gi_Second_Gate_Select_Shift,
+       Gi_Second_Gate_Select_Logic_Low_Bits = 0x1f << Gi_Second_Gate_Select_Shift,
+       Gi_Second_Gate_Polarity_Bit = 0x2000
+};
+static inline unsigned Gi_Second_Gate_Select_Up_Down_Pin_Bits(unsigned n)
+{
+       BUG_ON(n > 7);
+       return (0x2 + n) << 7;
+}
+static inline unsigned Gi_Second_Gate_Select_RTSI_Bits(unsigned n)
+{
+       BUG_ON(n > 6);
+       return (0xb + n) << 7;
+}
+
+enum Gxx_Status_Bits
+{
+       G0_Save_Bit = 0x1,
+       G1_Save_Bit = 0x2,
+       G0_Counting_Bit = 0x4,
+       G1_Counting_Bit = 0x8,
+       G0_Next_Load_Source_Bit = 0x10,
+       G1_Next_Load_Source_Bit = 0x20,
+       G0_Stale_Data_Bit = 0x40,
+       G1_Stale_Data_Bit = 0x80,
+       G0_Armed_Bit = 0x100,
+       G1_Armed_Bit = 0x200,
+       G0_No_Load_Between_Gates_Bit = 0x400,
+       G1_No_Load_Between_Gates_Bit = 0x800,
+       G0_TC_Error_Bit = 0x1000,
+       G1_TC_Error_Bit = 0x2000
+};
+static inline unsigned G_Counting_Bit(unsigned counter_index)
+{
+       if(counter_index % 2) return G1_Counting_Bit;
+       return G0_Counting_Bit;
+}
+static inline unsigned G_Armed_Bit(unsigned counter_index)
+{
+       if(counter_index % 2) return G1_Armed_Bit;
+       return G0_Armed_Bit;
+}
+
+static const lsampl_t counter_status_mask = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING;
+
+static int __init ni_tio_init_module(void)
+{
+       return 0;
+}
+module_init(ni_tio_init_module);
+
+static void __exit ni_tio_cleanup_module(void)
+{
+}
+module_exit(ni_tio_cleanup_module);
+
+static lsampl_t ni_tio_counter_status(struct ni_gpct *counter)
+{
+       lsampl_t status = 0;
+       const unsigned bits = counter->read_register(counter, NITIO_Gxx_Status_Reg(counter->counter_index));
+       if(bits & G_Armed_Bit(counter->counter_index))
+       {
+               status |= COMEDI_COUNTER_ARMED;
+               if(bits & G_Counting_Bit(counter->counter_index))
+                       status |= COMEDI_COUNTER_COUNTING;
+       }
+       return status;
+}
+
+static void ni_tio_set_sync_mode(struct ni_gpct *counter, int force_alt_sync)
+{
+       const unsigned counting_mode_reg = NITIO_Gi_Counting_Mode_Reg(counter->counter_index);
+       static const unsigned min_normal_sync_period_ns = 25;
+
+       if(counter->counting_mode_registers_present == 0) return;
+
+       switch(counter->regs[counting_mode_reg] & Gi_Counting_Mode_Mask)
+       {
+       case Gi_Counting_Mode_QuadratureX1_Bits:
+       case Gi_Counting_Mode_QuadratureX2_Bits:
+       case Gi_Counting_Mode_QuadratureX4_Bits:
+       case Gi_Counting_Mode_Sync_Source_Bits:
+               force_alt_sync = 1;
+               break;
+       default:
+               break;
+       }
+       /* It's not clear what we should do if clock_period_ns is set to zero, so we are not
+       using the alt sync in that case, but allow the caller to decide by using the
+       force_alt_sync parameter. */
+       /* FIXME: take into account possibility of the divide-by-8 prescale*/
+       if(force_alt_sync ||
+               (counter->clock_period_ns && counter->clock_period_ns < min_normal_sync_period_ns))
+       {
+               counter->regs[counting_mode_reg] |= Gi_Alternate_Sync_Bit;
+       }else
+       {
+               counter->regs[counting_mode_reg] &= ~Gi_Alternate_Sync_Bit;
+       }
+       counter->write_register(counter, counter->regs[counting_mode_reg], counting_mode_reg);
+}
+
+static int ni_tio_set_counter_mode(struct ni_gpct *counter, unsigned mode)
+{
+       const unsigned counting_mode_reg = NITIO_Gi_Counting_Mode_Reg(counter->counter_index);
+       const unsigned mode_reg = NITIO_Gi_Mode_Reg(counter->counter_index);
+       const unsigned command_reg = NITIO_Gi_Command_Reg(counter->counter_index);
+       const unsigned input_select_reg = NITIO_Gi_Input_Select_Reg(counter->counter_index);
+       /* these bits map directly on to the mode register */
+       static const unsigned mode_reg_direct_mask = NI_GPCT_GATE_ON_BOTH_EDGES_BIT |
+               NI_GPCT_EDGE_GATE_MODE_MASK | NI_GPCT_STOP_MODE_MASK |
+               NI_GPCT_OUTPUT_MODE_MASK | NI_GPCT_HARDWARE_DISARM_MASK |
+               NI_GPCT_LOADING_ON_TC_BIT | NI_GPCT_LOADING_ON_GATE_BIT;
+
+       switch(mode & NI_GPCT_RELOAD_SOURCE_MASK)
+       {
+       case NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS:
+               counter->regs[mode_reg] |= Gi_Reload_Source_Switching_Bit;
+               counter->regs[input_select_reg] &= ~Gi_Gate_Select_Load_Source_Bit;
+               break;
+       case NI_GPCT_RELOAD_SOURCE_GATE_SELECT_BITS:
+               counter->regs[input_select_reg] |= Gi_Gate_Select_Load_Source_Bit;
+               counter->regs[mode_reg] &= ~(Gi_Reload_Source_Switching_Bit | Gi_Gating_Mode_Mask);
+               counter->regs[mode_reg] |= Gi_Level_Gating_Bits;
+               break;
+       default:
+               break;
+       }
+
+       counter->regs[mode_reg] &= ~mode_reg_direct_mask;
+       counter->regs[mode_reg] |= mode & mode_reg_direct_mask;
+       counter->write_register(counter, counter->regs[mode_reg], mode_reg);
+
+       if(counter->counting_mode_registers_present)
+       {
+               counter->regs[counting_mode_reg] &= ~Gi_Counting_Mode_Mask;
+               counter->regs[counting_mode_reg] |= (mode >> NI_GPCT_COUNTING_MODE_SHIFT) & Gi_Counting_Mode_Mask;
+               counter->regs[counting_mode_reg] &= ~Gi_Index_Phase_Mask;
+               counter->regs[counting_mode_reg] |= ((mode >> NI_GPCT_INDEX_PHASE_BITSHIFT) << Gi_Index_Phase_Bitshift) & Gi_Index_Phase_Mask;
+               if(mode & NI_GPCT_INDEX_ENABLE_BIT)
+               {
+                       counter->regs[counting_mode_reg] |= Gi_Index_Mode_Bit;
+               }else
+               {
+                       counter->regs[counting_mode_reg] &= ~Gi_Index_Mode_Bit;
+               }
+               counter->write_register(counter, counter->regs[counting_mode_reg], counting_mode_reg);
+               ni_tio_set_sync_mode(counter, 0);
+       }
+
+       counter->regs[command_reg] &= ~Gi_Up_Down_Mask;
+       counter->regs[command_reg] |= ((mode >> NI_GPCT_COUNTING_DIRECTION_SHIFT) << Gi_Up_Down_Shift) & Gi_Up_Down_Mask;
+       counter->write_register(counter, counter->regs[command_reg], command_reg);
+
+       if(mode & NI_GPCT_OR_GATE_BIT)
+       {
+               counter->regs[input_select_reg] |= Gi_Or_Gate_Bit;
+       }else
+       {
+               counter->regs[input_select_reg] &= ~Gi_Or_Gate_Bit;
+       }
+       if(mode & NI_GPCT_INVERT_OUTPUT_BIT)
+       {
+               counter->regs[input_select_reg] |= Gi_Output_Polarity_Bit;
+       }else
+       {
+               counter->regs[input_select_reg] &= ~Gi_Output_Polarity_Bit;
+       }
+       counter->write_register(counter, counter->regs[input_select_reg], input_select_reg);
+
+       return 0;
+}
+
+static void ni_tio_arm(struct ni_gpct *counter, int arm)
+{
+       unsigned bits = counter->regs[NITIO_Gi_Command_Reg(counter->counter_index)];
+       if(arm)
+               bits |= Gi_Arm_Bit;
+       else
+               bits |= Gi_Disarm_Bit;
+       counter->write_register(counter, bits, NITIO_Gi_Command_Reg(counter->counter_index));
+}
+
+static int ni_tio_set_clock_src(struct ni_gpct *counter, lsampl_t clock_source, lsampl_t period_ns)
+{
+       const unsigned input_select_reg = NITIO_Gi_Input_Select_Reg(counter->counter_index);
+       const unsigned source_bits = CR_CHAN(clock_source) << Gi_Source_Select_Shift;
+
+/*FIXME: m-series has a couple extra gate sources using Gi_src_subselect bit */
+/*FIXME: add support for prescale in counting mode register */
+       if((source_bits & Gi_Source_Select_Mask) != source_bits) return -EINVAL;
+       counter->regs[input_select_reg] &= ~Gi_Source_Select_Mask;
+       counter->regs[input_select_reg] |= source_bits;
+       if(clock_source & CR_INVERT)
+               counter->regs[input_select_reg] |= Gi_Source_Polarity_Bit;
+       else
+               counter->regs[input_select_reg] &= ~Gi_Source_Polarity_Bit;
+       counter->write_register(counter, counter->regs[input_select_reg], input_select_reg);
+       counter->update_clock_period(counter, clock_source, period_ns);
+       ni_tio_set_sync_mode(counter, 0);
+       return 0;
+}
+
+static void ni_tio_get_clock_src(struct ni_gpct *counter, lsampl_t *clock_source, lsampl_t *period_ns)
+{
+       const unsigned input_select_reg = NITIO_Gi_Input_Select_Reg(counter->counter_index);
+       *clock_source = (counter->regs[input_select_reg] & Gi_Source_Select_Mask) >> Gi_Source_Select_Shift;
+       *period_ns = counter->clock_period_ns;
+}
+
+static int ni_tio_set_gate_src(struct ni_gpct *counter, unsigned gate_index, lsampl_t gate_source)
+{
+       const unsigned input_select_reg = NITIO_Gi_Input_Select_Reg(counter->counter_index);
+       const unsigned mode_reg = NITIO_Gi_Mode_Reg(counter->counter_index);
+       const unsigned second_gate_reg = NITIO_Gi_Second_Gate_Reg(counter->counter_index);
+       unsigned gate_select_bits;
+       unsigned second_gate_select_bits;
+
+       switch(gate_index)
+       {
+       case 0:
+               if(CR_CHAN(gate_source) == 0)
+               {
+                       counter->regs[mode_reg] &= ~Gi_Gating_Mode_Mask;
+                       counter->regs[mode_reg] |= Gi_Gating_Disabled_Bits;
+                       counter->write_register(counter, counter->regs[mode_reg], mode_reg);
+               }else
+               {
+                       gate_select_bits = (CR_CHAN(gate_source) - 1) << Gi_Gate_Select_Shift;
+                       if((gate_select_bits & Gi_Gate_Select_Mask) != gate_select_bits)
+                               return -EINVAL;
+                       counter->regs[input_select_reg] &= ~Gi_Gate_Select_Mask;
+                       counter->regs[input_select_reg] |= gate_select_bits;
+                       counter->write_register(counter, counter->regs[input_select_reg], input_select_reg);
+                       if(gate_source & CR_INVERT)
+                       {
+                               counter->regs[mode_reg] |= Gi_Gate_Polarity_Bit;
+                       }else
+                       {
+                               counter->regs[mode_reg] &= ~Gi_Gate_Polarity_Bit;
+                       }
+                       counter->regs[mode_reg] &= ~Gi_Gating_Mode_Mask;
+                       if(gate_source & CR_EDGE)
+                       {
+                               counter->regs[mode_reg] |= Gi_Level_Gating_Bits;
+                       }else
+                       {
+                               counter->regs[mode_reg] |= Gi_Rising_Edge_Gating_Bits;
+                       }
+                       counter->write_register(counter, counter->regs[mode_reg], mode_reg);
+               }
+               break;
+       case 1:
+               if(counter->second_gate_registers_present == 0) return -EINVAL;
+               if(CR_CHAN(gate_source) == 0)
+               {
+                       counter->regs[second_gate_reg] &= ~Gi_Second_Gate_Mode_Bit;
+               }else
+               {
+                       counter->regs[second_gate_reg] |= Gi_Second_Gate_Mode_Bit;
+                       second_gate_select_bits = (CR_CHAN(gate_source) - 1) << Gi_Second_Gate_Select_Shift;
+                       if((second_gate_select_bits & Gi_Second_Gate_Select_Mask) != second_gate_select_bits)
+                       {
+                               return -EINVAL;
+                       }
+                       counter->regs[second_gate_reg] &= ~Gi_Second_Gate_Select_Mask;
+                       counter->regs[second_gate_reg] |= second_gate_select_bits;
+                       if(gate_source & CR_INVERT)
+                       {
+                               counter->regs[second_gate_reg] |= Gi_Second_Gate_Polarity_Bit;
+                       }
+               }
+               counter->write_register(counter, counter->regs[second_gate_reg], second_gate_reg);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int ni_tio_get_gate_src(struct ni_gpct *counter, unsigned gate_index, lsampl_t *gate_source)
+{
+       const unsigned input_select_reg = NITIO_Gi_Input_Select_Reg(counter->counter_index);
+       const unsigned mode_reg = NITIO_Gi_Mode_Reg(counter->counter_index);
+       const unsigned second_gate_reg = NITIO_Gi_Second_Gate_Reg(counter->counter_index);
+
+       switch(gate_index)
+       {
+       case 0:
+               if((counter->regs[mode_reg] & Gi_Gating_Mode_Mask) == Gi_Gating_Disabled_Bits)
+               {
+                       *gate_source = 0;
+               }else
+               {
+                       *gate_source = (counter->regs[input_select_reg] & Gi_Gate_Select_Mask) >> Gi_Gate_Select_Shift;
+                       if(counter->regs[input_select_reg] & Gi_Gate_Polarity_Bit)
+                       {
+                               *gate_source |= CR_INVERT;
+                       }
+                       if((counter->regs[mode_reg] & Gi_Gating_Mode_Mask) != Gi_Level_Gating_Bits)
+                       {
+                               *gate_source |= CR_EDGE;
+                       }
+               }
+               break;
+       case 1:
+               if((counter->regs[mode_reg] & Gi_Gating_Mode_Mask) == Gi_Gating_Disabled_Bits ||
+                       (counter->regs[second_gate_reg] & Gi_Second_Gate_Mode_Bit) == 0)
+               {
+                       *gate_source = 0;
+               }else
+               {
+                       *gate_source = (counter->regs[second_gate_reg] & Gi_Second_Gate_Select_Mask) >> Gi_Second_Gate_Select_Shift;
+                       if(counter->regs[second_gate_reg] & Gi_Second_Gate_Polarity_Bit)
+                       {
+                               *gate_source |= CR_INVERT;
+                       }
+                       /* second gate can't have edge/level mode set independently */
+                       if((counter->regs[mode_reg] & Gi_Gating_Mode_Mask) != Gi_Level_Gating_Bits)
+                       {
+                               *gate_source |= CR_EDGE;
+                       }
+               }
+               break;
+       default:
+               return -EINVAL;
+               break;
+       }
+       return 0;
+}
+
+int ni_tio_insn_config(struct ni_gpct *counter,
+       comedi_insn *insn,
+       lsampl_t *data)
+{
+       switch(data[0])
+       {
+       case INSN_CONFIG_SET_COUNTER_MODE:
+               return ni_tio_set_counter_mode(counter, data[1]);
+               break;
+       case INSN_CONFIG_ARM:
+               ni_tio_arm(counter, 1);
+               return 0;
+               break;
+       case INSN_CONFIG_DISARM:
+               ni_tio_arm(counter, 0);
+               return 0;
+               break;
+       case INSN_CONFIG_GET_COUNTER_STATUS:
+               data[1] = ni_tio_counter_status(counter);
+               data[2] = counter_status_mask;
+               return 0;
+               break;
+       case INSN_CONFIG_SET_CLOCK_SRC:
+               return ni_tio_set_clock_src(counter, data[1], data[2]);
+               break;
+       case INSN_CONFIG_GET_CLOCK_SRC:
+               ni_tio_get_clock_src(counter, &data[1], &data[2]);
+               return 0;
+               break;
+       case INSN_CONFIG_SET_GATE_SRC:
+               return ni_tio_set_gate_src(counter, data[1], data[2]);
+               break;
+       case INSN_CONFIG_GET_GATE_SRC:
+               return ni_tio_get_gate_src(counter, data[1], &data[2]);
+               break;
+       default:
+               break;
+       }
+       return -EINVAL;
+}
+
+int ni_tio_rinsn(struct ni_gpct *counter,
+       comedi_insn *insn,
+       lsampl_t *data)
+{
+       const unsigned command_reg = NITIO_Gi_Command_Reg(counter->counter_index);
+       const unsigned channel = CR_CHAN(insn->chanspec);
+       unsigned first_read;
+       unsigned second_read;
+       unsigned correct_read;
+
+       if(insn->n < 1) return 0;
+       switch(channel)
+       {
+       case 0:
+               counter->regs[command_reg] &= ~Gi_Save_Trace_Bit;
+               counter->write_register(counter, counter->regs[command_reg], command_reg);
+               counter->write_register(counter, counter->regs[command_reg] | Gi_Save_Trace_Bit,
+                       command_reg);
+               /* The count doesn't get latched until the next clock edge, so it is possible the count
+               may change (once) while we are reading.  Since the read of the SW_Save_Reg isn't
+               atomic, we need to read twice and make sure the reading hasn't changed.  If it has,
+               a third read will be correct since the count value will definitely have latched by then. */
+               first_read = counter->read_register(counter, NITIO_Gi_SW_Save_Reg(counter->counter_index));
+               second_read = counter->read_register(counter, NITIO_Gi_SW_Save_Reg(counter->counter_index));
+               if(first_read != second_read)
+                       correct_read = counter->read_register(counter, NITIO_Gi_SW_Save_Reg(counter->counter_index));
+               else
+                       correct_read = first_read;
+               data[0] = correct_read;
+               return 0;
+               break;
+       case 1:
+               data[0] = counter->regs[NITIO_Gi_LoadA_Reg(counter->counter_index)];
+               break;
+       case 2:
+               data[0] = counter->regs[NITIO_Gi_LoadB_Reg(counter->counter_index)];
+               break;
+       };
+       return 0;
+}
+
+int ni_tio_winsn(struct ni_gpct *counter,
+       comedi_insn *insn,
+       lsampl_t * data)
+{
+       const unsigned channel = CR_CHAN(insn->chanspec);
+       const unsigned command_reg = NITIO_Gi_Command_Reg(counter->counter_index);
+       const unsigned mode_reg = NITIO_Gi_Mode_Reg(counter->counter_index);
+
+       if(insn->n < 1) return 0;
+       switch(channel)
+       {
+       case 0:
+               counter->regs[NITIO_Gi_LoadA_Reg(counter->counter_index)] = data[0];
+               counter->write_register(counter, data[0], NITIO_Gi_LoadA_Reg(counter->counter_index));
+               counter->regs[mode_reg] &= ~Gi_Load_Source_Select_Bit;
+               counter->write_register(counter, counter->regs[mode_reg], mode_reg);
+               counter->write_register(counter, counter->regs[command_reg] | Gi_Load_Bit, command_reg);
+               break;
+       case 1:
+               counter->regs[NITIO_Gi_LoadA_Reg(counter->counter_index)] = data[0];
+               counter->write_register(counter, data[0], NITIO_Gi_LoadA_Reg(counter->counter_index));
+               break;
+       case 2:
+               counter->regs[NITIO_Gi_LoadB_Reg(counter->counter_index)] = data[0];
+               counter->write_register(counter, data[0], NITIO_Gi_LoadB_Reg(counter->counter_index));
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
diff --git a/comedi/drivers/ni_tio.h b/comedi/drivers/ni_tio.h
new file mode 100644 (file)
index 0000000..9520198
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+    drivers/ni_tio.h
+    Header file for NI general purpose counter support code (ni_tio.c)
+
+    COMEDI - Linux Control and Measurement Device Interface
+
+    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.
+
+*/
+
+#ifndef _COMEDI_NI_TIO_H
+#define _COMEDI_NI_TIO_H
+
+#include <linux/comedidev.h>
+
+enum ni_gpct_register
+{
+       NITIO_G0_Autoincrement_Reg,
+       NITIO_G1_Autoincrement_Reg,
+       NITIO_G2_Autoincrement_Reg,
+       NITIO_G3_Autoincrement_Reg,
+       NITIO_G0_Command_Reg,
+       NITIO_G1_Command_Reg,
+       NITIO_G2_Command_Reg,
+       NITIO_G3_Command_Reg,
+       NITIO_G0_HW_Save_Reg,
+       NITIO_G1_HW_Save_Reg,
+       NITIO_G2_HW_Save_Reg,
+       NITIO_G3_HW_Save_Reg,
+       NITIO_G0_SW_Save_Reg,
+       NITIO_G1_SW_Save_Reg,
+       NITIO_G2_SW_Save_Reg,
+       NITIO_G3_SW_Save_Reg,
+       NITIO_G0_Mode_Reg,
+       NITIO_G1_Mode_Reg,
+       NITIO_G2_Mode_Reg,
+       NITIO_G3_Mode_Reg,
+       NITIO_G0_LoadA_Reg,
+       NITIO_G1_LoadA_Reg,
+       NITIO_G2_LoadA_Reg,
+       NITIO_G3_LoadA_Reg,
+       NITIO_G0_LoadB_Reg,
+       NITIO_G1_LoadB_Reg,
+       NITIO_G2_LoadB_Reg,
+       NITIO_G3_LoadB_Reg,
+       NITIO_G0_Input_Select_Reg,
+       NITIO_G1_Input_Select_Reg,
+       NITIO_G2_Input_Select_Reg,
+       NITIO_G3_Input_Select_Reg,
+       NITIO_G0_Counting_Mode_Reg,
+       NITIO_G1_Counting_Mode_Reg,
+       NITIO_G2_Counting_Mode_Reg,
+       NITIO_G3_Counting_Mode_Reg,
+       NITIO_G0_Second_Gate_Reg,
+       NITIO_G1_Second_Gate_Reg,
+       NITIO_G2_Second_Gate_Reg,
+       NITIO_G3_Second_Gate_Reg,
+       NITIO_G01_Status_Reg,
+       NITIO_G23_Status_Reg,
+       NITIO_G01_Joint_Reset_Reg,
+       NITIO_G23_Joint_Reset_Reg,
+       NITIO_G01_Joint_Status1_Reg,
+       NITIO_G23_Joint_Status1_Reg,
+       NITIO_G01_Joint_Status2_Reg,
+       NITIO_G23_Joint_Status2_Reg,
+       NITIO_Num_Registers,
+};
+
+#define MAX_NUM_NITIO_REGS 0x40
+struct ni_gpct
+{
+       comedi_device *dev;
+       unsigned counter_index;
+       unsigned chip_index;
+       void (*write_register)(struct ni_gpct *this, unsigned bits, enum ni_gpct_register);
+       unsigned (*read_register)(struct ni_gpct *this, enum ni_gpct_register);
+       /* update_clock_period() should update the clock_period_ns field appropriately for the
+       given source.  It is needed since the different hardware families provide different
+       clock sources. */
+       void (*update_clock_period)(struct ni_gpct *this, lsampl_t clock_source, lsampl_t period_ns);
+       lsampl_t clock_period_ns;
+       unsigned counting_mode_registers_present : 1;
+       unsigned second_gate_registers_present : 1;
+       unsigned regs[MAX_NUM_NITIO_REGS];
+};
+
+extern int ni_tio_rinsn(struct ni_gpct *counter,
+       comedi_insn *insn,
+       lsampl_t *data);
+extern int ni_tio_insn_config(struct ni_gpct *counter,
+       comedi_insn *insn,
+       lsampl_t *data);
+extern int ni_tio_winsn(struct ni_gpct *counter,
+       comedi_insn *insn,
+       lsampl_t * data);
+
+#endif /* _COMEDI_NI_TIO_H */
+
index e6371773ede2a4b0105d6a740cb103705b9235ef..6d6b200fdb7d4a58004d0e7d83eb2ecf728a2159 100644 (file)
@@ -239,6 +239,9 @@ enum configuration_ids
        INSN_CONFIG_DIO_QUERY = 28,
        INSN_CONFIG_PWM_OUTPUT = 29,
        INSN_CONFIG_GET_PWM_OUTPUT = 30,
+       INSN_CONFIG_ARM = 31,
+       INSN_CONFIG_DISARM = 32,
+       INSN_CONFIG_GET_COUNTER_STATUS = 33,
        INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR = 1001, // Use CTR as single pulsegenerator
        INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR = 1002, // Use CTR as pulsetraingenerator
        INSN_CONFIG_GPCT_QUADRATURE_ENCODER = 1003, // Use the counter as encoder
@@ -246,7 +249,8 @@ enum configuration_ids
        INSN_CONFIG_GET_GATE_SRC = 2002,        // Get gate source
        INSN_CONFIG_SET_CLOCK_SRC = 2003,       // Set master clock source
        INSN_CONFIG_GET_CLOCK_SRC = 2004,       // Get master clock source
-       INSN_CONFIG_8254_SET_MODE = 4097,
+       INSN_CONFIG_SET_COUNTER_MODE = 4097,
+       INSN_CONFIG_8254_SET_MODE = INSN_CONFIG_SET_COUNTER_MODE,       /* deprecated */
        INSN_CONFIG_8254_READ_STATUS = 4098,
        INSN_CONFIG_SET_ROUTING = 4099,
        INSN_CONFIG_GET_ROUTING = 4109,
@@ -473,35 +477,12 @@ struct comedi_bufinfo_struct{
 // Reset when index pulse arrives?
 #define GPCT_RESET_COUNTER_ON_INDEX 1
 
-/*
-  Counter clock and gate source configuration.
-
-  Four config commands to set/get the gate/clock source for a counter channel:
-
-  0 ID: INSN_CONFIG_SET_GATE_SRC
-  1 gate source
-
-  0 ID: INSN_CONFIG_GET_GATE_SRC
-  1 <-- Current gate source returned here.
-
-  0 ID: INSN_CONFIG_SET_CLOCK_SRC
-  1 clock source
-
-  0 ID: INSN_CONFIG_GET_CLOCK_SRC
-  1 <-- Current clock source returned here.
-
-  Notes:
-  1. Gate and clock sources are hardware-specific.
-  2. 'chanspec' indicates the channel to configure (if the hardware supports
-     per-channel configuration of the gate and clock sources).
-*/
-
 /*
   8254 specific configuration.
 
   It supports two config commands:
 
-  0 ID: INSN_CONFIG_8254_SET_MODE
+  0 ID: INSN_CONFIG_SET_COUNTER_MODE
   1 8254 Mode
     I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
     OR'ed with:
@@ -527,6 +508,58 @@ enum i8254_mode
        I8254_BINARY = 0
 };
 
+/* mode bits for NI general-purpose counters, set with INSN_CONFIG_SET_COUNTER_MODE */
+#define NI_GPCT_COUNTING_MODE_SHIFT 16
+#define NI_GPCT_INDEX_PHASE_BITSHIFT 20
+#define NI_GPCT_COUNTING_DIRECTION_SHIFT 24
+enum ni_gpct_mode_bits
+{
+       NI_GPCT_GATE_ON_BOTH_EDGES_BIT = 0x4,
+       NI_GPCT_EDGE_GATE_MODE_MASK = 0x18,
+       NI_GPCT_EDGE_GATE_STARTS_STOPS_BITS = 0x0,
+       NI_GPCT_EDGE_GATE_STOPS_STARTS_BITS = 0x8,
+       NI_GPCT_EDGE_GATE_STARTS_BITS = 0x10,
+       NI_GPCT_EDGE_GATE_NO_STARTS_NO_STOPS_BITS = 0x18,
+       NI_GPCT_STOP_MODE_MASK = 0x60,
+       NI_GPCT_STOP_ON_GATE_BITS = 0x00,
+       NI_GPCT_STOP_ON_GATE_OR_TC_BITS = 0x20,
+       NI_GPCT_STOP_ON_GATE_OR_SECOND_TC_BITS = 0x40,
+       NI_GPCT_OUTPUT_MODE_MASK = 0x300,
+       NI_GPCT_OUTPUT_TC_PULSE_BITS = 0x100,
+       NI_GPCT_OUTPUT_TC_TOGGLE_BITS = 0x200,
+       NI_GPCT_OUTPUT_TC_OR_GATE_TOGGLE_BITS = 0x300,
+       NI_GPCT_HARDWARE_DISARM_MASK = 0xc00,
+       NI_GPCT_NO_HARDWARE_DISARM_BITS = 0x000,
+       NI_GPCT_DISARM_AT_TC_BITS = 0x400,
+       NI_GPCT_DISARM_AT_GATE_BITS = 0x800,
+       NI_GPCT_DISARM_AT_TC_OR_GATE_BITS = 0xc00,
+       NI_GPCT_LOADING_ON_TC_BIT = 0x1000,
+       NI_GPCT_LOADING_ON_GATE_BIT = 0x4000,
+       NI_GPCT_COUNTING_MODE_MASK = 0x7 << NI_GPCT_COUNTING_MODE_SHIFT,
+       NI_GPCT_COUNTING_MODE_NORMAL_BITS = 0x0 << NI_GPCT_COUNTING_MODE_SHIFT,
+       NI_GPCT_COUNTING_MODE_QUADRATURE_X1_BITS = 0x1 << NI_GPCT_COUNTING_MODE_SHIFT,
+       NI_GPCT_COUNTING_MODE_QUADRATURE_X2_BITS = 0x2 << NI_GPCT_COUNTING_MODE_SHIFT,
+       NI_GPCT_COUNTING_MODE_QUADRATURE_X4_BITS = 0x3 << NI_GPCT_COUNTING_MODE_SHIFT,
+       NI_GPCT_COUNTING_MODE_TWO_PULSE_BITS = 0x4 << NI_GPCT_COUNTING_MODE_SHIFT,
+       NI_GPCT_COUNTING_MODE_SYNC_SOURCE_BITS = 0x6 << NI_GPCT_COUNTING_MODE_SHIFT,
+       NI_GPCT_INDEX_PHASE_MASK = 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+       NI_GPCT_INDEX_PHASE_LOW_A_LOW_B_BITS = 0x0 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+       NI_GPCT_INDEX_PHASE_LOW_A_HIGH_B_BITS = 0x1 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+       NI_GPCT_INDEX_PHASE_HIGH_A_LOW_B_BITS = 0x2 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+       NI_GPCT_INDEX_PHASE_HIGH_A_HIGH_B_BITS = 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+       NI_GPCT_INDEX_ENABLE_BIT = 0x400000,
+       NI_GPCT_COUNTING_DIRECTION_MASK = 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+       NI_GPCT_COUNTING_DIRECTION_DOWN_BITS = 0x00 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+       NI_GPCT_COUNTING_DIRECTION_UP_BITS = 0x1 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+       NI_GPCT_COUNTING_DIRECTION_HW_UP_DOWN_BITS = 0x2 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+       NI_GPCT_COUNTING_DIRECTION_HW_GATE_BITS = 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+       NI_GPCT_RELOAD_SOURCE_MASK = 0xc000000,
+       NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS = 0x4000000,
+       NI_GPCT_RELOAD_SOURCE_GATE_SELECT_BITS = 0x8000000,
+       NI_GPCT_OR_GATE_BIT = 0x10000000,
+       NI_GPCT_INVERT_OUTPUT_BIT = 0x20000000
+};
+
 /* clock sources for ni mio boards and INSN_CONFIG_SET_CLOCK_SRC */
 enum ni_mio_clock_source
 {
@@ -614,6 +647,14 @@ static inline unsigned NI_EXT_RTSI(unsigned rtsi_channel)
        else return 19 + rtsi_channel;
 }
 
+/* status bits for INSN_CONFIG_GET_COUNTER_STATUS */
+enum comedi_counter_status_flags
+{
+       COMEDI_COUNTER_ARMED = 0x1,
+       COMEDI_COUNTER_COUNTING = 0x2,
+       COMEDI_COUNTER_TERMINAL_COUNT = 0x4,
+};
+
 #ifdef __cplusplus
 }
 #endif