6713 is completely failing
[comedilib.git] / comedi_calibrate / ni.c
index c4975c75afb4e4084150590cfa7cfaeaeddfd13d..c028fbd0603bfc161d48124ba4c39eac6df7233d 100644 (file)
@@ -2,28 +2,24 @@
    A little auto-calibration utility, for boards
    that support it.
 
-   Right now, it only supports NI E series boards,
-   but it should be easily portable.
-
-   A few things need improvement here:
-    - current system gets "close", but doesn't
-      do any fine-tuning
-    - no pre/post gain discrimination for the
-      A/D zero offset.
-    - should read (and use) the actual reference
-      voltage value from eeprom
-    - statistics would be nice, to show how good
-      the calibration is.
-    - doesn't check unipolar ranges
-    - "alternate calibrations" would be cool--to
-      accurately measure 0 in a unipolar range
-    - more portable
+   copyright (C) 1999,2000,2001,2002 by David Schleef
+   copyright (C) 2003 by Frank Mori Hess
+
  */
 
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License as        *
+ *   published by                                                          *
+ *   the Free Software Foundation; either version 2.1 of the License, or   *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
 #define _GNU_SOURCE
 
 #include <stdio.h>
-#include <comedilib.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
@@ -32,6 +28,7 @@
 #include <math.h>
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 
 #include "calib.h"
 
@@ -42,92 +39,255 @@ struct board_struct{
        char *name;
        int status;
        int (*cal)( calibration_setup_t *setup);
+       void (*setup_observables)( calibration_setup_t *setup );
+       int ref_eeprom_lsb;
+       int ref_eeprom_msb;
 };
 
-int ni_setup_board( calibration_setup_t *setup , const char *device_name );
-void ni_setup_observables( calibration_setup_t *setup );
-
-int cal_ni_at_mio_16e_2(calibration_setup_t *setup);
-int cal_ni_daqcard_ai_16xe_50(calibration_setup_t *setup);
-int cal_ni_at_mio_16e_1(calibration_setup_t *setup);
-int cal_ni_pci_mio_16e_1(calibration_setup_t *setup);
-int cal_ni_pci_6025e(calibration_setup_t *setup);
-int cal_ni_pci_6035e(calibration_setup_t *setup);
-int cal_ni_pci_6071e(calibration_setup_t *setup);
-int cal_ni_pxi_6071e(calibration_setup_t *setup);
-int cal_ni_at_mio_16e_10(calibration_setup_t *setup);
-int cal_ni_pci_mio_16xe_50(calibration_setup_t *setup);
-int cal_ni_pci_6023e(calibration_setup_t *setup);
-int cal_ni_pci_6024e(calibration_setup_t *setup);
-int cal_ni_at_mio_16xe_50(calibration_setup_t *setup);
-int cal_ni_pci_mio_16xe_10(calibration_setup_t *setup);
-int cal_ni_pci_6052e(calibration_setup_t *setup);
-int cal_ni_pci_mio_16e_4(calibration_setup_t *setup);
+static int ni_setup_board( calibration_setup_t *setup , const char *device_name );
+static void ni_setup_observables( calibration_setup_t *setup );
+static void ni_setup_observables_611x( calibration_setup_t *setup );
+static void ni67xx_setup_observables( calibration_setup_t *setup );
+
+static int cal_ni_at_mio_16e_2(calibration_setup_t *setup);
+static int cal_ni_daqcard_ai_16xe_50(calibration_setup_t *setup);
+static int cal_ni_at_mio_16e_1(calibration_setup_t *setup);
+static int cal_ni_pci_mio_16e_1(calibration_setup_t *setup);
+static int cal_ni_pci_6014(calibration_setup_t *setup);
+static int cal_ni_pci_6024e(calibration_setup_t *setup);
+static int cal_ni_pci_6025e(calibration_setup_t *setup);
+static int cal_ni_pci_6032e(calibration_setup_t *setup);
+static int cal_ni_pci_6035e(calibration_setup_t *setup);
+static int cal_ni_pci_6036e(calibration_setup_t *setup);
+static int cal_ni_pci_6071e(calibration_setup_t *setup);
+static int cal_ni_pxi_6071e(calibration_setup_t *setup);
+static int cal_ni_at_mio_16e_10(calibration_setup_t *setup);
+static int cal_ni_pci_mio_16xe_50(calibration_setup_t *setup);
+static int cal_ni_pci_6023e(calibration_setup_t *setup);
+static int cal_ni_at_mio_16xe_50(calibration_setup_t *setup);
+static int cal_ni_pci_mio_16xe_10(calibration_setup_t *setup);
+static int cal_ni_pci_6052e(calibration_setup_t *setup);
+static int cal_ni_daqcard_ai_16e_4(calibration_setup_t *setup);
+static int cal_ni_pci_611x(calibration_setup_t *setup);
+static int cal_ni_pci_mio_16e_4(calibration_setup_t *setup);
+static int cal_ni_daqcard_6062e(calibration_setup_t *setup);
+static int cal_ni_daqcard_6024e(calibration_setup_t *setup);
+static int cal_ni_daqcard_6036e(calibration_setup_t *setup);
+static int cal_ni_pci_6711(calibration_setup_t *setup);
+
+static double ni_get_reference( calibration_setup_t *setup, int lsb_loc,int msb_loc);
 
 static struct board_struct boards[]={
-       { "at-mio-16e-2",       STATUS_DONE,    cal_ni_at_mio_16e_2 },
-       { "DAQCard-ai-16xe-50", STATUS_DONE,    cal_ni_daqcard_ai_16xe_50 },
-       { "at-mio-16xe-50",     STATUS_SOME,    cal_ni_at_mio_16xe_50 },
-       { "at-mio-16e-1",       STATUS_SOME,    cal_ni_at_mio_16e_1 },
-       { "pci-mio-16e-1",      STATUS_DONE,    cal_ni_pci_mio_16e_1 },
-       { "pci-6025e",          STATUS_SOME,    cal_ni_pci_6025e },
-       { "pci-6035e",          STATUS_DONE,    cal_ni_pci_6035e },
-       { "pci-6071e",          STATUS_SOME,    cal_ni_pci_6071e },
-       { "pxi-6071e",          STATUS_GUESS,   cal_ni_pxi_6071e },
-       { "at-mio-16e-10",      STATUS_GUESS,   cal_ni_at_mio_16e_10 },
-       { "pci-mio-16xe-50",    STATUS_SOME,    cal_ni_pci_mio_16xe_50 },
-       { "pci-6023e",          STATUS_SOME,    cal_ni_pci_6023e },
-       { "pci-mio-16xe-10",    STATUS_DONE,    cal_ni_pci_mio_16xe_10 },
-       { "pci-6052e",          STATUS_DONE,    cal_ni_pci_6052e },
-       { "pci-6024e",          STATUS_SOME,    cal_ni_pci_6024e },
-       { "pci-mio-16e-4",      STATUS_SOME,    cal_ni_pci_mio_16e_4 },
+       { "at-ai-16xe-10", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1b7, 0x1b8 },
+       { "at-mio-16de-10", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1a7, 0x1a8 },
+       { "at-mio-16e-1", STATUS_DONE, cal_ni_at_mio_16e_1, ni_setup_observables, 0x1a9, 0x1aa },
+       { "at-mio-16e-2", STATUS_DONE, cal_ni_at_mio_16e_2, ni_setup_observables, 0x1a9, 0x1aa },
+       { "at-mio-16e-10", STATUS_DONE, cal_ni_at_mio_16e_10, ni_setup_observables, 0x1a7, 0x1a8 },
+       { "at-mio-16xe-10", STATUS_UNKNOWN, NULL, ni_setup_observables, 0x1b7, 0x1b8 },
+       { "at-mio-16xe-50", STATUS_SOME, cal_ni_at_mio_16xe_50, ni_setup_observables, 0x1b5, 0x1b6 },
+       { "DAQCard-ai-16e-4", STATUS_DONE, cal_ni_daqcard_ai_16e_4, ni_setup_observables, 0x1b5, 0x1b6 },
+       { "DAQCard-ai-16xe-50", STATUS_DONE, cal_ni_daqcard_ai_16xe_50, ni_setup_observables, 0x1be, 0x1bf },
+       { "DAQCard-6024E", STATUS_SOME, cal_ni_daqcard_6024e, ni_setup_observables, -1, -1 },
+       { "DAQCard-6036E", STATUS_DONE, cal_ni_daqcard_6036e, ni_setup_observables, 0x1ab, 0x1ac },
+       { "DAQCard-6062E", STATUS_DONE, cal_ni_daqcard_6062e, ni_setup_observables, 0x1a9, 0x1aa },
+       { "pci-mio-16e-1", STATUS_DONE, cal_ni_pci_mio_16e_1, ni_setup_observables, 0x1a9, 0x1aa },
+       { "pci-mio-16e-4", STATUS_SOME, cal_ni_pci_mio_16e_4, ni_setup_observables, 0x1a9, 0x1aa },
+       { "pci-mio-16xe-10", STATUS_DONE,       cal_ni_pci_mio_16xe_10, ni_setup_observables, 0x1ae, 0x1af },
+       { "pci-mio-16xe-50", STATUS_SOME, cal_ni_pci_mio_16xe_50, ni_setup_observables, 0x1b5, 0x1b6 },
+       { "pci-6014", STATUS_SOME, cal_ni_pci_6014, ni_setup_observables, 0x1ab, 0x1ac },
+       { "pci-6023e", STATUS_DONE, cal_ni_pci_6023e, ni_setup_observables, 0x1bb, 0x1bc },
+       { "pci-6024e", STATUS_SOME, cal_ni_pci_6024e, ni_setup_observables, 0x1af, 0x1b0 },
+       { "pci-6025e", STATUS_SOME, cal_ni_pci_6025e, ni_setup_observables, 0x1af, 0x1b0 },
+       { "pci-6031e", STATUS_DONE, cal_ni_pci_mio_16xe_10, ni_setup_observables, 0x1ae, 0x1af },
+       { "pci-6032e", STATUS_DONE, cal_ni_pci_6032e, ni_setup_observables, 0x1ae, 0x1af },
+       { "pci-6033e", STATUS_DONE, cal_ni_pci_6032e, ni_setup_observables, 0x1b7, 0x1b8 },
+       { "pci-6034e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pci-6035e", STATUS_DONE, cal_ni_pci_6035e, ni_setup_observables, 0x1af, 0x1b0 },
+       { "pci-6036e", STATUS_DONE, cal_ni_pci_6036e, ni_setup_observables, 0x1ab, 0x1ac },
+       { "pci-6052e", STATUS_DONE, cal_ni_pci_6052e, ni_setup_observables, 0x19f, 0x1a0 },
+       { "pci-6071e", STATUS_DONE, cal_ni_pci_6071e, ni_setup_observables, 0x1a9, 0x1aa },
+       { "pci-6110", STATUS_DONE, cal_ni_pci_611x, ni_setup_observables_611x, 0x1d4, 0x1d5 },
+       { "pci-6111", STATUS_DONE, cal_ni_pci_611x, ni_setup_observables_611x, 0x1d4, 0x1d5 },
+       { "pxi-6025e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pxi-6030e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pxi-6031e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pxi-6040e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pxi-6052e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pxi-6070e", STATUS_UNKNOWN, NULL, ni_setup_observables, -1, -1 },
+       { "pxi-6071e", STATUS_GUESS, cal_ni_pxi_6071e, ni_setup_observables, -1, -1 },
+       { "pci-6711", STATUS_DONE, cal_ni_pci_6711, ni67xx_setup_observables, 0x1d4, 0x1d5},
+       { "pci-6713", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
+       { "pci-6731", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
+       { "pci-6733", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
+       { "pxi-6711", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
+       { "pxi-6713", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
+       { "pxi-6731", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
+       { "pxi-6733", STATUS_GUESS, cal_ni_pci_6711, ni67xx_setup_observables, -1, -1},
 #if 0
-//     { "at-mio-16de-10",     cal_ni_unknown },
        { "at-mio-64e-3",       cal_ni_16e_1 },
-//     { "at-mio-16xe-50",     cal_ni_unknown },
-//     { "at-mio-16xe-10",     cal_ni_unknown },
-//     { "at-ai-16xe-10",      cal_ni_unknown },
-//     { "pxi-6030e",          cal_ni_unknown },
-//     { "pxi-6040e",          cal_ni_unknown },
-//     { "pci-6031e",          cal_ni_unknown },
-//     { "pci-6032e",          cal_ni_unknown },
-//     { "pci-6033e",          cal_ni_unknown },
-       { "pxi-6025e",          cal_ni_6023e }, // guess
-       { "pci-6034e",          cal_ni_6023e }, // guess
-//     { "pci-6110e",          cal_ni_unknown },
-//     { "pci-6111e",          cal_ni_unknown },
-//     { "pci-6711",           cal_ni_unknown },
-//     { "pci-6713",           cal_ni_unknown },
-//     { "pxi-6070e",          cal_ni_unknown },
-//     { "pxi-6052e",          cal_ni_unknown },
-//     { "DAQCard-ai-16e-4",   cal_ni_unknown },
-//     { "DAQCard-6062e",      cal_ni_unknown },
-//     { "DAQCard-6024e",      cal_ni_unknown },
 #endif
 };
 #define n_boards (sizeof(boards)/sizeof(boards[0]))
 
-enum {
+static const int ni_num_observables = 20;
+enum observables{
        ni_zero_offset_low = 0,
        ni_zero_offset_high,
        ni_reference_low,
-       ni_unip_offset_low,
+       ni_unip_zero_offset_low,
+       ni_unip_zero_offset_high,
+       ni_unip_reference_low,
        ni_ao0_zero_offset,
        ni_ao0_reference,
+       ni_ao0_linearity,
        ni_ao1_zero_offset,
        ni_ao1_reference,
+       ni_ao1_linearity,
+       ni_ao0_unip_zero_offset,
+       ni_ao0_unip_reference,
+       ni_ao0_unip_low_linearity,
+       ni_ao0_unip_mid_linearity,
+       ni_ao1_unip_zero_offset,
+       ni_ao1_unip_reference,
+       ni_ao1_unip_low_linearity,
+       ni_ao1_unip_mid_linearity,
+};
+static inline unsigned int ni_ao_zero_offset( unsigned int channel )
+{
+       if( channel ) return ni_ao1_zero_offset;
+       else return ni_ao0_zero_offset;
+}
+static inline unsigned int ni_ao_reference( unsigned int channel )
+{
+       if( channel ) return ni_ao1_reference;
+       else return ni_ao0_reference;
+}
+static inline unsigned int ni_ao_mid_linearity( unsigned int channel )
+{
+       if( channel ) return ni_ao1_linearity;
+       else return ni_ao0_linearity;
+}
+static inline unsigned int ni_ao_unip_zero_offset( unsigned int channel )
+{
+       if( channel ) return ni_ao1_unip_zero_offset;
+       else return ni_ao0_unip_zero_offset;
+}
+static inline unsigned int ni_ao_unip_reference( unsigned int channel )
+{
+       if( channel ) return ni_ao1_unip_reference;
+       else return ni_ao0_unip_reference;
+}
+static inline unsigned int ni_ao_unip_low_linearity( unsigned int channel )
+{
+       if( channel ) return ni_ao1_unip_low_linearity;
+       else return ni_ao0_unip_low_linearity;
+}
+static inline unsigned int ni_ao_unip_mid_linearity( unsigned int channel )
+{
+       if( channel ) return ni_ao1_unip_mid_linearity;
+       else return ni_ao0_unip_mid_linearity;
+}
+
+static const int num_ao_observables_611x = 4;
+static int ni_ao_zero_offset_611x( const calibration_setup_t *setup,
+       unsigned int channel, unsigned int range ) {
+       assert( range == 0 );
+       return 2 * channel;
+};
+static int ni_ao_reference_611x( const calibration_setup_t *setup,
+       unsigned int channel, unsigned int range ) {
+       assert( range == 0 );
+       return 2 * channel + 1;
+};
+static int ni_zero_offset_611x( const calibration_setup_t *setup,
+       unsigned int channel, unsigned int range ) {
+       return num_ao_observables_611x + 8 * range + 2 * channel;
 };
+static int ni_reference_611x( const calibration_setup_t *setup,
+       unsigned int channel, unsigned int range ) {
+       return num_ao_observables_611x + 8 * range + 2 * channel + 1;
+};
+
+enum reference_sources {
+       REF_GND_GND = 0,
+       REF_AOGND_AIGND = 1,
+       REF_DAC0_GND = 2,
+       REF_DAC1_GND = 3,
+       REF_CALSRC_CALSRC = 4,
+       REF_CALSRC_GND = 5,
+       REF_DAC0_CALSRC = 6,
+       REF_DAC1_CALSRC = 7,
+};
+static inline unsigned int REF_DAC_GND( unsigned int channel )
+{
+       if( channel ) return REF_DAC1_GND;
+       else return REF_DAC0_GND;
+}
+static inline unsigned int REF_DAC_CALSRC( unsigned int channel )
+{
+       if( channel ) return REF_DAC1_CALSRC;
+       else return REF_DAC0_CALSRC;
+}
+
+static struct board_struct* ni_board( calibration_setup_t *setup )
+{
+       return setup->private_data;
+}
+
+typedef struct
+{
+       int adc_pregain_offset;
+       int adc_postgain_offset;
+       int adc_gain;
+       int adc_pregain_offset_fine;
+       int adc_postgain_offset_fine;
+       int adc_gain_fine;
+       int adc_unip_offset;
+       int adc_unip_offset_fine;
+       int dac_offset[ 2 ];
+       int dac_offset_fine[ 2 ];
+       int dac_gain[ 2 ];
+       int dac_gain_fine[ 2 ];
+       int dac_linearity[ 2 ];
+} ni_caldac_layout_t;
+
+static int cal_ni_generic( calibration_setup_t *setup,
+       const ni_caldac_layout_t *layout );
+
+static inline void init_ni_caldac_layout( ni_caldac_layout_t *layout )
+{
+       int i;
+
+       layout->adc_pregain_offset = -1;
+       layout->adc_postgain_offset = -1;
+       layout->adc_gain = -1;
+       layout->adc_unip_offset = -1;
+       layout->adc_unip_offset_fine = -1;
+       layout->adc_pregain_offset_fine = -1;
+       layout->adc_postgain_offset_fine = -1;
+       layout->adc_gain_fine = -1;
+       for( i = 0; i < 2; i++ )
+       {
+               layout->dac_offset[ i ] = -1;
+               layout->dac_offset_fine[ i ] = -1;
+               layout->dac_gain[ i ] = -1;
+               layout->dac_gain_fine[ i ] = -1;
+               layout->dac_linearity[ i ] = -1;
+       }
+}
 
 int ni_setup( calibration_setup_t *setup , const char *device_name )
 {
-       ni_setup_board( setup, device_name );
-       ni_setup_observables( setup );
+       int retval;
+
+       retval = ni_setup_board( setup, device_name );
+       if( retval < 0 ) return retval;
        setup_caldacs( setup, setup->caldac_subdev );
 
        return 0;
 }
 
-int ni_setup_board( calibration_setup_t *setup, const char *device_name )
+static int ni_setup_board( calibration_setup_t *setup, const char *device_name )
 {
        int i;
 
@@ -135,571 +295,1348 @@ int ni_setup_board( calibration_setup_t *setup, const char *device_name )
                if(!strcmp( device_name, boards[i].name )){
                        setup->status = boards[i].status;
                        setup->do_cal = boards[i].cal;
+                       setup->private_data = &boards[ i ];
+                       boards[i].setup_observables( setup );
                        break;
                }
        }
+       if( i == n_boards ) return -1;
        return 0;
 }
 
-void ni_setup_observables( calibration_setup_t *setup )
+static void ni_setup_ao_observables( calibration_setup_t *setup )
+{
+       observable *o;
+       comedi_insn tmpl, po_tmpl;
+       unsigned int channel;
+       int ai_bipolar_lowgain;
+       int ao_bipolar_lowgain;
+       int ao_unipolar_lowgain;
+
+       ai_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->ad_subdev);
+       assert(ai_bipolar_lowgain >= 0);
+       ao_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->da_subdev);
+       assert(ao_bipolar_lowgain >= 0);
+       ao_unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->da_subdev);
+
+       memset(&tmpl,0,sizeof(tmpl));
+       tmpl.insn = INSN_READ;
+       tmpl.n = 1;
+       tmpl.subdev = setup->ad_subdev;
+
+       memset(&po_tmpl, 0, sizeof(po_tmpl));
+       po_tmpl.insn = INSN_WRITE;
+       po_tmpl.n = 1;
+       po_tmpl.subdev = setup->da_subdev;
+
+       for( channel = 0; channel < 2; channel++ )
+       {
+               /* ao zero offset */
+               o = setup->observables + ni_ao_zero_offset( channel );
+               assert( o->name == NULL );
+               asprintf( &o->name, "ao %i, zero offset, low gain", channel );
+               o->preobserve_insn = po_tmpl;
+               o->preobserve_insn.chanspec = CR_PACK(channel,ao_bipolar_lowgain,0);
+               o->preobserve_insn.data = o->preobserve_data;
+               o->observe_insn = tmpl;
+               o->observe_insn.chanspec =
+                       CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
+                       | CR_ALT_SOURCE | CR_ALT_FILTER;
+               o->reference_source = REF_DAC_GND( channel );
+               set_target( setup, ni_ao_zero_offset( channel ),0.0);
+
+               /* ao gain */
+               o = setup->observables + ni_ao_reference( channel );
+               assert( o->name == NULL );
+               asprintf( &o->name, "ao %i, reference voltage, low gain", channel );
+               o->preobserve_insn = po_tmpl;
+               o->preobserve_insn.chanspec = CR_PACK(channel,ao_bipolar_lowgain,0);
+               o->preobserve_insn.data = o->preobserve_data;
+               o->observe_insn = tmpl;
+               o->observe_insn.chanspec =
+                       CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
+                       | CR_ALT_SOURCE | CR_ALT_FILTER;
+               o->reference_source = REF_DAC_GND( channel );
+               set_target( setup, ni_ao_reference( channel ),8.0);
+
+               /* ao linearity, mid */
+               o = setup->observables + ni_ao_mid_linearity( channel );
+               assert( o->name == NULL );
+               asprintf( &o->name, "ao %i, linearity (mid), low gain", channel );
+               o->preobserve_insn = po_tmpl;
+               o->preobserve_insn.chanspec = CR_PACK(channel,ao_bipolar_lowgain,0);
+               o->preobserve_insn.data = o->preobserve_data;
+               o->observe_insn = tmpl;
+               o->observe_insn.chanspec =
+                       CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
+                       | CR_ALT_SOURCE | CR_ALT_FILTER;
+               o->reference_source = REF_DAC_GND( channel );
+               set_target( setup, ni_ao_mid_linearity( channel ),4.0);
+
+               if( ao_unipolar_lowgain >= 0 )
+               {
+                       /* ao unipolar zero offset */
+                       o = setup->observables + ni_ao_unip_zero_offset( channel );
+                       assert( o->name == NULL );
+                       asprintf( &o->name, "ao %i, unipolar zero offset, low gain", channel );
+                       o->preobserve_insn = po_tmpl;
+                       o->preobserve_insn.chanspec = CR_PACK(channel,ao_unipolar_lowgain,0);
+                       o->preobserve_insn.data = o->preobserve_data;
+                       o->observe_insn = tmpl;
+                       o->observe_insn.chanspec =
+                               CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
+                               | CR_ALT_SOURCE | CR_ALT_FILTER;
+                       o->reference_source = REF_DAC_GND( channel );
+                       set_target( setup, ni_ao_unip_zero_offset( channel ),0.0);
+
+                       /* ao unipolar gain */
+                       o = setup->observables + ni_ao_unip_reference( channel );
+                       assert( o->name == NULL );
+                       asprintf( &o->name, "ao %i, unipolar high, low gain", channel );
+                       o->preobserve_insn = po_tmpl;
+                       o->preobserve_insn.chanspec = CR_PACK(channel,ao_unipolar_lowgain,0);
+                       o->preobserve_insn.data = o->preobserve_data;
+                       o->observe_insn = tmpl;
+                       o->observe_insn.chanspec =
+                               CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
+                               | CR_ALT_SOURCE | CR_ALT_FILTER;
+                       o->reference_source = REF_DAC_GND( channel );
+                       set_target( setup, ni_ao_unip_reference( channel ), 9.0);
+
+                       /* ao unipolar linearity, mid */
+                       o = setup->observables + ni_ao_unip_mid_linearity( channel );
+                       assert( o->name == NULL );
+                       asprintf( &o->name, "ao %i, unipolar linearity (mid), low gain", channel );
+                       o->preobserve_insn = po_tmpl;
+                       o->preobserve_insn.chanspec = CR_PACK(channel,ao_unipolar_lowgain,0);
+                       o->preobserve_insn.data = o->preobserve_data;
+                       o->observe_insn = tmpl;
+                       o->observe_insn.chanspec =
+                               CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
+                               | CR_ALT_SOURCE | CR_ALT_FILTER;
+                       o->reference_source = REF_DAC_GND( channel );
+                       set_target( setup, ni_ao_unip_mid_linearity( channel ), 5.0);
+
+                       /* ao unipolar linearity, low */
+                       o = setup->observables + ni_ao_unip_low_linearity( channel );
+                       assert( o->name == NULL );
+                       asprintf( &o->name, "ao %i, unipolar linearity (low), low gain", channel );
+                       o->preobserve_insn = po_tmpl;
+                       o->preobserve_insn.chanspec = CR_PACK(channel,ao_unipolar_lowgain,0);
+                       o->preobserve_insn.data = o->preobserve_data;
+                       o->observe_insn = tmpl;
+                       o->observe_insn.chanspec =
+                               CR_PACK(REF_DAC_GND( channel ),ai_bipolar_lowgain,AREF_OTHER)
+                               | CR_ALT_SOURCE | CR_ALT_FILTER;
+                       o->reference_source = REF_DAC_GND( channel );
+                       set_target( setup, ni_ao_unip_low_linearity( channel ), 1.0);
+               }
+       }
+}
+
+static void ni_setup_observables( calibration_setup_t *setup )
 {
        comedi_insn tmpl;
-       comedi_insn po_tmpl2;
        int bipolar_lowgain;
        int bipolar_highgain;
        int unipolar_lowgain;
+       int unipolar_highgain;
        double voltage_reference;
        observable *o;
 
        bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->ad_subdev);
        bipolar_highgain = get_bipolar_highgain( setup->dev, setup->ad_subdev);
        unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->ad_subdev);
-
-       voltage_reference = 5.000;
+       unipolar_highgain = get_unipolar_highgain( setup->dev, setup->ad_subdev);
+
+       if( ni_board( setup )->ref_eeprom_lsb >= 0 &&
+               ni_board( setup )->ref_eeprom_msb >= 0 )
+       {
+               voltage_reference = ni_get_reference( setup,
+                       ni_board( setup )->ref_eeprom_lsb, ni_board( setup )->ref_eeprom_msb );
+       }else
+       {
+               DPRINT( 0, "WARNING: unknown eeprom address for reference voltage\n"
+                       "correction.  This might be fixable if you send us an eeprom dump\n"
+                       "(see the demo/eeprom_dump program).\n");
+               voltage_reference = 5.0;
+       }
 
        memset(&tmpl,0,sizeof(tmpl));
        tmpl.insn = INSN_READ;
        tmpl.n = 1;
        tmpl.subdev = setup->ad_subdev;
 
-       memset(&po_tmpl2,0,sizeof(tmpl));
-       po_tmpl2.insn = INSN_CONFIG;
-       po_tmpl2.n = 2;
-       po_tmpl2.subdev = setup->ad_subdev;
+       setup->n_observables = ni_num_observables;
 
        /* 0 offset, low gain */
        o = setup->observables + ni_zero_offset_low;
        o->name = "ai, bipolar zero offset, low gain";
-#if 0
-       o->preobserve_insn = po_tmpl2;
-       o->preobserve_insn.data = o->preobserve_data;
-       o->preobserve_insn.data[0] = INSN_CONFIG_ALT_SOURCE;
-       o->preobserve_insn.data[1] = CR_PACK(0,0,0);
-       o->observe_insn = tmpl;
-       o->observe_insn.chanspec = CR_ALT_SOURCE;
-       o->target = 0;
-#else
        o->observe_insn = tmpl;
-       o->observe_insn.chanspec = CR_PACK(0,bipolar_lowgain,AREF_OTHER);
+       o->observe_insn.chanspec = CR_PACK(REF_GND_GND,bipolar_lowgain,AREF_OTHER)
+               | CR_ALT_SOURCE | CR_ALT_FILTER;
+       o->reference_source = REF_GND_GND;
        o->target = 0;
-#endif
 
        /* 0 offset, high gain */
        o = setup->observables + ni_zero_offset_high;
        o->name = "ai, bipolar zero offset, high gain";
        o->observe_insn = tmpl;
-       o->observe_insn.chanspec = CR_PACK(0,bipolar_highgain,AREF_OTHER);
+       o->observe_insn.chanspec = CR_PACK(REF_GND_GND,bipolar_highgain,AREF_OTHER)
+               | CR_ALT_SOURCE | CR_ALT_FILTER;
+       o->reference_source = REF_GND_GND;
        o->target = 0;
 
        /* voltage reference */
        o = setup->observables + ni_reference_low;
        o->name = "ai, bipolar voltage reference, low gain";
        o->observe_insn = tmpl;
-       o->observe_insn.chanspec = CR_PACK(5,bipolar_lowgain,AREF_OTHER);
+       o->observe_insn.chanspec = CR_PACK(REF_CALSRC_GND,bipolar_lowgain,AREF_OTHER)
+               | CR_ALT_SOURCE | CR_ALT_FILTER;
+       o->reference_source = REF_CALSRC_GND;
        o->target = voltage_reference;
 
-       setup->n_observables = ni_reference_low + 1;
-
        if(unipolar_lowgain>=0){
-               /* unip/bip offset */
-               o = setup->observables + ni_unip_offset_low;
+               o = setup->observables + ni_unip_zero_offset_low;
                o->name = "ai, unipolar zero offset, low gain";
                o->observe_insn = tmpl;
                o->observe_insn.chanspec =
-                       CR_PACK(0,unipolar_lowgain,AREF_OTHER);
-               o->target = 0;
+                       CR_PACK(REF_GND_GND,unipolar_lowgain,AREF_OTHER)
+                       | CR_ALT_SOURCE | CR_ALT_FILTER;
+               o->reference_source = REF_GND_GND;
+               o->target = very_low_target( setup->dev, setup->ad_subdev, 0, unipolar_lowgain );
 
-#if 0
-               /* unip gain */
-               o = observables + ni_unip_reference_low;
+               o = setup->observables + ni_unip_reference_low;
                o->name = "ai, unipolar voltage reference, low gain";
                o->observe_insn = tmpl;
                o->observe_insn.chanspec =
-                       CR_PACK(5,unipolar_lowgain,AREF_OTHER);
+                       CR_PACK(REF_CALSRC_GND,unipolar_lowgain,AREF_OTHER)
+                       | CR_ALT_SOURCE | CR_ALT_FILTER;
+               o->reference_source = REF_CALSRC_GND;
                o->target = voltage_reference;
-               i++;
-#endif
-               setup->n_observables = ni_unip_offset_low + 1;
        }
 
-       if(setup->da_subdev>=0){
-               comedi_insn po_tmpl;
-
-               memset(&po_tmpl,0,sizeof(po_tmpl));
-               po_tmpl.insn = INSN_WRITE;
-               po_tmpl.n = 1;
-               po_tmpl.subdev = setup->da_subdev;
-
-               /* ao 0, zero offset */
-               o = setup->observables + ni_ao0_zero_offset;
-               o->name = "ao 0, zero offset, low gain";
-               o->preobserve_insn = po_tmpl;
-               o->preobserve_insn.chanspec = CR_PACK(0,0,0);
-               o->preobserve_insn.data = o->preobserve_data;
+       if(unipolar_highgain >= 0)
+       {
+               o = setup->observables + ni_unip_zero_offset_high;
+               o->name = "ai, unipolar zero offset, high gain";
                o->observe_insn = tmpl;
                o->observe_insn.chanspec =
-                       CR_PACK(2,bipolar_lowgain,AREF_OTHER);
-               set_target( setup, ni_ao0_zero_offset,0.0);
+                       CR_PACK(REF_GND_GND,unipolar_highgain,AREF_OTHER)
+                       | CR_ALT_SOURCE | CR_ALT_FILTER;
+               o->reference_source = REF_GND_GND;
+               o->target = very_low_target( setup->dev, setup->ad_subdev, 0, unipolar_highgain );
+       }
 
-               /* ao 0, gain */
-               o = setup->observables + ni_ao0_reference;
-               o->name = "ao 0, reference voltage, low gain";
-               o->preobserve_insn = po_tmpl;
-               o->preobserve_insn.chanspec = CR_PACK(0,0,0);
-               o->preobserve_insn.data = o->preobserve_data;
-               o->observe_insn = tmpl;
-               o->observe_insn.chanspec = 
-                       CR_PACK(6,bipolar_lowgain,AREF_OTHER);
-               set_target( setup, ni_ao0_reference,5.0);
-               o->target -= voltage_reference;
-
-               /* ao 1, zero offset */
-               o = setup->observables + ni_ao1_zero_offset;
-               o->name = "ao 1, zero offset, low gain";
+       if(setup->da_subdev >= 0)
+               ni_setup_ao_observables( setup );
+}
+
+/* for +-50V and +-20V ranges, the reference source goes 0V
+ * to 50V instead of 0V to 5V */
+static unsigned int cal_gain_register_bits_611x( double reference, double *voltage )
+{
+       int bits;
+
+       bits = 200.0 * ( *voltage / reference );
+       if( bits > 200 ) bits = 200;
+       if( bits < 0 ) bits = 0;
+
+       *voltage = reference * ( bits / 200.0 );
+       return bits;
+}
+
+static unsigned int ref_source_611x( unsigned int ref_source, unsigned int cal_gain_bits )
+{
+       return ( ref_source & 0xf ) | ( ( cal_gain_bits << 4 ) & 0xff0 );
+}
+
+static void reference_target_611x( calibration_setup_t *setup,
+       observable *o, double master_reference, unsigned int range )
+{
+       int cal_gain_reg_bits;
+       double reference;
+       double target;
+       comedi_range *range_ptr;
+
+       range_ptr = comedi_get_range( setup->dev, setup->ad_subdev, 0, range );
+       assert( range_ptr != NULL );
+       if( range_ptr->max > 19.0 ) reference = 10 * master_reference;
+       else reference = master_reference;
+       target = range_ptr->max * 0.8;
+
+       cal_gain_reg_bits = cal_gain_register_bits_611x( reference, &target );
+
+       o->reference_source = ref_source_611x( REF_CALSRC_GND, cal_gain_reg_bits );
+       o->target = target;
+}
+
+static void ni_setup_observables_611x( calibration_setup_t *setup )
+{
+       comedi_insn tmpl;
+       comedi_insn po_tmpl;
+       int range, channel;
+       double master_reference;
+       observable *o;
+       int num_ai_channels, num_ai_ranges;
+       static const int num_ao_channels = 2;
+
+       setup->sv_settling_time_ns = 10000000;
+       setup->sv_order = 14;
+
+       master_reference = ni_get_reference( setup,
+               ni_board( setup )->ref_eeprom_lsb, ni_board( setup )->ref_eeprom_msb );
+
+       memset(&tmpl,0,sizeof(tmpl));
+       tmpl.insn = INSN_READ;
+       tmpl.n = 1;
+       tmpl.subdev = setup->ad_subdev;
+
+       num_ai_channels = comedi_get_n_channels( setup->dev, setup->ad_subdev );
+       assert( num_ai_channels >= 0 );
+       num_ai_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 );
+       assert( num_ai_ranges >= 0 );
+
+       for( channel = 0; channel < num_ai_channels; channel++ )
+       {
+               for( range = 0; range < num_ai_ranges; range++ )
+               {
+                       /* 0 offset */
+                       o = setup->observables + ni_zero_offset_611x( setup, channel, range );
+                       assert( o->name == NULL );
+                       asprintf( &o->name, "ai, ch %i, range %i, zero offset",
+                               channel, range );
+                       o->observe_insn = tmpl;
+                       o->observe_insn.chanspec = CR_PACK( channel, range, AREF_DIFF )
+                               | CR_ALT_SOURCE | CR_ALT_FILTER;
+                       o->reference_source = REF_GND_GND;
+                       o->target = 0.0;
+
+                       /* voltage reference */
+                       o = setup->observables + ni_reference_611x( setup, channel, range );
+                       assert( o->name == NULL );
+                       asprintf( &o->name, "ai, ch %i, range %i, voltage reference",
+                               channel, range );
+                       o->observe_insn = tmpl;
+                       o->observe_insn.chanspec = CR_PACK( channel, range, AREF_DIFF )
+                               | CR_ALT_SOURCE | CR_ALT_FILTER;
+                       reference_target_611x( setup, o, master_reference, range );
+               }
+       }
+
+       memset(&po_tmpl,0,sizeof(po_tmpl));
+       po_tmpl.insn = INSN_WRITE;
+       po_tmpl.n = 1;
+       po_tmpl.subdev = setup->da_subdev;
+
+       for( channel = 0; channel < num_ao_channels; channel ++ )
+       {
+               static const int ai_range_for_ao = 2;
+
+               /* ao zero offset */
+               o = setup->observables + ni_ao_zero_offset_611x( setup, channel, 0 );
+               assert( o->name == NULL );
+               asprintf( &o->name, "ao ch %i, zero offset", channel );
                o->preobserve_insn = po_tmpl;
-               o->preobserve_insn.chanspec = CR_PACK(1,0,0);
+               o->preobserve_insn.chanspec = CR_PACK( channel, 0, AREF_GROUND );
                o->preobserve_insn.data = o->preobserve_data;
                o->observe_insn = tmpl;
-               o->observe_insn.chanspec =
-                       CR_PACK(3,bipolar_lowgain,AREF_OTHER);
-               set_target( setup, ni_ao1_zero_offset,0.0);
-
-               /* ao 1, gain */
-               o = setup->observables + ni_ao1_reference;
-               o->name = "ao 1, reference voltage, low gain";
+               o->observe_insn.chanspec = CR_PACK( 0, ai_range_for_ao, AREF_DIFF )
+                       | CR_ALT_SOURCE | CR_ALT_FILTER;
+               o->reference_source = REF_DAC_GND( channel );
+               set_target( setup, ni_ao_zero_offset_611x( setup, channel, 0 ), 0.0 );
+
+               /* ao gain */
+               o = setup->observables + ni_ao_reference_611x( setup, channel, 0 );
+               assert( o->name == NULL );
+               asprintf( &o->name, "ao ch %i, reference voltage", channel );
                o->preobserve_insn = po_tmpl;
-               o->preobserve_insn.chanspec = CR_PACK(1,0,0);
+               o->preobserve_insn.chanspec = CR_PACK( channel, 0, AREF_GROUND );
                o->preobserve_insn.data = o->preobserve_data;
                o->observe_insn = tmpl;
-               o->observe_insn.chanspec =
-                       CR_PACK(7,bipolar_lowgain,AREF_OTHER);
-               set_target( setup, ni_ao1_reference,5.0);
-               o->target -= voltage_reference;
-
-               setup->n_observables = ni_ao1_reference + 1;
+               o->observe_insn.chanspec = CR_PACK( 0, ai_range_for_ao, AREF_DIFF )
+                       | CR_ALT_SOURCE | CR_ALT_FILTER;
+               o->reference_source = REF_DAC_GND( channel );
+               set_target( setup, ni_ao_reference_611x( setup, channel, 0 ), 5.0 );
        }
+
+       setup->n_observables = num_ao_observables_611x + 2 * num_ai_ranges * num_ai_channels;
 }
 
-int cal_ni_at_mio_16e_2(calibration_setup_t *setup)
+static int cal_ni_daqcard_ai_16xe_50(calibration_setup_t *setup)
 {
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,1);
-       cal1( setup, ni_zero_offset_high,0);
-       cal1( setup, ni_reference_low,3);
-       cal1( setup, ni_unip_offset_low,2);
-       if(do_output){
-               cal1( setup, ni_ao0_zero_offset,5);
-               cal1( setup, ni_ao0_reference,6);
-               cal1( setup, ni_ao1_zero_offset,8);
-               cal1( setup, ni_ao1_reference,9);
-       }
-       return 0;
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 8;
+       layout.adc_postgain_offset = 2;
+       layout.adc_gain = 0;
+       layout.adc_gain_fine = 1;
+
+       return cal_ni_generic( setup, &layout );
 }
 
-/*
- * Device name: DAQCard-ai-16xe-50
- * Comedi version: 0.7.60
- * ai, bipolar zero offset, low gain
- * offset 5.87(63)e-3, target 0
- * caldac[0] gain=-2.243(21)e-6 V/bit S_min=208.079 dof=254
- * caldac[2] gain=1.56378(22)e-4 V/bit S_min=1782.91 dof=254
- * caldac[8] gain=2.499(14)e-7 V/bit S_min=234.915 dof=254
- * ai, bipolar zero offset, high gain
- * offset 4.251(49)e-5, target 0
- * caldac[0] gain=-2.396(30)e-8 V/bit S_min=231.387 dof=254
- * caldac[2] gain=1.56428(28)e-6 V/bit S_min=829.096 dof=254
- * caldac[8] gain=2.61244(18)e-7 V/bit S_min=773.092 dof=254
- * ai, bipolar voltage reference, low gain
- * offset 4.99650(81), target 5
- * caldac[0] gain=-3.78250(23)e-4 V/bit S_min=12207.6 dof=254
- * caldac[1] gain=-9.878(22)e-6 V/bit S_min=346.795 dof=254
- * caldac[2] gain=1.57172(23)e-4 V/bit S_min=969.526 dof=254
- * caldac[8] gain=2.795(14)e-7 V/bit S_min=245.703 dof=254
- * ai, unipolar zero offset, low gain
- * offset 0.0133(14), target 0
- * caldac[0] gain=3.73923(29)e-4 V/bit S_min=2855.79 dof=151
- * caldac[1] gain=9.784(11)e-6 V/bit S_min=727.295 dof=254
- * caldac[2] gain=7.8670(11)e-5 V/bit S_min=903.291 dof=254
- * caldac[8] gain=2.7732(74)e-7 V/bit S_min=415.399 dof=254
- */
-int cal_ni_daqcard_ai_16xe_50(calibration_setup_t *setup)
+static int cal_ni_at_mio_16xe_50(calibration_setup_t *setup)
 {
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,2);
-       cal1( setup, ni_zero_offset_high,8);
-       cal1( setup, ni_reference_low,0);
-       cal1_fine( setup, ni_reference_low,0);
-       cal1( setup, ni_reference_low,1);
-       return 0;
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 8;
+       layout.adc_postgain_offset = 2;
+       layout.adc_gain = 0;
+       layout.adc_gain_fine = 1;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 4;
+       layout.dac_offset[ 1 ] = 7;
+       layout.dac_gain[ 1 ] = 5;
+
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_at_mio_16xe_50(calibration_setup_t *setup)
+static int cal_ni_pci_mio_16xe_10(calibration_setup_t *setup)
 {
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,2);
-       cal1( setup, ni_zero_offset_high,8);
-       cal1( setup, ni_reference_low,0);
-       cal1_fine( setup, ni_reference_low,0);
-       cal1( setup, ni_reference_low,1);
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 8;
+       layout.adc_postgain_offset = 2;
+       layout.adc_postgain_offset_fine = 3;
+       layout.adc_gain = 0;
+       layout.adc_gain_fine = 1;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 4;
+       layout.dac_offset[ 1 ] = 7;
+       layout.dac_gain[ 1 ] = 5;
+
+       return cal_ni_generic( setup, &layout );
+}
 
-       if(do_output){
-               cal1( setup, ni_ao0_zero_offset,6);
-               cal1( setup, ni_ao0_reference,4);
-               cal1( setup, ni_ao1_zero_offset,7);
-               cal1( setup, ni_ao1_reference,5);
-       }
-       return 0;
+static int cal_ni_at_mio_16e_1(calibration_setup_t *setup)
+{
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_postgain_offset = 1;
+       layout.adc_gain = 3;
+       layout.adc_unip_offset = 2;
+       layout.dac_offset[0] = 5;
+       layout.dac_gain[0] = 6;
+       layout.dac_linearity[0] = 4;
+       layout.dac_offset[1] = 8;
+       layout.dac_gain[1] = 9;
+       layout.dac_linearity[1] = 7;
+
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_pci_mio_16xe_10(calibration_setup_t *setup)
+static int cal_ni_at_mio_16e_2(calibration_setup_t *setup)
 {
-       postgain_cal( setup, ni_zero_offset_low, ni_zero_offset_high, 2);
-       postgain_cal( setup, ni_zero_offset_low, ni_zero_offset_high, 3);
-       cal1( setup, ni_zero_offset_high, 8);
-       cal1( setup, ni_reference_low, 0);
-       cal1( setup, ni_reference_low, 1);
+       return cal_ni_at_mio_16e_1(setup);
+}
 
-       if(do_output){
-               cal1( setup, ni_ao0_zero_offset,6);
-               cal1( setup, ni_ao0_reference,4);
-               cal1( setup, ni_ao1_zero_offset,7);
-               cal1( setup, ni_ao1_reference,5);
-       }
-       return 0;
+static int cal_ni_pci_mio_16e_1(calibration_setup_t *setup)
+{
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_postgain_offset = 1;
+       layout.adc_unip_offset = 2;
+       layout.adc_gain = 3;
+       layout.dac_offset[ 0 ] = 5;
+       layout.dac_gain[ 0 ] = 6;
+       layout.dac_linearity[ 0 ] = 4;
+       layout.dac_offset[ 1 ] = 8;
+       layout.dac_gain[ 1 ] = 9;
+       layout.dac_linearity[ 1 ] = 7;
+
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_at_mio_16e_1(calibration_setup_t *setup)
+static int cal_ni_pci_6014(calibration_setup_t *setup)
 {
-       return cal_ni_at_mio_16e_2( setup );
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_postgain_offset = 4;
+       layout.adc_pregain_offset_fine = 8;
+       layout.adc_gain = 2;
+       layout.dac_offset[0] = 6;
+       layout.dac_offset_fine[0] = 10;
+       layout.dac_gain[0] = 7;
+       layout.dac_gain_fine[0] = 11;
+       layout.dac_offset[1] = 9;
+       layout.dac_offset_fine[1] = 1;
+       layout.dac_gain[1] = 3;
+       layout.dac_gain_fine[1] = 5;
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_pci_mio_16e_1(calibration_setup_t *setup)
+static int cal_ni_pci_6032e(calibration_setup_t *setup)
 {
-       //cal_ni_at_mio_16e_2();
+       ni_caldac_layout_t layout;
 
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,1);
-       cal1( setup, ni_zero_offset_high,0);
-       cal1( setup, ni_reference_low,3);
-       cal1( setup, ni_unip_offset_low,2);
-       if(do_output){
-               cal1( setup, ni_ao0_zero_offset,5);
-               //cal1( setup, ni_ao0_zero_offset,4); /* linearity? */
-               cal1( setup, ni_ao0_reference,6);
-               cal1( setup, ni_ao1_zero_offset,8);
-               //cal1( setup, ni_ao1_zero_offset,7); /* linearity? */
-               cal1( setup, ni_ao1_reference,9);
-       }
-       return 0;
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 8;
+       layout.adc_postgain_offset = 2;
+       layout.adc_postgain_offset_fine = 3;
+       layout.adc_gain = 0;
+       layout.adc_gain_fine = 1;
+
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_pci_6035e(calibration_setup_t *setup)
+static int cal_ni_pci_6035e(calibration_setup_t *setup)
 {
        /* this is for the ad8804_debug caldac */
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_pregain_offset_fine = 8;
+       layout.adc_postgain_offset = 4;
+       layout.adc_gain = 2;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 11;
+       layout.dac_linearity[ 0 ] = 10;
+       layout.dac_offset[ 1 ] = 9;
+       layout.dac_gain[ 1 ] = 5;
+       layout.dac_linearity[ 1 ] = 1;
+
+       return cal_ni_generic( setup, &layout );
+}
 
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,4);
-
-       cal1( setup, ni_zero_offset_high,0);
-       cal1( setup, ni_zero_offset_high,8);
-
-       cal1( setup, ni_reference_low,2);
+static int cal_ni_pci_6036e(calibration_setup_t *setup)
+{
+       ni_caldac_layout_t layout;
 
-       if(do_output){
-               cal1( setup, ni_ao0_zero_offset,6);
-               //cal1( setup, ni_ao0_zero_offset,10); /* linearity? */
-               cal1( setup, ni_ao0_reference,11);
-               cal1( setup, ni_ao1_zero_offset,9);
-               //cal1( setup, ni_ao1_zero_offset,1); /* linearity? */
-               cal1( setup, ni_ao1_reference,5);
+       if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 66 ) )
+       {
+               DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
+                "for this calibration to work properly\n" );
        }
-       return 0;
+
+       /* this is for the ad8804_debug caldac */
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_postgain_offset = 4;
+       layout.adc_pregain_offset_fine = 8;
+       layout.adc_gain = 2;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 7;
+       layout.dac_gain_fine[ 0 ] = 11;
+       layout.dac_linearity[ 0 ] = 10;
+       layout.dac_offset[ 1 ] = 9;
+       layout.dac_gain[ 1 ] = 3;
+       layout.dac_gain_fine[ 1 ] = 5;
+       layout.dac_linearity[ 1 ] = 1;
+
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_pci_6071e(calibration_setup_t *setup)
+static int cal_ni_pci_6071e(calibration_setup_t *setup)
 {
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,1);
-       cal1( setup, ni_zero_offset_high,0);
-       cal1( setup, ni_reference_low,3);
-       cal1_fine( setup, ni_reference_low,3);
-       if(do_output){
-               cal1( setup, ni_ao0_zero_offset,5);
-               //cal1( setup, ni_ao0_zero_offset,4); /* linearity? */
-               cal1( setup, ni_ao0_reference,6); /* guess.  Doesn't show up correctly in dump */
-               cal1( setup, ni_ao1_zero_offset,8);
-               //cal1( setup, ni_ao1_zero_offset,7); /* linearity? */
-               cal1( setup, ni_ao1_reference,9);
+       ni_caldac_layout_t layout;
+
+       if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 66 ) )
+       {
+               DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
+                "for this calibration to work properly\n" );
        }
-       return 0;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 8;
+       layout.adc_postgain_offset = 4;
+       layout.adc_unip_offset = 7;
+       layout.adc_gain = 2;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 11;
+       layout.dac_linearity[ 0 ] = 10;
+       layout.dac_offset[ 1 ] = 9;
+       layout.dac_gain[ 1 ] = 5;
+       layout.dac_linearity[ 1 ] = 1;
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_pxi_6071e(calibration_setup_t *setup)
+static int cal_ni_pxi_6071e(calibration_setup_t *setup)
 {
-       // 6071e (old)
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,1);
-       cal1( setup, ni_zero_offset_high,0);
-       cal1( setup, ni_reference_low,3);
-       if(do_output){
-               // unknown
+       ni_caldac_layout_t layout;
+
+       if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 66 ) )
+       {
+               DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
+                "for this calibration to work properly\n" );
        }
-       return 0;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_pregain_offset_fine = 8;
+       layout.adc_postgain_offset = 4;
+       layout.adc_gain = 2;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 11;
+       layout.dac_linearity[ 0 ] = 10;
+       layout.dac_offset[ 1 ] = 9;
+       layout.dac_gain[ 1 ] = 5;
+       layout.dac_linearity[ 1 ] = 1;
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_at_mio_16e_10(calibration_setup_t *setup)
+static int cal_ni_at_mio_16e_10(calibration_setup_t *setup)
 {
-       // 16e-10 (old)
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,1);
-       cal1( setup, ni_zero_offset_high,10);
-       cal1( setup, ni_zero_offset_high,0);
-       cal1( setup, ni_reference_low,3);
-       cal1( setup, ni_unip_offset_low,2);
-       if(do_output){
-               cal1( setup, ni_ao0_zero_offset,5); // guess
-               cal1( setup, ni_ao0_reference,6); // guess
-               cal1( setup, ni_ao1_zero_offset,8); // guess
-               cal1( setup, ni_ao1_reference,9); // guess
+       ni_caldac_layout_t layout;
+
+       if(comedi_get_version_code(setup->dev) <= COMEDI_VERSION_CODE(0, 7, 68))
+       {
+               DPRINT(0, "WARNING: you need comedi driver version 0.7.69 or later\n"
+                "for this calibration to work properly\n" );
        }
-       return 0;
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_pregain_offset_fine = 8;
+       layout.adc_postgain_offset = 4;
+       layout.adc_gain = 2;
+       layout.adc_unip_offset = 7;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 11; 
+       layout.dac_linearity[ 0 ] = 10;
+       layout.dac_offset[ 1 ] = 9;
+       layout.dac_gain[ 1 ] = 5;
+       layout.dac_linearity[1] = 1;
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_pci_mio_16xe_50(calibration_setup_t *setup)
+static int cal_ni_pci_mio_16xe_50(calibration_setup_t *setup)
 {
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,2);
-       cal1( setup, ni_zero_offset_high,8);
-       cal1( setup, ni_reference_low,0);
-       cal1_fine( setup, ni_reference_low,0);
-       cal1( setup, ni_reference_low,1);
-
-       if(do_output){
-               cal1( setup, ni_ao0_zero_offset,6);
-               cal1( setup, ni_ao0_reference,4);
-               cal1( setup, ni_ao1_zero_offset,7);
-               cal1( setup, ni_ao1_reference,5);
-       }
-       return 0;
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 8;
+       layout.adc_postgain_offset = 2;
+       layout.adc_gain = 0;
+       layout.adc_gain_fine = 1;
+       layout.adc_unip_offset = 7;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 4;
+       layout.dac_offset[ 1 ] = 7;
+       layout.dac_gain[ 1 ] = 5;
+
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_pci_6023e(calibration_setup_t *setup)
+static int cal_ni_pci_6023e(calibration_setup_t *setup)
 {
-       /* There seems to be a bug in the driver that doesn't allow
-        * access to caldac 10, and possibly others. */
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,1);
-       //cal1( setup, ni_zero_offset_high,10);
-       //cal1( setup, ni_zero_offset_high,0);
-       cal1( setup, ni_reference_low,3);
-       return 0;
+       /* for comedi-0.7.65 */
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 8; /* possibly wrong */
+       layout.adc_pregain_offset_fine = 0;
+       layout.adc_postgain_offset = 4;
+       layout.adc_gain = 2;
+
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_pci_6024e(calibration_setup_t *setup)
+static int cal_ni_pci_6024e(calibration_setup_t *setup)
 {
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,4);
-       cal1( setup, ni_zero_offset_high,0);
-       cal1( setup, ni_zero_offset_high,8);
-       cal1( setup, ni_reference_low,2);
-       if(do_output){
-               cal1( setup, ni_ao0_zero_offset,6);
-               //cal1( setup, ni_ao0_zero_offset,10); // nonlinearity?
-               cal1( setup, ni_ao0_reference,11);
-               cal1( setup, ni_ao1_zero_offset,9);
-               //cal1( setup, ni_ao1_zero_offset,1); // nonlinearity?
-               cal1( setup, ni_ao1_reference,5);
-       }
-       return 0;
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_postgain_offset = 4;
+       layout.adc_pregain_offset_fine = 8;
+       layout.adc_gain = 2;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 11;
+       layout.dac_linearity[ 0 ] = 10;
+       layout.dac_offset[ 1 ] = 9;
+       layout.dac_gain[ 1 ] = 5;
+       layout.dac_linearity[ 1 ] = 1;
+
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_pci_6025e(calibration_setup_t *setup)
+static int cal_ni_pci_6025e(calibration_setup_t *setup)
 {
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,4); // was 1
-       //cal1( setup, ni_zero_offset_high,XXX); // was 10
-       cal1( setup, ni_zero_offset_high,8); // was 0
-       cal1( setup, ni_reference_low,2); // was 3
-       if(do_output){
-               cal1( setup, ni_ao0_zero_offset,6); // was 5
-               //cal1( setup, ni_ao0_zero_offset,10); // nonlinearity was 4
-               //cal1( setup, ni_ao0_reference,XXX); // was 6
-               cal1( setup, ni_ao1_zero_offset,9); // was 8
-               //cal1( setup, ni_ao1_zero_offset,1); // nonlinearity was 7
-               cal1( setup, ni_ao1_reference,5); // was 9
-       }
-       return 0;
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_postgain_offset = 4;
+       layout.adc_pregain_offset_fine = 8;
+       layout.adc_gain = 2;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 11;
+       layout.dac_linearity[ 0 ] = 10;
+       layout.dac_offset[ 1 ] = 9;
+       layout.dac_gain[ 1 ] = 5;
+       layout.dac_linearity[ 1 ] = 1;
+
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_pci_6052e(calibration_setup_t *setup)
+static int cal_ni_pci_6052e(calibration_setup_t *setup)
 {
        /*
         * This board has noisy caldacs
         *
-        * The NI documentation says:
-        *   0, 8   AI pregain  (coarse, fine)          3, 11
-        *   4, 12  AI postgain                         15,7
-        *   2, 10  AI reference                        1, 9
-        *   14, 7  AI unipolar offset                  5, 13
+        * The NI documentation says (true mb88341 addressing):
+        *   0, 8   AI pregain  (coarse, fine)
+        *   4, 12  AI postgain
+        *   2, 10  AI reference
+        *   14, 7  AI unipolar offset
         *
         *   0      AO0 linearity
-        *   8, 4   AO0 reference               23, 19  7, 3
-        *   12     AO0 offset                  27      11
-        *   2      AO1 linearity                       
-        *   10, 6  AO1 reference               25, 21  9, 5
-        *   14     AO1 offset                  29, 17  13, 1
-        *
-        *   0  3       x       0011
-        *
-        *   2  1       x       0001
+        *   8, 4   AO0 reference
+        *   12     AO0 offset
+        *   2      AO1 linearity
+        *   10, 6  AO1 reference
+        *   14     AO1 offset
         *
-        *   4  7       15 3    0111 0011
+        *  For us, these map to (ad8804 channels)
         *
-        *   6          17 5         0101
-        *   7  x       
-        *   8  11      19 7    1011 0111
+        *   0, 1   AI pregain  (coarse, fine)
+        *   2, 3  AI postgain
+        *   4, 5  AI reference
+        *   7  AI unipolar offset
         *
-        *   10 9       21 9    1001 1001
+        *   0      AO0 linearity
+        *   1, 2   AO0 reference
+        *   3      AO0 offset
+        *   4      AO1 linearity
+        *   5, 6   AO1 reference
+        *   7      AO1 offset
         *
-        *   12 x       23 11        1011
+        *  or, with mb88341 channels
         *
-        *   14 5       13 1    0101 0001
+        *   xxx    AO0 linearity
+        *   7, 3   AO0 reference
+        *   11     AO0 offset
+        *   1      AO1 linearity
+        *   9, 5   AO1 reference
+        *   xxx    AO1 offset
         *
         */
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_postgain_offset = 2;
+       layout.adc_gain = 4;
+       layout.adc_unip_offset = 6;
+       layout.adc_unip_offset_fine = 7;
+       layout.adc_pregain_offset_fine = 1;
+       layout.adc_postgain_offset_fine = 3;
+       layout.adc_gain_fine = 5;
+
+       DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
+        "for this calibration to work properly\n" );
+/* this works when the first two caldacs are ad8804_debug */
+       layout.dac_offset[ 0 ] = 16 + 3;
+       layout.dac_gain[ 0 ] = 16 + 1;
+       layout.dac_gain_fine[ 0 ] = 16 + 2;
+       layout.dac_linearity[ 0 ] = 16 + 0;
+       layout.dac_offset[ 1 ] = 16 + 7;
+       layout.dac_gain[ 1 ] = 16 + 5;
+       layout.dac_gain_fine[ 1 ] = 16 + 6;
+       layout.dac_linearity[ 1 ] = 16 + 4;
+
+       return cal_ni_generic( setup, &layout );
+}
+
+static int cal_ni_daqcard_ai_16e_4(calibration_setup_t *setup)
+{
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 0;
+       layout.adc_postgain_offset = 1;
+       layout.adc_gain = 3;
+       layout.adc_unip_offset = 2;
+
+       return cal_ni_generic( setup, &layout );
+}
+
+static int adc_offset_611x( unsigned int channel )
+{
+       return 2 * channel + 2;
+}
+static int adc_gain_611x( unsigned int channel )
+{
+       return 2 * channel + 1;
+}
+static int dac_offset_611x( unsigned int channel )
+{
+       return 12 + 2 + 2 * channel;
+}
+static int dac_gain_611x( unsigned int channel )
+{
+       return 12 + 1 + 2 * channel;
+}
+static int cal_ni_pci_611x( calibration_setup_t *setup )
+{
+       generic_layout_t layout;
+
+       init_generic_layout( &layout );
+       layout.adc_offset = adc_offset_611x;
+       layout.adc_gain = adc_gain_611x;
+       layout.dac_offset = dac_offset_611x;
+       layout.dac_gain = dac_gain_611x;
+       layout.adc_high_observable = ni_reference_611x;
+       layout.adc_ground_observable = ni_zero_offset_611x;
+       layout.dac_high_observable = ni_ao_reference_611x;
+       layout.dac_ground_observable = ni_ao_zero_offset_611x;
+
+       return generic_cal_by_channel_and_range( setup, &layout );
+}
+
+static int cal_ni_pci_mio_16e_4( calibration_setup_t *setup )
+{
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 8;
+       layout.adc_postgain_offset = 4;
+       layout.adc_gain = 2;
+       layout.adc_unip_offset = 7;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 11;
+       layout.dac_linearity[ 0 ] = 10;
+       layout.dac_offset[ 1 ] = 9;
+       layout.dac_gain[ 1 ] = 5;
+       layout.dac_linearity[ 1 ] = 1;
+
+       return cal_ni_generic( setup, &layout );
+}
 
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,2);
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,3);
-       cal1( setup, ni_zero_offset_high,0);
-       cal1( setup, ni_zero_offset_high,1);
-       cal1( setup, ni_reference_low,4);
-       cal1_fine( setup, ni_reference_low,4);
-       cal1( setup, ni_reference_low,5);
-       cal1( setup, ni_unip_offset_low,6);
-       cal1_fine( setup, ni_unip_offset_low,6);
-       //cal1( setup, ni_unip_offset_low,7);
-       if(do_output){
-               cal1( setup, ni_ao0_zero_offset,12+11);
-               cal1( setup, ni_ao0_reference,12+7);
-               cal1( setup, ni_ao0_reference,12+3);
-               cal1( setup, ni_ao1_zero_offset,12+1);
-               cal1( setup, ni_ao1_reference,12+9);
-               cal1( setup, ni_ao1_reference,12+5);
+static int cal_ni_daqcard_6062e( calibration_setup_t *setup )
+{
+       ni_caldac_layout_t layout;
+
+       if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 66 ) )
+       {
+               DPRINT(0, "WARNING: you need comedi driver version 0.7.67 or later\n"
+                "for this calibration to work properly\n" );
        }
-       return 0;
+       init_ni_caldac_layout( &layout );
+       layout.adc_pregain_offset = 8;
+       layout.adc_postgain_offset = 4;
+       layout.adc_gain = 2;
+       layout.adc_unip_offset = 7;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 11;
+       layout.dac_linearity[ 0 ] = 10;
+       layout.dac_offset[ 1 ] = 9;
+       layout.dac_gain[ 1 ] = 5;
+       layout.dac_linearity[ 1 ] = 1;
+
+       return cal_ni_generic( setup, &layout );
 }
 
-int cal_ni_pci_mio_16e_4(calibration_setup_t *setup)
+static int cal_ni_daqcard_6024e( calibration_setup_t *setup )
 {
-       /* this is for the ad8804_debug caldac */
+       ni_caldac_layout_t layout;
+
+       init_ni_caldac_layout( &layout );
+
+       layout.adc_pregain_offset = 0;
+       layout.adc_postgain_offset = 4;
+       layout.adc_gain = 2;
+       //layout.adc_unip_offset = 7;
+       layout.dac_offset[ 0 ] = 6;
+       layout.dac_gain[ 0 ] = 3;
+       //layout.dac_linearity[ 0 ] = 10;
+       layout.dac_offset[ 1 ] = 1;
+       layout.dac_gain[ 1 ] = 5;
+       //layout.dac_linearity[ 1 ] = 1;
+
+       return cal_ni_generic( setup, &layout );
+}
+
+static int cal_ni_daqcard_6036e( calibration_setup_t *setup )
+{
+       ni_caldac_layout_t layout;
+
+       if( comedi_get_version_code( setup->dev ) <= COMEDI_VERSION_CODE( 0, 7, 68 ) )
+       {
+               DPRINT(0, "WARNING: you need comedi driver version 0.7.69 or later\n"
+                "for this calibration to work properly\n" );
+       }
 
-       postgain_cal( setup, ni_zero_offset_low,ni_zero_offset_high,4);
-       cal1( setup, ni_zero_offset_high,8);
-       cal1( setup, ni_reference_low,2);
+       init_ni_caldac_layout( &layout );
 
-       cal1( setup, ni_unip_offset_low,7);
+       layout.adc_pregain_offset = 0;
+       layout.adc_pregain_offset_fine = 8;
+       layout.adc_postgain_offset = 4;
+       layout.adc_gain = 2;
+
+       layout.dac_offset[0] = 6;
+       layout.dac_gain[0] = 7;
+       layout.dac_gain_fine[ 0 ] = 11;
+       layout.dac_linearity[0] = 10;
+
+       layout.dac_offset[ 1 ] = 9;
+       layout.dac_gain[ 1 ] = 3;
+       layout.dac_gain_fine[ 1 ] = 5;
+       layout.dac_linearity[1] = 1;
+
+       return cal_ni_generic( setup, &layout );
+}
 
-       if(do_output){
-               cal1( setup, ni_ao0_zero_offset,6);
-               //cal1( setup, ni_ao0_nonlinearity,10);
-               cal1( setup, ni_ao0_reference,11);
-               cal1( setup, ni_ao1_zero_offset,9);
-               //cal1( setup, ni_ao1_nonlinearity,1);
-               cal1( setup, ni_ao1_reference,5);
+static void prep_adc_caldacs_generic( calibration_setup_t *setup,
+       const ni_caldac_layout_t *layout, unsigned int range )
+{
+       int retval;
+
+       if( setup->old_calibration == NULL )
+       {
+               reset_caldac( setup, layout->adc_pregain_offset );
+               reset_caldac( setup, layout->adc_postgain_offset );
+               reset_caldac( setup, layout->adc_gain );
+               reset_caldac( setup, layout->adc_pregain_offset_fine );
+               reset_caldac( setup, layout->adc_postgain_offset_fine );
+               reset_caldac( setup, layout->adc_gain_fine );
+               reset_caldac( setup, layout->adc_unip_offset );
+               reset_caldac( setup, layout->adc_unip_offset_fine );
+       }else
+       {
+               retval = comedi_apply_parsed_calibration( setup->dev, setup->ad_subdev,
+                       0, range, AREF_GROUND, setup->old_calibration );
+               if( retval < 0 )
+               {
+                       DPRINT( 0, "Failed to apply existing calibration, reseting adc caldacs.\n" );
+                       reset_caldac( setup, layout->adc_pregain_offset );
+                       reset_caldac( setup, layout->adc_postgain_offset );
+                       reset_caldac( setup, layout->adc_gain );
+                       reset_caldac( setup, layout->adc_pregain_offset_fine );
+                       reset_caldac( setup, layout->adc_postgain_offset_fine );
+                       reset_caldac( setup, layout->adc_gain_fine );
+                       reset_caldac( setup, layout->adc_unip_offset );
+                       reset_caldac( setup, layout->adc_unip_offset_fine );
+               }
+       }
+}
+
+static void prep_dac_caldacs_generic( calibration_setup_t *setup,
+       const ni_caldac_layout_t *layout, unsigned int channel, unsigned int range )
+{
+       int retval;
+
+       if( setup->da_subdev < 0 ) return;
+
+       if( setup->old_calibration == NULL )
+       {
+               reset_caldac( setup, layout->dac_offset[ channel ] );
+               reset_caldac( setup, layout->dac_offset_fine[ channel ] );
+               reset_caldac( setup, layout->dac_gain[ channel ] );
+               reset_caldac( setup, layout->dac_gain_fine[ channel ] );
+               reset_caldac( setup, layout->dac_linearity[ channel ] );
+       }else
+       {
+               retval = comedi_apply_parsed_calibration( setup->dev, setup->da_subdev,
+                       channel, range, AREF_GROUND, setup->old_calibration );
+               if( retval < 0 )
+               {
+                       DPRINT( 0, "Failed to apply existing calibration, reseting dac caldacs.\n" );
+                       reset_caldac( setup, layout->dac_offset[ channel ] );
+                       reset_caldac( setup, layout->dac_offset_fine[ channel ] );
+                       reset_caldac( setup, layout->dac_gain[ channel ] );
+                       reset_caldac( setup, layout->dac_gain_fine[ channel ] );
+                       reset_caldac( setup, layout->dac_linearity[ channel ] );
+               }
        }
-       return 0;
 }
 
-double ni_get_reference( calibration_setup_t *setup, int lsb_loc,int msb_loc)
+static void prep_adc_for_dac( calibration_setup_t *setup, int observable )
+{
+       unsigned int adc_range;
+       int chanspec;
+
+       if( observable < 0 ) return;
+
+       chanspec = setup->observables[ observable ].observe_insn.chanspec;
+       adc_range = CR_RANGE( chanspec );
+
+       comedi_apply_parsed_calibration( setup->dev, setup->ad_subdev,
+               0, adc_range, 0, setup->new_calibration );
+}
+
+static int cal_ni_generic( calibration_setup_t *setup, const ni_caldac_layout_t *layout )
+{
+       comedi_calibration_setting_t *current_cal;
+       int retval;
+       int num_ai_ranges;
+       int range;
+       int ai_unipolar_lowgain, ai_bipolar_lowgain;
+
+       num_ai_ranges = comedi_get_n_ranges( setup->dev, setup->ad_subdev, 0 );
+       assert( num_ai_ranges > 0 );
+
+       ai_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->ad_subdev );
+       ai_unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->ad_subdev );
+
+       prep_adc_caldacs_generic( setup, layout, ai_bipolar_lowgain );
+
+       current_cal = sc_alloc_calibration_setting( setup );
+       current_cal->subdevice = setup->ad_subdev;
+       reset_caldac( setup, layout->adc_gain_fine );
+       generic_do_relative( setup, current_cal, ni_zero_offset_low,
+               ni_reference_low, layout->adc_gain );
+       reset_caldac( setup, layout->adc_postgain_offset_fine );
+       generic_do_relative( setup, current_cal, ni_zero_offset_low,
+               ni_zero_offset_high, layout->adc_postgain_offset );
+       generic_do_relative( setup, current_cal, ni_zero_offset_low,
+               ni_zero_offset_high, layout->adc_postgain_offset_fine );
+       reset_caldac( setup, layout->adc_pregain_offset_fine );
+       generic_do_cal( setup, current_cal, ni_zero_offset_high, layout->adc_pregain_offset );
+       generic_do_relative( setup, current_cal, ni_zero_offset_low,
+               ni_reference_low, layout->adc_gain_fine );
+       generic_do_cal( setup, current_cal, ni_zero_offset_high,
+               layout->adc_pregain_offset_fine );
+       sc_push_channel( current_cal, SC_ALL_CHANNELS );
+       sc_push_aref( current_cal, SC_ALL_AREFS );
+       if( layout->adc_unip_offset >= 0 )
+       {
+               sc_push_range( current_cal, SC_ALL_RANGES );
+       }else
+       {
+               for( range = 0; range < num_ai_ranges; range++ )
+               {
+                       if( is_bipolar( setup->dev, setup->ad_subdev, 0, range ) )
+                               sc_push_range( current_cal, range );
+               }
+       }
+
+       /* do seperate unipolar calibration if appropriate */
+       if( ai_unipolar_lowgain >= 0 )
+       {
+               current_cal = sc_alloc_calibration_setting( setup );
+               current_cal->subdevice = setup->ad_subdev;
+               if( layout->adc_unip_offset >= 0 )
+               {
+                       reset_caldac( setup, layout->adc_unip_offset_fine );
+                       generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
+                               layout->adc_unip_offset );
+                       generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
+                               layout->adc_unip_offset_fine );
+               /* if we don't have a unipolar offset caldac, do a fully
+                * independent calibration for unipolar ranges */
+               }else
+               {
+                       prep_adc_caldacs_generic( setup, layout, ai_unipolar_lowgain );
+                       generic_peg( setup, ni_unip_zero_offset_low,
+                               layout->adc_pregain_offset, 1 );
+                       generic_peg( setup, ni_unip_zero_offset_low,
+                               layout->adc_postgain_offset, 1 );
+                       generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
+                               ni_unip_reference_low, layout->adc_gain );
+                       generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
+                               ni_unip_zero_offset_high, layout->adc_postgain_offset );
+                       generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
+                               ni_unip_zero_offset_high, layout->adc_postgain_offset_fine );
+                       generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
+                               layout->adc_pregain_offset );
+                       generic_do_relative( setup, current_cal, ni_unip_zero_offset_low,
+                               ni_unip_reference_low, layout->adc_gain_fine );
+                       generic_do_cal( setup, current_cal, ni_unip_zero_offset_high,
+                               layout->adc_pregain_offset_fine );
+               }
+               for( range = 0; range < num_ai_ranges; range++ )
+               {
+                       if( is_unipolar( setup->dev, setup->ad_subdev, 0, range ) )
+                               sc_push_range( current_cal, range );
+               }
+               sc_push_channel( current_cal, SC_ALL_CHANNELS );
+               sc_push_aref( current_cal, SC_ALL_AREFS );
+       }
+       if( setup->da_subdev >= 0 && setup->do_output )
+       {
+               unsigned int channel, range;
+               int ao_unipolar_lowgain = get_unipolar_lowgain( setup->dev, setup->da_subdev );
+               int ao_bipolar_lowgain = get_bipolar_lowgain( setup->dev, setup->da_subdev );
+               int num_ao_ranges;
+
+               for( channel = 0; channel < 2; channel++ )
+               {
+                       num_ao_ranges = comedi_get_n_ranges( setup->dev, setup->da_subdev, channel );
+                       prep_dac_caldacs_generic( setup, layout, channel, ao_bipolar_lowgain );
+                       prep_adc_for_dac( setup, ni_ao_reference( channel ) );
+
+                       current_cal = sc_alloc_calibration_setting( setup );
+                       current_cal->subdevice = setup->da_subdev;
+                       generic_do_linearity( setup, current_cal, ni_ao_zero_offset( channel ),
+                               ni_ao_mid_linearity( channel ), ni_ao_reference( channel ),
+                               layout->dac_linearity[ channel ] );
+                       reset_caldac(setup, layout->dac_offset_fine[channel]);
+                       generic_do_cal( setup, current_cal, ni_ao_zero_offset( channel ),
+                               layout->dac_offset[ channel ] );
+                       generic_do_cal( setup, current_cal, ni_ao_zero_offset( channel ),
+                               layout->dac_offset_fine[ channel ] );
+                       reset_caldac( setup, layout->dac_gain_fine[ channel ] );
+                       generic_do_cal( setup, current_cal, ni_ao_reference( channel ),
+                               layout->dac_gain[ channel ] );
+                       generic_do_cal( setup, current_cal, ni_ao_reference( channel ),
+                               layout->dac_gain_fine[ channel ] );
+                       sc_push_channel( current_cal, channel );
+                       for( range = 0; range < num_ao_ranges; range++ )
+                       {
+                               if( is_bipolar( setup->dev, setup->da_subdev, channel, range ) )
+                                       sc_push_range( current_cal, range );
+                       }
+                       sc_push_aref( current_cal, SC_ALL_AREFS );
+
+                       if( ao_unipolar_lowgain >= 0 )
+                       {
+                               prep_dac_caldacs_generic( setup, layout, channel, ao_unipolar_lowgain );
+
+                               current_cal = sc_alloc_calibration_setting( setup );
+                               current_cal->subdevice = setup->da_subdev;
+                               generic_do_linearity( setup, current_cal, ni_ao_unip_low_linearity( channel ),
+                                       ni_ao_unip_mid_linearity( channel ), ni_ao_unip_reference( channel ),
+                                       layout->dac_linearity[ channel ] );
+                               reset_caldac( setup, layout->dac_offset_fine[ channel ] );
+                               generic_do_cal( setup, current_cal, ni_ao_unip_zero_offset( channel),
+                                       layout->dac_offset[ channel ] );
+                               generic_do_cal( setup, current_cal, ni_ao_unip_zero_offset( channel),
+                                       layout->dac_offset_fine[ channel ] );
+                               reset_caldac( setup, layout->dac_gain_fine[ channel ] );
+                               generic_do_cal( setup, current_cal, ni_ao_unip_reference( channel ),
+                                       layout->dac_gain[ channel ] );
+                               generic_do_cal( setup, current_cal, ni_ao_unip_reference( channel ),
+                                       layout->dac_gain_fine[ channel ] );
+                               sc_push_channel( current_cal, channel );
+                               for( range = 0; range < num_ao_ranges; range++ )
+                               {
+                                       if( is_unipolar( setup->dev, setup->da_subdev, channel, range ) )
+                                               sc_push_range( current_cal, range );
+                               }
+                               sc_push_aref( current_cal, SC_ALL_AREFS );
+                       }
+               }
+       }
+
+       retval = write_calibration_file( setup );
+
+       return retval;
+}
+
+static double ni_get_reference( calibration_setup_t *setup, int lsb_loc,int msb_loc)
 {
        int lsb,msb;
-       int uv;
+       int16_t uv;
        double ref;
 
-       lsb=read_eeprom( setup, lsb_loc);
-       msb=read_eeprom( setup, msb_loc);
-       printf("lsb=%d msb=%d\n",read_eeprom( setup, 425),read_eeprom( setup, 426));
+       lsb=read_eeprom( setup, lsb_loc );
+       msb=read_eeprom( setup, msb_loc );
+       assert( lsb >=0 && msb >= 0 );
+       DPRINT(0,"eeprom reference lsb=%d msb=%d\n", lsb, msb);
 
-       uv=lsb | (msb<<8);
-       if(uv>=0x8000)uv-=0x10000;
+       uv = ( lsb & 0xff ) | ( ( msb << 8 ) & 0xff00 );
        ref=5.000+1.0e-6*uv;
-       printf("ref=%g\n",ref);
+       DPRINT(0, "resulting reference voltage: %g\n", ref );
+       if( fabs( ref - 5.0 ) > 0.005 )
+               DPRINT( 0, "WARNING: eeprom indicates reference is more than 5mV away\n"
+                       "from 5V.  Possible bad eeprom address?\n" );
 
        return ref;
 }
 
-#if 0
-void cal_ni_results(void)
+/****************
+ NI 671x and 673x support
+ **************/
+
+static const int channels_per_ad8804 = 16;
+
+static inline int ni67xx_ao_gain_caldac(unsigned int ao_channel)
 {
-       comedi_range *range;
-       int bipolar_lowgain;
-       int bipolar_highgain;
-       int unipolar_lowgain;
-       //int have_ao;
-       char s[32];
+       int ad8804_gain_channels[4] = {8, 2, 11, 5};
+       int caldac_channel = ad8804_gain_channels[ao_channel % 4];
+       int caldac_index = ao_channel / 4;
+       /* just guessing that second ad8804 is works for ao channels 4-7
+        * the same as the first ad8804 works for ao channels 0-3 */
+       return caldac_index * channels_per_ad8804 + caldac_channel;
+}
+static inline int ni67xx_ao_linearity_caldac(unsigned int ao_channel)
+{
+       int ad8804_linearity_channels[4] = {4, 10, 1, 0};
+       int caldac_channel = ad8804_linearity_channels[ao_channel % 4];
+       int caldac_index = ao_channel / 4;
 
-       bipolar_lowgain = get_bipolar_lowgain(dev,setup->ad_subdev);
-       bipolar_highgain = get_bipolar_highgain(dev,setup->ad_subdev);
-       unipolar_lowgain = get_unipolar_lowgain(dev,setup->ad_subdev);
+       return caldac_index * channels_per_ad8804 + caldac_channel;
+}
+static inline int ni67xx_ao_offset_caldac(unsigned int ao_channel)
+{
+       int ad8804_offset_channels[4] = {7, 6, 9, 3};
+       int caldac_channel = ad8804_offset_channels[ao_channel % 4];
+       int caldac_index = ao_channel / 4;
 
-       /* 0 offset, low gain */
-       range = comedi_get_range(dev,setup->ad_subdev,0,bipolar_lowgain);
-       read_chan2(s,0,bipolar_lowgain);
-       DPRINT(0,"bipolar zero offset, low gain [%g,%g]: %s\n",
-               range->min,range->max,s);
+       return caldac_index * channels_per_ad8804 + caldac_channel;
+}
 
-       /* 0 offset, high gain */
-       range = comedi_get_range(dev,setup->ad_subdev,0,bipolar_highgain);
-       read_chan2(s,0,bipolar_highgain);
-       DPRINT(0,"bipolar zero offset, high gain [%g,%g]: %s\n",
-               range->min,range->max,s);
+static int ni67xx_ao_ground_observable_index( const calibration_setup_t *setup,
+       unsigned int channel, unsigned int ao_range )
+{
+       return 3 * channel + 0;
+}
 
-       /* unip/bip offset */
-       range = comedi_get_range(dev,setup->ad_subdev,0,unipolar_lowgain);
-       read_chan2(s,0,unipolar_lowgain);
-       DPRINT(0,"unipolar zero offset, low gain [%g,%g]: %s\n",
-               range->min,range->max,s);
+static int ni67xx_ao_mid_observable_index( const calibration_setup_t *setup,
+       unsigned int channel, unsigned int ao_range )
+{
+       return 3 * channel + 1;
+}
 
+static int ni67xx_ao_high_observable_index( const calibration_setup_t *setup,
+       unsigned int channel, unsigned int ao_range )
+{
+       return 3 * channel + 2;
 }
 
-void ni_mio_ai_postgain_cal(void)
+static const double ni67xx_unitless_adc_offset = 0.5;
+
+/* determine conversion factor between actual voltage and
+ * interval [0,1) returned by reads from the calibration adc
+ * subdevice.
+ */
+static double ni67xx_unitless_adc_slope(calibration_setup_t *setup)
 {
-       linear_fit_t l;
-       double offset_r0;
-       double offset_r7;
-       double gain;
-       double a;
+       double reference_in_volts;
+       double reference_unitless;
+       double slope;
+       comedi_insn insn;
+       lsampl_t data;
+       comedi_range *range;
+       static const int maxdata = 0x10000;
+       int retval;
+
+       if(ni_board(setup)->ref_eeprom_lsb >= 0 &&
+               ni_board(setup)->ref_eeprom_msb >= 0)
+       {
+               reference_in_volts = ni_get_reference(setup,
+                       ni_board(setup)->ref_eeprom_lsb, ni_board(setup)->ref_eeprom_msb );
+       }else
+       {
+               DPRINT( 0, "WARNING: unknown eeprom address for reference voltage\n"
+                       "correction.  This might be fixable if you send us an eeprom dump\n"
+                       "(see the demo/eeprom_dump program).\n");
+               reference_in_volts = 5.0;
+       }
 
-       check_gain_chan_x(&l,CR_PACK(0,0,AREF_OTHER),1);
-       offset_r0=linear_fit_func_y(&l,caldacs[1].current);
-       printf("offset r0 %g\n",offset_r0);
+       memset(&insn, 0, sizeof(insn));
+       insn.insn = INSN_READ;
+       insn.n = 1;
+       insn.subdev = setup->ad_subdev;
+       insn.data = &data;
+       insn.chanspec = CR_PACK(0, 0, AREF_GROUND) | CR_ALT_SOURCE;
+       retval = comedi_do_insn(setup->dev, &insn);
+       assert(retval >= 0);
 
-       check_gain_chan_x(&l,CR_PACK(0,7,AREF_OTHER),1);
-       offset_r7=linear_fit_func_y(&l,caldacs[1].current);
-       printf("offset r7 %g\n",offset_r7);
+       range = comedi_get_range(setup->dev, setup->ad_subdev, 0, 0);
+       assert( range );
+       reference_unitless = comedi_to_phys(data, range, maxdata);
 
-       gain=l.slope;
-       
-       a=(offset_r0-offset_r7)/(200.0-1.0);
-       a=caldacs[1].current-a/gain;
+       slope = (reference_unitless - ni67xx_unitless_adc_offset) / reference_in_volts;
 
-       printf("%g\n",a);
+       return slope;
+}
 
-       caldacs[1].current=rint(a);
-       update_caldac(1);
+/* calibration adc uses RANGE_UNKNOWN, so it will return a value from
+   0.0 to 1.0 instead of a voltage, so we need to renormalize. */
+static void ni67xx_set_target( calibration_setup_t *setup, int obs, double target, double slope)
+{
+       set_target(setup, obs, target);
+       /* convert target from volts to interval [0,1) which calibration
+        * adc returns */
+       setup->observables[obs].target *= slope;
+       setup->observables[obs].target += ni67xx_unitless_adc_offset;
 }
 
-void ni_mio_ai_postgain_cal_2(int chan,int dac,int range_lo,int range_hi,double gain)
+static void ni67xx_setup_observables( calibration_setup_t *setup )
 {
-       double offset_lo,offset_hi;
-       linear_fit_t l;
+       comedi_insn tmpl, po_tmpl;
+       observable *o;
+       int num_ao_channels;
+       int i;
        double slope;
-       double a;
 
-       check_gain_chan_x(&l,CR_PACK(chan,range_lo,AREF_OTHER),dac);
-       offset_lo=linear_fit_func_y(&l,caldacs[dac].current);
-       printf("offset lo %g\n",offset_lo);
+       slope = ni67xx_unitless_adc_slope(setup);
 
-       check_gain_chan_x(&l,CR_PACK(chan,range_hi,AREF_OTHER),dac);
-       offset_hi=linear_fit_func_y(&l,caldacs[dac].current);
-       printf("offset hi %g\n",offset_hi);
+       /* calibration adc is very slow (15HZ) but accurate, so only sample a few times */
+       setup->sv_order = 1;
 
-       slope=l.slope;
-       
-       a=(offset_lo-offset_hi)/(gain-1.0);
-       a=caldacs[dac].current-a/slope;
+       num_ao_channels = comedi_get_n_channels(setup->dev, setup->da_subdev);
+       assert(num_ao_channels >= 0);
+
+       memset( &tmpl, 0, sizeof(tmpl) );
+       tmpl.insn = INSN_READ;
+       tmpl.n = 1;
+       tmpl.subdev = setup->ad_subdev;
 
-       printf("%g\n",a);
+       memset( &po_tmpl, 0, sizeof(po_tmpl) );
+       po_tmpl.insn = INSN_WRITE;
+       po_tmpl.n = 1;
+       po_tmpl.subdev = setup->da_subdev;
 
-       caldacs[dac].current=rint(a);
-       update_caldac(dac);
+       setup->n_observables = 0;
+
+       for(i = 0; i < num_ao_channels; i++)
+       {
+               o = setup->observables + ni67xx_ao_ground_observable_index( setup,
+                       i, 0);
+               o->reference_source = -1;
+               assert( o->name == NULL );
+               asprintf(&o->name, "dac%i ground, ground referenced", i);
+               o->preobserve_insn = po_tmpl;
+               o->preobserve_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
+               o->preobserve_insn.data = o->preobserve_data;
+               o->observe_insn = tmpl;
+               o->observe_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
+               ni67xx_set_target(setup, ni67xx_ao_ground_observable_index(setup, i, 0), 0.0, slope);
+               setup->n_observables++;
+
+               o = setup->observables + ni67xx_ao_mid_observable_index( setup,
+                       i, 0);
+               o->reference_source = -1;
+               assert( o->name == NULL );
+               asprintf(&o->name, "dac%i mid, ground referenced", i);
+               o->preobserve_insn = po_tmpl;
+               o->preobserve_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
+               o->preobserve_insn.data = o->preobserve_data;
+               o->observe_insn = tmpl;
+               o->observe_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
+               ni67xx_set_target(setup, ni67xx_ao_mid_observable_index(setup, i, 0), 4.0, slope);
+               setup->n_observables++;
+
+               o = setup->observables + ni67xx_ao_high_observable_index( setup, i, 0);
+               o->reference_source = -1;
+               assert( o->name == NULL );
+               asprintf(&o->name, "dac%i high, ground referenced", i);
+               o->preobserve_insn = po_tmpl;
+               o->preobserve_insn.chanspec = CR_PACK( i, 0, AREF_GROUND );
+               o->preobserve_insn.data = o->preobserve_data;
+               o->observe_insn = tmpl;
+               o->observe_insn.chanspec = CR_PACK(i, 0, AREF_GROUND);
+               ni67xx_set_target(setup, ni67xx_ao_high_observable_index(setup, i, 0), 8.0, slope);
+               setup->n_observables++;
+       }
+
+       return;
+}
+
+static int cal_ni_pci_6711(calibration_setup_t *setup)
+{
+       generic_layout_t layout;
+
+       init_generic_layout( &layout );
+       layout.dac_gain = ni67xx_ao_gain_caldac;
+       layout.dac_linearity = ni67xx_ao_linearity_caldac;
+       layout.dac_offset = ni67xx_ao_offset_caldac;
+       layout.dac_high_observable = ni67xx_ao_high_observable_index;
+       layout.dac_mid_observable = ni67xx_ao_mid_observable_index;
+       layout.dac_ground_observable = ni67xx_ao_ground_observable_index;
+       layout.dac_fractional_tolerance = get_tolerance( setup, setup->da_subdev, 1.0 );
+       return generic_cal_ao(setup, &layout);
 }
-#endif