Initial revision
authorDavid Schleef <ds@schleef.org>
Wed, 2 Feb 2000 05:14:23 +0000 (05:14 +0000)
committerDavid Schleef <ds@schleef.org>
Wed, 2 Feb 2000 05:14:23 +0000 (05:14 +0000)
79 files changed:
INSTALL [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
comedi_calibrate/Makefile [new file with mode: 0644]
comedi_calibrate/comedi_calibrate.c [new file with mode: 0644]
demo/Makefile [new file with mode: 0644]
demo/README [new file with mode: 0644]
demo/ao_waveform.c [new file with mode: 0644]
demo/cmd.c [new file with mode: 0644]
demo/eeprom_dump.c [new file with mode: 0644]
demo/info.c [new file with mode: 0644]
demo/inp.c [new file with mode: 0644]
demo/inpn.c [new file with mode: 0644]
demo/main.c [new file with mode: 0644]
demo/mode1.c [new file with mode: 0644]
demo/mode2.c [new file with mode: 0644]
demo/mode3.c [new file with mode: 0644]
demo/mode4.c [new file with mode: 0644]
demo/outp.c [new file with mode: 0644]
demo/rt/Makefile [new file with mode: 0644]
demo/rt/README [new file with mode: 0644]
demo/rt/ai.c [new file with mode: 0644]
demo/rt/it.c [new file with mode: 0644]
demo/sv.c [new file with mode: 0644]
demo/tut1.c [new file with mode: 0644]
demo/tut2.c [new file with mode: 0644]
doc/FAQ [new file with mode: 0644]
doc/comedilib.sgml [new file with mode: 0644]
doc/html/comedilib-1.html [new file with mode: 0644]
doc/html/comedilib-2.html [new file with mode: 0644]
doc/html/comedilib-3.html [new file with mode: 0644]
doc/html/comedilib-4.html [new file with mode: 0644]
doc/html/comedilib-5.html [new file with mode: 0644]
doc/html/comedilib-6.html [new file with mode: 0644]
doc/html/comedilib.html [new file with mode: 0644]
doc/tutorial [new file with mode: 0644]
include/comedilib.h [new file with mode: 0644]
lib/Makefile [new file with mode: 0644]
lib/comedi.c [new file with mode: 0644]
lib/data.c [new file with mode: 0644]
lib/dio.c [new file with mode: 0644]
lib/error.c [new file with mode: 0644]
lib/filler.c [new file with mode: 0644]
lib/get.c [new file with mode: 0644]
lib/ioctl.c [new file with mode: 0644]
lib/libinternal.h [new file with mode: 0644]
lib/range.c [new file with mode: 0644]
lib/sv.c [new file with mode: 0644]
lib/timed.c [new file with mode: 0644]
lib/timer.c [new file with mode: 0644]
lib/version_script [new file with mode: 0644]
perl/Changes [new file with mode: 0644]
perl/Comedi.pm [new file with mode: 0644]
perl/Comedi.xs [new file with mode: 0644]
perl/INSTALL [new file with mode: 0644]
perl/Lib/Changes [new file with mode: 0644]
perl/Lib/Lib.pm [new file with mode: 0644]
perl/Lib/Lib.xs [new file with mode: 0644]
perl/Lib/Makefile.PL [new file with mode: 0644]
perl/MANIFEST [new file with mode: 0644]
perl/Makefile.PL [new file with mode: 0644]
perl/README [new file with mode: 0644]
perl/Trigger/Changes [new file with mode: 0644]
perl/Trigger/Makefile.PL [new file with mode: 0644]
perl/Trigger/Trigger.pm [new file with mode: 0644]
perl/Trigger/test.pl [new file with mode: 0644]
perl/example.perl [new file with mode: 0644]
perl/info.perl [new file with mode: 0644]
perl/lperl [new file with mode: 0755]
perl/mode1.perl [new file with mode: 0644]
perl/mode2.perl [new file with mode: 0644]
perl/mode2lib.perl [new file with mode: 0644]
perl/test.pl [new file with mode: 0644]
perl/typemap [new file with mode: 0644]
testing/Makefile [new file with mode: 0644]
testing/inpn.c [new file with mode: 0644]
testing/main.c [new file with mode: 0644]
testing/mode1.c [new file with mode: 0644]
testing/mode2.c [new file with mode: 0644]

diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..ce3024c
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,21 @@
+
+Comedi has a mailing list.  Send "subscribe comedi" to
+majordomo@stm.lbl.gov to subscribe.
+
+If you are upgrading from a previous version, see the upgrade notes
+below.
+
+Compile using 'make'.  If this doesn't work, make sure you have the
+basic tools installed to compile.  If you can successfully compile
+other things, consult the author, as he has probably made a mistake.
+
+Install using 'make install' as root.  This installs the files:
+       /usr/lib/libcomedi.so.0.3
+       /usr/include/comedi.h
+       /usr/include/comedilib.h
+       and other things...
+
+To write programs that use comedi, look at examples in the demo/ and
+the documentation in the doc/ directory.
+
+
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..7b8ad6d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,61 @@
+
+# Makefile for comedi
+
+.EXPORT_ALL_VARIABLES:
+
+VERSION = 0
+SUBVERSION = 7
+SUBSUBVERSION = 8
+
+VERSION_CODE = ${VERSION}.${SUBVERSION}.${SUBSUBVERSION}
+
+CFLAGS = -Wall -O2
+
+all:   comedilib
+
+SUBDIRS= lib demo comedi_calibrate testing
+
+DOCFILES= README INSTALL `find doc -type f`
+
+ifeq ($(CONFIG_USRLOCAL),y)
+INSTALLDIR=/usr/local
+else
+INSTALLDIR=/usr
+endif
+
+comedilib:     subdirs
+
+config:        dummy
+
+install:       dummy
+       install -d ${INSTALLDIR}/include
+       (cd include;install -m 644 comedilib.h ${INSTALLDIR}/include)
+       install lib/libcomedi.so.${VERSION_CODE} ${INSTALLDIR}/lib
+       #ln -s ${INSTALLDIR}/lib/libcomedi.so.${VERSION_CODE} ${INSTALLDIR}/lib/libcomedi.so
+       install -m 644 lib/libcomedi.a ${INSTALLDIR}/lib
+       /sbin/ldconfig -n ${INSTALLDIR}/lib
+       install -d ${INSTALLDIR}/doc/comedilib-${VERSION_CODE}
+       install ${DOCFILES} ${INSTALLDIR}/doc/comedilib-${VERSION_CODE}
+
+lpr:   dummy
+       find . -name '*.[chs]'|xargs enscript -2r -pit.ps
+
+subdirs:       dummy
+       set -e;for i in ${SUBDIRS};do ${MAKE} -C $$i ; done
+
+clean: dummy
+       set -e;for i in $(SUBDIRS);do ${MAKE} clean -C $$i ; done
+
+distclean:     clean
+
+dev:   dummy
+       -rm /dev/comedi*
+       /bin/mknod /dev/comedi0 c 98 0
+       /bin/mknod /dev/comedi1 c 98 1
+       /bin/mknod /dev/comedi2 c 98 2
+       /bin/mknod /dev/comedi3 c 98 3
+       chown root.root /dev/comedi*
+       chmod 666 /dev/comedi*
+
+dummy:
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..17439ae
--- /dev/null
+++ b/README
@@ -0,0 +1,56 @@
+
+
+
+       Linux Control and Measurement Device Interface (comedi)
+
+               David Schleef
+               <ds@stm.lbl.gov>
+
+
+A new minor version of comedi, 0.6.0, is now available at
+<ftp://stm.lbl.gov/pub/comedi/>.  The change in minor version
+level to 6 represents a complete redesign and rewrite of
+the kernel interface.  Because of this, 0.6.0 is almost
+completely incompatible with previous versions.
+
+This redesign was necessary for current and future growth
+of comedi.  The current needs that have been addressed are
+SMP and RT compatibility, the need to be more friendly to
+high-level programming, and an overall simplification of
+the interface.  Using the 0.6 series should eventually be
+easier and more understandable.
+
+The major changes from the 0.5 series are:
+
+       - Comedi devices are now divided into "subdevices",
+         such as a A/D subsystem or D/A subsystem, instead
+         of channels.  This eliminates much of the redundant
+         information and coding that was present in the 0.5
+         series, and also allows proper locking of devices
+         necessary for proper SMP and RT performance.
+       
+       - The 0.6 series will allow multiple methods for
+         data transfer from hardware to your user-level
+         process, including read()/write(), ioctl(), and
+         mmap(), giving users needed flexibility.
+       
+       - Hardware drivers that are real-time compatibile will
+         now be able to perform RTLinux tasks timed by device
+         interrupts.
+
+Upgrading to 0.6 should not be too difficult, although it
+will require editing and recompiling all programs that use
+comedi, since the interface has changed.
+
+Comedi includes hardware drivers for many boards.  See ./drivers
+for details.
+
+If you want to add support for another hardware device, you
+will want to look at the comedi hardware driver writing HOWTO
+in the doc directory.  It is fairly simple to write a new hardware
+driver for comedi, but I am always willing to answer any questions
+you might have.
+
+Comedi may be freely distibuted and modified in accordance with the
+GNU General Public License.
+
diff --git a/comedi_calibrate/Makefile b/comedi_calibrate/Makefile
new file mode 100644 (file)
index 0000000..bffc0af
--- /dev/null
@@ -0,0 +1,16 @@
+
+
+
+CFLAGS +=-I ../include -I . -O2 -Wall
+LDFLAGS=-L../lib/ -lcomedi
+
+
+BINS=comedi_calibrate
+
+all: $(BINS)
+
+$(patsubst %,_bins_%,$(BINS)) : dummy
+       $(CC) -o $(patsubst _bins_%,%,$@) $(patsubst _bins_%,%.o,$@) $(LDFLAGS)
+
+clean:
+       -rm *.o $(BINS)
diff --git a/comedi_calibrate/comedi_calibrate.c b/comedi_calibrate/comedi_calibrate.c
new file mode 100644 (file)
index 0000000..81cf1f1
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+   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
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#define N_CALDACS 16
+
+typedef struct{
+       int subdev;
+       int chan;
+
+       int maxdata;
+       int current;
+
+       int type;
+       double gain;
+}caldac;
+
+static caldac caldacs[N_CALDACS];
+static int n_caldacs;
+static comedi_t *dev;
+
+static int ad_subdev;
+static int eeprom_subdev;
+
+double read_chan(int adc,int range);
+void write_caldac(comedi_t *dev,int subdev,int addr,int val);
+void check_gain(int ad_chan,int range);
+double check_gain_chan(int ad_chan,int range,int cdac);
+
+
+void update_caldac(int i);
+void reset_caldacs(void);
+void setup_caldacs(void);
+void cal_ni_mio_E(void);
+void ni_mio_ai_postgain_cal(void);
+void channel_dependence(int adc,int range);
+void caldac_dependence(int caldac);
+void dump_curve(int adc,int caldac);
+void chan_cal(int adc,int caldac,int range,double target);
+int read_eeprom(int addr);
+
+typedef struct {
+       int n;
+
+       double *y_data;
+       double *yerr_data;
+       double *x_data;
+
+       double x0;
+       double dx;
+       double yerr;
+
+       /* stats */
+       double s1,sx,sy,sxy,sxx;
+
+       /* results */
+       double ave_x;
+       double ave_y;
+       double slope;
+       double err_slope;
+       double err_ave_y;
+       double S_min;
+       double dof;
+
+}linear_fit_t;
+int linear_fit_monotonic(linear_fit_t *l);
+double linear_fit_func_y(linear_fit_t *l,double x);
+double check_gain_chan_x(linear_fit_t *l,int ad_chan,int range,int cdac);
+
+typedef struct{
+       comedi_t *dev;
+       comedi_trig t;
+
+       unsigned int chanlist[1];
+
+       int maxdata;
+       int order;
+       comedi_range *rng;
+
+       int n;
+       double average;
+       double stddev;
+       double error;
+}new_sv_t;
+
+int new_sv_measure(new_sv_t *sv);
+int new_sv_init(new_sv_t *sv,comedi_t *dev,int subdev,int chan,int range,int aref);
+
+int main(int argc, char *argv[])
+{
+       char *fn = NULL;
+       int c;
+       char *drivername;
+
+
+       fn = "/dev/comedi0";
+       while (1) {
+               c = getopt(argc, argv, "rf");
+               if (c == -1)
+                       break;
+               switch (c) {
+               case 'f':
+                       fn = argv[optind];
+                       break;
+               default:
+                       printf("bad option\n");
+                       exit(1);
+               }
+       }
+
+       dev = comedi_open(fn);
+       if (dev == NULL ) {
+               perror(fn);
+               exit(0);
+       }
+
+       ad_subdev=0;
+       setup_caldacs();
+
+       eeprom_subdev=comedi_find_subdevice_by_type(dev,COMEDI_SUBD_MEMORY,0);
+
+       drivername=comedi_get_driver_name(dev);
+
+       if(!strcmp(drivername,"atmio-E") || !strcmp(drivername,"pcimio-E"))
+               cal_ni_mio_E();
+
+       return 0;
+}
+
+
+void cal_ni_mio_E(void)
+{
+       char *boardname;
+       double ref;
+       int i;
+
+       boardname=comedi_get_board_name(dev);
+
+       if(!strcmp(boardname,"at-mio-16e-1") ||
+          !strcmp(boardname,"at-mio-16e-2") ||
+          !strcmp(boardname,"at-mio-64e-3") ||
+          !strcmp(boardname,"pci-mio-16e-1")){
+/*
+   layout
+
+   0   AI pre-gain offset      1.5e-6
+   1   AI post-gain offset     8.1e-4
+   2   AI unipolar offset      7.9e-4
+   3   AI gain                 
+   4   AO
+   5   AO
+   6   AO
+   7   A0
+   8   AO
+   9   AO
+   10  analog trigger
+   11  unknown
+ */
+               printf("last factory calibration %02d/%02d/%02d\n",
+                       read_eeprom(508),read_eeprom(507),read_eeprom(506));
+
+               printf("lsb=%d msb=%d\n",read_eeprom(425),read_eeprom(426));
+
+               ref=5.000+(1e-6*(read_eeprom(425)+read_eeprom(426)));
+               printf("ref=%g\n",ref);
+
+               reset_caldacs();
+
+               printf("postgain offset\n");
+               ni_mio_ai_postgain_cal();
+
+               printf("pregain offset\n");
+               chan_cal(0,0,7,0.0);
+               chan_cal(0,0,7,0.0);
+
+               printf("unipolar offset\n");
+               chan_cal(0,2,8,0.0);
+               chan_cal(0,2,8,0.0);
+
+               printf("gain offset\n");
+               chan_cal(5,3,0,5.0);
+               chan_cal(5,3,0,5.0);
+
+               return;
+       }
+       if(!strcmp(boardname,"at-mio-16e-10")){
+/*
+   layout
+
+   0   AI pre-gain offset      1.5e-6
+   1   AI post-gain offset     8.1e-4
+   2   AI unipolar offset      7.9e-4
+   3   AI gain                 3.5e-4
+   4   AO
+   5   AO
+   6   AO
+   7   AO
+   8   AO
+   9   AO
+   10  AI pre-gain offset      6.4e-5
+   11  unknown
+ */
+               printf("last factory calibration %02d/%02d/%02d\n",
+                       read_eeprom(508),read_eeprom(507),read_eeprom(506));
+
+               printf("lsb=%d msb=%d\n",read_eeprom(423),read_eeprom(424));
+
+               ref=5.000+(0.001*(read_eeprom(423)+read_eeprom(424)));
+               printf("ref=%g\n",ref);
+
+               reset_caldacs();
+
+               printf("postgain offset\n");
+               ni_mio_ai_postgain_cal();
+
+               printf("pregain offset\n");
+               chan_cal(0,10,7,0.0);
+               chan_cal(0,0,7,0.0);
+               chan_cal(0,0,7,0.0);
+
+               printf("unipolar offset\n");
+               chan_cal(0,2,8,0.0);
+               chan_cal(0,2,8,0.0);
+
+               printf("gain offset\n");
+               chan_cal(5,3,0,5.0);
+               chan_cal(5,3,0,5.0);
+
+               printf("results (offset)\n");
+               for(i=0;i<16;i++){
+                       read_chan(0,i);
+               }
+
+               return;
+       }
+
+       {
+               int n_ranges;
+
+       reset_caldacs();
+       printf("please send this output to <ds@stm.lbl.gov>\n");
+       printf("%s\n",comedi_get_board_name(dev));
+
+       n_ranges=comedi_get_n_ranges(dev,ad_subdev,0);
+
+       printf("channel dependence 0 range 0\n");
+       channel_dependence(0,0);
+
+       printf("channel dependence 0 range %d\n",n_ranges/2-1);
+       channel_dependence(0,n_ranges/2-1);
+
+       printf("channel dependence 0 range %d\n",n_ranges/2);
+       channel_dependence(0,n_ranges/2);
+
+       printf("channel dependence 5 range 0\n");
+       channel_dependence(5,0);
+       }
+}
+
+
+void ni_mio_ai_postgain_cal(void)
+{
+       linear_fit_t l;
+       double offset_r0;
+       double offset_r7;
+       double gain;
+       double a;
+
+       check_gain_chan_x(&l,0,0,1);
+       offset_r0=linear_fit_func_y(&l,caldacs[1].current);
+       printf("offset r0 %g\n",offset_r0);
+
+       check_gain_chan_x(&l,0,7,1);
+       offset_r7=linear_fit_func_y(&l,caldacs[1].current);
+       printf("offset r7 %g\n",offset_r7);
+
+       gain=l.slope;
+       
+       a=(offset_r0-offset_r7)/(200.0-1.0);
+       a=caldacs[1].current-a/gain;
+
+       printf("%g\n",a);
+
+       caldacs[1].current=rint(a);
+       update_caldac(1);
+}
+
+void chan_cal(int adc,int cdac,int range,double target)
+{
+       linear_fit_t l;
+       double offset;
+       double gain;
+       double a;
+
+       check_gain_chan_x(&l,adc,range,cdac);
+       offset=linear_fit_func_y(&l,caldacs[cdac].current);
+       printf("offset %g\n",offset);
+       gain=l.slope;
+       
+       a=caldacs[cdac].current+(target-offset)/gain;
+       printf("%g\n",a);
+
+       caldacs[cdac].current=rint(a);
+       update_caldac(cdac);
+}
+
+
+
+void channel_dependence(int adc,int range)
+{
+       int i;
+       double gain;
+
+       for(i=0;i<n_caldacs;i++){
+               gain=check_gain_chan(adc,range,i);
+       }
+}
+
+void caldac_dependence(int caldac)
+{
+       int i;
+       double gain;
+
+       for(i=0;i<8;i++){
+               gain=check_gain_chan(i,0,caldac);
+       }
+}
+
+int dump_flag;
+
+void dump_curve(int adc,int caldac)
+{
+       linear_fit_t l;
+
+       dump_flag=1;
+       check_gain_chan_x(&l,adc,0,caldac);
+       dump_flag=0;
+}
+
+
+void setup_caldacs(void)
+{
+       int s,n_chan,i;
+
+       s=comedi_find_subdevice_by_type(dev,COMEDI_SUBD_CALIB,0);
+       if(s<0){
+               printf("no calibration subdevice\n");
+               return;
+       }
+       //printf("calibration subdevice is %d\n",s);
+
+       n_chan=comedi_get_n_channels(dev,s);
+
+       for(i=0;i<n_chan;i++){
+               caldacs[i].subdev=s;
+               caldacs[i].chan=i;
+               caldacs[i].maxdata=comedi_get_maxdata(dev,s,i);
+/* XXX */
+caldacs[i].maxdata=255;
+               caldacs[i].current=0;
+               //printf("caldac %d, %d\n",i,caldacs[i].maxdata);
+       }
+
+       n_caldacs=n_chan;
+}
+
+void reset_caldacs(void)
+{
+       int i;
+
+       for(i=0;i<n_caldacs;i++){
+               caldacs[i].current=caldacs[i].maxdata/2;
+               update_caldac(i);
+       }
+}
+
+void update_caldac(int i)
+{
+       int ret;
+       
+       //printf("update %d %d %d\n",caldacs[i].subdev,caldacs[i].chan,caldacs[i].current);
+       //ret=comedi_data_write(dev,caldacs[i].subdev,caldacs[i].chan,0,0,caldacs[i].current);
+       write_caldac(dev,caldacs[i].subdev,caldacs[i].chan,caldacs[i].current);
+       ret=0;
+       if(ret<0)
+               printf("comedi_data_write() error\n");
+}
+
+
+void check_gain(int ad_chan,int range)
+{
+       int i;
+
+       for(i=0;i<n_caldacs;i++){
+               check_gain_chan(ad_chan,range,i);
+       }
+}
+
+double check_gain_chan(int ad_chan,int range,int cdac)
+{
+       linear_fit_t l;
+
+       return check_gain_chan_x(&l,ad_chan,range,cdac);
+}
+
+double check_gain_chan_x(linear_fit_t *l,int ad_chan,int range,int cdac)
+{
+       int orig,i,n;
+       new_sv_t sv;
+       double sum_err;
+       int sum_err_count=0;
+
+       n=caldacs[cdac].maxdata+1;
+       memset(l,0,sizeof(*l));
+       l->y_data=malloc(n*sizeof(double));
+
+       orig=caldacs[cdac].current;
+
+       new_sv_init(&sv,dev,0,ad_chan,range,AREF_OTHER);
+
+       sum_err=0;
+       for(i=0;i<n;i++){
+               caldacs[cdac].current=i;
+               update_caldac(cdac);
+
+               new_sv_measure(&sv);
+
+               l->y_data[i]=sv.average;
+               if(!isnan(sv.average)){
+                       sum_err+=sv.error;
+                       sum_err_count++;
+               }
+       }
+
+       caldacs[cdac].current=orig;
+       update_caldac(cdac);
+
+       l->yerr=sum_err/sum_err_count;
+       l->n=n;
+       l->dx=1;
+       l->x0=0;
+
+       linear_fit_monotonic(l);
+
+       printf("caldac[%d] gain=%g V/bit err=%g S_min=%g dof=%g\n",
+               cdac,l->slope,l->err_slope,l->S_min,l->dof);
+       //printf("--> %g\n",fabs(l.slope/l.err_slope));
+
+       if(dump_flag){
+               for(i=0;i<n;i++){
+                       printf("%d %g\n",i,l->y_data[i]);
+               }
+       }
+
+
+       free(l->y_data);
+
+       return l->slope;
+}
+
+
+
+
+/* helpers */
+
+
+int read_eeprom(int addr)
+{
+       unsigned int data=0;
+
+       comedi_data_read(dev,eeprom_subdev,addr,0,0,&data);
+
+       return data;
+}
+
+double read_chan(int adc,int range)
+{
+       int n;
+       new_sv_t sv;
+
+       new_sv_init(&sv,dev,0,adc,range,AREF_OTHER);
+       sv.order=10;
+       n=new_sv_measure(&sv);
+
+       printf("chan=%d ave=%g error=%g\n",adc,sv.average,sv.error);
+
+       return sv.average;
+}
+
+void write_caldac(comedi_t *dev,int subdev,int addr,int val)
+{
+       comedi_trig it;
+       unsigned int chan=CR_PACK(addr,0,0);
+       sampl_t data=val;
+
+       memset(&it,0,sizeof(it));
+
+       it.subdev = subdev;
+       it.n_chan = 1;
+       it.chanlist = &chan;
+       it.data = &data;
+       it.n = 1;
+
+       if(comedi_trigger(dev,&it)<0){
+               perror("write_caldac");
+               exit(0);
+       }
+}
+
+
+
+int new_sv_init(new_sv_t *sv,comedi_t *dev,int subdev,int chan,int range,int aref)
+{
+       memset(sv,0,sizeof(*sv));
+
+       sv->t.subdev=subdev;
+       sv->t.flags=TRIG_DITHER;
+       sv->t.n_chan=1;
+       sv->t.chanlist=sv->chanlist;
+
+       sv->chanlist[0]=CR_PACK(chan,range,aref);
+
+       sv->maxdata=comedi_get_maxdata(dev,subdev,chan);
+       sv->rng=comedi_get_range(dev,subdev,chan,range);
+
+       sv->order=7;
+
+       return 0;
+}
+
+int new_sv_measure(new_sv_t *sv)
+{
+       sampl_t *data;
+       int n,i,ret;
+
+       double a,x,s,s2;
+
+       n=1<<sv->order;
+
+       data=malloc(sizeof(sampl_t)*n);
+
+       for(i=0;i<n;){
+               sv->t.data=data+i;
+               sv->t.n=n-i;
+               ret=comedi_trigger(dev,&sv->t);
+               if(ret<0)goto out;
+               i+=ret;
+       }
+
+       s=0;
+       s2=0;
+       a=comedi_to_phys(data[0],sv->rng,sv->maxdata);
+       for(i=0;i<n;i++){
+               x=comedi_to_phys(data[i],sv->rng,sv->maxdata);
+               s+=x-a;
+               s2+=(x-a)*(x-a);
+       }
+       sv->average=a+s/n;
+       sv->stddev=sqrt(n*s2-s*s)/n;
+       sv->error=sv->stddev/sqrt(n);
+
+       ret=n;
+
+out:
+       free(data);
+
+       return ret;
+}
+
+int new_sv_measure_order(new_sv_t *sv,int order)
+{
+       sampl_t *data;
+       int n,i,ret;
+       double a,x,s,s2;
+
+       n=1<<order;
+
+       data=malloc(sizeof(sampl_t)*n);
+
+       for(i=0;i<n;){
+               sv->t.data=data+i;
+               sv->t.n=n-i;
+               ret=comedi_trigger(dev,&sv->t);
+               if(ret<0)goto out;
+               i+=ret;
+       }
+
+       s=0;
+       s2=0;
+       a=comedi_to_phys(data[0],sv->rng,sv->maxdata);
+       for(i=0;i<n;i++){
+               x=comedi_to_phys(data[i],sv->rng,sv->maxdata);
+               s+=x-a;
+               s2+=(x-a)*(x-a);
+       }
+       sv->average=a+s/n;
+       sv->stddev=sqrt(n*s2-s*s)/n;
+       sv->error=sv->stddev/sqrt(n);
+
+       ret=n;
+
+out:
+       free(data);
+
+       return ret;
+}
+
+
+
+/* linear fitting */
+
+int calculate_residuals(linear_fit_t *l);
+
+int linear_fit_monotonic(linear_fit_t *l)
+{
+       double x,y;
+       double sxp;
+       int i;
+
+       l->s1=0;
+       l->sx=0;
+       l->sy=0;
+       l->sxy=0;
+       l->sxx=0;
+       for(i=0;i<l->n;i++){
+               x=l->x0+i*l->dx;
+               y=l->y_data[i];
+
+               if(isnan(y))continue;
+
+               l->s1+=1;
+               l->sx+=x;
+               l->sy+=y;
+               l->sxy+=x*y;
+               l->sxx+=x*x;
+       }
+       sxp=l->sxx-l->sx*l->sx/l->s1;
+       
+       l->ave_x=l->sx/l->s1;
+       l->ave_y=l->sy/l->s1;
+       l->slope=(l->s1*l->sxy-l->sx*l->sy)/(l->s1*l->sxx-l->sx*l->sx);
+       l->err_slope=l->yerr/sqrt(sxp);
+       l->err_ave_y=l->yerr/sqrt(l->s1);
+
+       calculate_residuals(l);
+
+       return 0;
+}
+
+int calculate_residuals(linear_fit_t *l)
+{
+       double x,y;
+       double res,sum_res2;
+       int i;
+
+       sum_res2=0;
+       for(i=0;i<l->n;i++){
+               x=l->x0+i*l->dx-l->ave_x;
+               y=l->y_data[i];
+
+               if(isnan(y))continue;
+
+               res=l->ave_y+l->slope*x-y;
+
+               sum_res2+=res*res;
+       }
+       l->S_min=sum_res2/(l->yerr*l->yerr);
+       l->dof=l->s1-2;
+
+       return 0;
+}
+
+double linear_fit_func_y(linear_fit_t *l,double x)
+{
+       return l->ave_y+l->slope*(x-l->ave_x);
+}
+
diff --git a/demo/Makefile b/demo/Makefile
new file mode 100644 (file)
index 0000000..313500e
--- /dev/null
@@ -0,0 +1,20 @@
+
+
+
+CFLAGS +=-I ../include -I . -O2
+LDFLAGS=-L../lib/ -lcomedi
+
+
+BINS=mode4 mode3 mode2 ao_waveform tut2 cmd tut1
+MBINS=inp inpn sv eeprom_dump info outp
+
+all: $(patsubst %,_mbins_%,$(MBINS)) $(patsubst %,_bins_%,$(BINS))
+
+$(patsubst %,_mbins_%,$(MBINS)) : main.o $(patsubst %,%.o,$(MBINS))
+       $(CC) -o $(patsubst _mbins_%,%,$@) main.o $(patsubst _mbins_%,%.o,$@) $(LDFLAGS)
+
+$(patsubst %,_bins_%,$(BINS)) : $(patsubst %,%.o,$(BINS))
+       $(CC) -o $(patsubst _bins_%,%,$@) $(patsubst _bins_%,%.o,$@) $(LDFLAGS)
+
+clean:
+       -rm *.o $(BINS) $(MBINS)
diff --git a/demo/README b/demo/README
new file mode 100644 (file)
index 0000000..334a6fb
--- /dev/null
@@ -0,0 +1,68 @@
+
+Examples
+
+
+ao_waveform:
+
+       You need a device (and driver) capable of streaming analog output,
+       which currently is some of the members of the NI AT-MIO and PCI-MIO
+       E series.  Creates a sine wave on an analog output channel.
+
+cmd:
+       Example of how to use the new 0.8 command structure.  Not functional.
+
+eeprom_dump:
+       Dumps the EEPROM of a card, if it has one.  Useful for debugging
+       devices/drivers.
+
+info:
+       Displays some information that Comedi knows about a device.
+
+inp:
+       Simple input demo.  Reads one sample from an input line.
+
+inpn:
+       Slightly more complicated input demo.  (It has a for() loop.)
+       Reads each channel on a subdevice, at every possible input
+       range, and converts the data to a voltage.
+
+mode1:
+       Doesn't work.
+
+mode2:
+       Demo for streaming analog input.  Only works for boards/drivers
+       with that capability.
+
+mode3:
+       Doesn't work.
+
+mode4:
+       Same as mode2, except using an external trigger.
+
+sv:
+       Similar to inp, but measures the input using the comedi_sv_*()
+       functions, which average many samples to try to get a more accurate
+       estimate of the actual input.
+
+tut1:
+tut2:
+       Tutorial examples.
+
+
+Many of these demos are linked with the file main.c, which parses
+command line options.  Some options don't make sense with all programs.
+The options are:
+
+
+-a <aref>      uses analog reference <aref> (default: 0)
+-c <chan>      uses channel <chan>
+-s <subd>      uses subdevice <subd>
+-r <range>     uses voltage range <range>
+-f <file>      uses device file <file> (default: /dev/comedi0)
+-v             verbose
+-d             set analog reference to differential
+-g             set analog reference to ground
+-o             set analog reference to other
+-m             set analog reference to common
+
+
diff --git a/demo/ao_waveform.c b/demo/ao_waveform.c
new file mode 100644 (file)
index 0000000..09c8485
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+   This demo uses an analog output subdevice in timed
+   mode (mode 2) to generate a waveform.  The waveform
+   in this example is a sine wave (surprise!), but this
+   can be easily changed to make a general function
+   generator.
+
+   The function generation algorithm is the same as
+   what is typically used in digital function generators.
+   A 32-bit accumulator is incremented by a phase factor,
+   which is the amount (in radians) that the generator
+   advances each time step.  The accumulator is then
+   shifted right by 20 bits, to get a 12 bit offset into
+   a lookup table.  The value in the lookup table at
+   that offset is then put into a buffer for output to
+   the DAC.
+
+   [ Actually, the accumulator is only 26 bits, for some
+   reason.  I'll fix this sometime. ]
+
+   On the comedi side of things, the setup for mode 2
+   is similar to analog input, except for the TRIG_WRITE
+   flag.  Once you have issued the command, comedi then
+   expects you to keep the buffer full of data to output
+   to the DAC.  This is done by write().  Since there
+   may be a delay between the ioctl() and a subsequent
+   write(), you should fill the buffer using write() before
+   you call ioctl(), as is done here.
+
+   Also NOTE!  The lseek() to offset 1 is used to tell
+   comedi that you want to write to subdevice 1.  This
+   is not needed for analog input, since AI is usually on
+   subdevice 0.
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <math.h>
+
+#define dds_init dds_init_pseudocycloid
+
+
+/* frequency of the sine wave to output */
+double waveform_frequency      = 100.0;
+
+/* update rate for the DAC, typically much higher than
+   the frequency of the sine wave. */
+double update_frequency                = 200000.0;
+
+/* peak-to-peak amplitude, in DAC units (i.e., 0-4095) */
+double amplitude               = 4000;
+
+/* offset, in DAC units */
+double offset                  = 2048;
+
+/* This is the size of chunks we deal with when creating and
+   outputting data.  This *could* be 1, but that would be
+   inefficient */
+#define BUF_LEN                4096
+
+
+#define N_SCANS                0
+#define N_CHANS                1
+
+int subdevice;
+int channels[] = { 0 };
+int range = 0;
+int aref = AREF_GROUND;
+int external_trigger_number = 0;
+
+sampl_t data[BUF_LEN];
+
+void dds_output(sampl_t *buf,int n);
+void dds_init_sine(void);
+void dds_init_pseudocycloid(void);
+
+int main(int argc, char *argv[])
+{
+       char *fn = NULL;
+       comedi_trig it;
+       int err;
+       int n,m,i;
+       int total=0;
+       comedi_t *dev;
+       double actual_freq;
+       unsigned int chan[N_CHANS];
+
+       if(argc>=2){
+               waveform_frequency=atof(argv[1]);
+       }
+
+       fn = "/dev/comedi0";
+
+       dev = comedi_open(fn);
+
+       subdevice = comedi_find_subdevice_by_type(dev,COMEDI_SUBD_AO,0);
+
+       it.subdev = subdevice;
+       it.mode = 2;
+       it.flags = TRIG_WRITE;
+       it.n_chan = N_CHANS;
+       it.chanlist = chan;
+       it.data = NULL;
+       it.n = N_SCANS;
+       it.trigsrc = 0;
+
+       /* convert the frequency into a timer value */
+       comedi_get_timer(dev,subdevice,update_frequency,&it.trigvar,&actual_freq);
+       fprintf(stderr,"primary actual frequency=%g timer value=%d\n",actual_freq,it.trigvar);
+
+       /* pack the channel list */
+       for(i=0;i<N_CHANS;i++){
+               chan[i] = CR_PACK(channels[i], range, aref);
+       }
+
+       dds_init();
+
+       dds_output(data,BUF_LEN);
+       dds_output(data,BUF_LEN);
+
+       lseek(comedi_fileno(dev),subdevice,SEEK_SET);
+       m=write(comedi_fileno(dev),data,BUF_LEN*sizeof(sampl_t));
+       perror("write");
+       printf("m=%d\n",m);
+
+
+       if ((err = comedi_trigger(dev, &it)) < 0) {
+               perror("ioctl");
+               exit(1);
+       }
+       while(1){
+               dds_output(data,BUF_LEN);
+               n=BUF_LEN*sizeof(sampl_t);
+               while(n>0){
+                       m=write(comedi_fileno(dev),(void *)data+(BUF_LEN*sizeof(sampl_t)-n),n);
+                       if(m<0){
+                               perror("write");
+                               exit(0);
+                       }
+                       //printf("m=%d\n",m);
+                       n-=m;
+               }
+               total+=BUF_LEN;
+               //printf("%d\n",total);
+       }
+
+       return 0;
+}
+
+
+
+#define WAVEFORM_SHIFT 16
+#define WAVEFORM_LEN (1<<WAVEFORM_SHIFT)
+#define WAVEFORM_MASK (WAVEFORM_LEN-1)
+
+
+sampl_t waveform[WAVEFORM_LEN];
+
+unsigned int acc;
+unsigned int adder;
+
+void dds_init(void)
+{
+       int i;
+
+       adder=waveform_frequency/update_frequency*(1<<16)*(1<<WAVEFORM_SHIFT);
+
+       dds_init_sine();
+
+       /* this is due to a bug in the NI-E driver */
+       if(range){
+               for(i=0;i<WAVEFORM_LEN;i++){
+                       waveform[i]^=0x800;
+               }
+       }
+}
+
+void dds_init_sine(void)
+{
+       int i;
+
+       for(i=0;i<WAVEFORM_LEN;i++){
+               waveform[i]=rint(offset+0.5*amplitude*cos(i*2*M_PI/WAVEFORM_LEN));
+       }
+}
+
+/* Yes, I know this is not the proper equation for a
+   cycloid.  Fix it. */
+void dds_init_cycloid(void)
+{
+       int i;
+       double t;
+
+       for(i=0;i<WAVEFORM_LEN/2;i++){
+               t=2*((double)i)/WAVEFORM_LEN;
+               waveform[i]=rint(offset+amplitude*sqrt(1-4*t*t));
+       }
+       for(i=WAVEFORM_LEN/2;i<WAVEFORM_LEN;i++){
+               t=2*(1-((double)i)/WAVEFORM_LEN);
+               waveform[i]=rint(offset+amplitude*sqrt(1-t*t));
+       }
+}
+
+void dds_init_sawtooth(void)
+{
+       int i;
+
+       for(i=0;i<WAVEFORM_LEN;i++){
+               waveform[i]=rint(offset+amplitude*((double)i)/WAVEFORM_LEN);
+       }
+}
+
+void dds_output(sampl_t *buf,int n)
+{
+       int i;
+       sampl_t *p=buf;
+
+       for(i=0;i<n;i++){
+               *p=waveform[(acc>>16)&WAVEFORM_MASK];
+               p++;
+               acc+=adder;
+       }
+}
+
diff --git a/demo/cmd.c b/demo/cmd.c
new file mode 100644 (file)
index 0000000..eb42535
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+   A little input demo for commands
+
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#define N_SCANS                10
+#define N_CHANS                16
+
+int subdevice = 0;
+int chan=0;
+int range = 0;
+int aref = AREF_GROUND;
+double freq = 1000;
+
+#define BUFSZ 100
+char buf[BUFSZ];
+
+
+static void do_cmd(comedi_t *dev);
+
+int main(int argc, char *argv[])
+{
+       char *fn = NULL;
+       comedi_t *dev;
+
+       fn = "/dev/comedi0";
+
+       dev = comedi_open(fn);
+
+       do_cmd(dev);
+
+       return 0;
+}
+
+static void do_cmd(comedi_t *dev)
+{
+       comedi_cmd cmd;
+       unsigned int chanlist[4];
+       int ret;
+
+       /* the subdevice that the command is sent to */
+       cmd.subdev =    subdevice;
+
+       /* flags */
+       cmd.flags =     0;
+
+       /* each event requires a trigger, which is specified
+          by a source and an argument.  For example, to specify
+          an external digital line 3 as a source, you would use
+          src=TRIG_EXT and arg=3. */
+
+       cmd.start_src =         TRIG_NOW;
+       cmd.start_arg =         0;
+
+       cmd.scan_begin_src =    TRIG_TIMER;
+       cmd.scan_begin_arg =    1000000;        /* in ns */
+
+       cmd.convert_src =       TRIG_TIMER;
+       cmd.convert_arg =       100000;         /* in ns */
+
+       cmd.scan_end_src =      TRIG_COUNT;
+       cmd.scan_end_arg =      4;              /* number of channels */
+
+       cmd.stop_src =          TRIG_COUNT;
+       cmd.stop_arg =          1000;
+
+       /* the channel list determined which channels are sampled.
+          In general, chanlist_len is the same as scan_end_arg.  Most
+          boards require this.  */
+       cmd.chanlist =          chanlist;
+       cmd.chanlist_len =      4;
+
+       chanlist[0]=CR_PACK(0,range,aref);
+       chanlist[1]=CR_PACK(1,range,aref);
+       chanlist[2]=CR_PACK(2,range,aref);
+       chanlist[3]=CR_PACK(3,range,aref);
+
+       /* data and data_len are not used for user-space
+          programs */
+       cmd.data = NULL;
+       cmd.data_len = 0;
+
+       ret=ioctl(comedi_fileno(dev),COMEDI_CMD,&cmd);
+
+       printf("ioctl returned %d\n",ret);
+
+       do{
+               ret=read(comedi_fileno(dev),buf,BUFSZ);
+               printf("read %d\n",ret);
+       }while(ret>=0);
+       printf("errno=%d\n",errno);
+}
+
diff --git a/demo/eeprom_dump.c b/demo/eeprom_dump.c
new file mode 100644 (file)
index 0000000..570be98
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+   Dumps eeproms
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+extern int verbose_flag;
+extern int subdevice;
+extern int range;
+extern int channel;
+extern int aref;
+extern char *filename;
+
+comedi_t *device;
+
+int read_eeprom(comedi_t *it,unsigned int **eeprom);
+void dump_eeprom(unsigned int *eeprom,int len);
+
+
+int main(int argc, char *argv[])
+{
+       lsampl_t data;
+       int ret;
+       int len;
+       unsigned int *eeprom;
+
+       parse_options(argc,argv);
+
+       device=comedi_open(filename);
+       if(!device){
+               comedi_perror(filename);
+               exit(0);
+       }
+
+       len=read_eeprom(device,&eeprom);
+       dump_eeprom(eeprom,len);
+
+       return 0;
+}
+
+
+
+
+int read_eeprom(comedi_t *it,unsigned int **eeprom)
+{
+       unsigned int subd;
+       unsigned int n,i,ret;
+       lsampl_t data;
+       unsigned int *ptr;
+       lsampl_t maxdata;
+
+       subd=comedi_find_subdevice_by_type(it,COMEDI_SUBD_MEMORY,0);
+       if(subd<0){
+               fprintf(stderr,"No memory subdevice\n");
+               return 0;
+       }
+
+       n=comedi_get_n_channels(it,subd);
+       maxdata=comedi_get_maxdata(it,subd,0);
+
+       if(maxdata!=0xff){
+               fprintf(stderr,"Memory subdevice has strange maxdata, aborting\n");
+       }
+
+       ptr=malloc(sizeof(unsigned int)*n);
+
+       for(i=0;i<n;i++){
+               ret=comedi_data_read(it,subd,i,0,0,&data);
+               ptr[i]=data;
+               if(ret<0){
+                       comedi_perror("comedi_data_read");
+                       return 0;
+               }
+       }
+
+       *eeprom=ptr;
+       return n;
+
+}
+
+void dump_eeprom(unsigned int *eeprom,int len)
+{
+       int i, j, c;
+
+       for (i = 0; i < len - 16; i+=16) {
+               printf("%04x: ",i);
+               for (j = 0; j < 16; j++) {
+                       printf("%02x", eeprom[i + j] & 0xff);
+               }
+               printf("  ");
+               for (j = 0; j < 16; j++) {
+                       c = eeprom[i + j] & 0xff;
+                       printf("%c", isprint(c) ? c : '.');
+               }
+               printf("\n");
+       }
+       if(i==len)return;
+       printf("%04x: ",i);
+       for (j = 0; j < len-i; j++) {
+               printf("%02x", eeprom[i + j] & 0xff);
+       }
+       for(;j<16;j++){
+               printf("  ");
+       }
+       printf("  ");
+       for (j = 0; j < len-i; j++) {
+               c = eeprom[i + j] & 0xff;
+               printf("%c", isprint(c) ? c : '.');
+       }
+       for(;j<16;j++){
+               printf(" ");
+       }
+       printf("\n");
+}
+
diff --git a/demo/info.c b/demo/info.c
new file mode 100644 (file)
index 0000000..0040d7d
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+   This demo reads information about a comedi device and
+   displays the information in a human-readable form.
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+void help(void)
+{
+       fprintf(stderr,"info </dev/comediN>\n");
+       exit(0);
+}
+
+char *tobinary(char *s,int bits,int n);
+
+char *subdevice_types[]={
+       "unused",
+       "analog input",
+       "analog output",
+       "digital input",
+       "digital output",
+       "digital I/O",
+       "counter",
+       "timer",
+       "memory",
+       "calibration",
+       "processor"
+};
+
+comedi_t *it;
+extern char *filename;
+
+
+int main(int argc,char *argv[])
+{
+       int i;
+       int n_subdevices,type;
+       
+       parse_options(argc,argv);
+
+       it=comedi_open(filename);
+       if(!it){
+               fprintf(stderr,"cannot open %s\n",filename);
+               exit(0);
+       }
+
+       printf("overall info:\n");
+       printf("  version code: 0x%06x\n",comedi_get_version_code(it));
+       printf("  driver name: %s\n",comedi_get_driver_name(it));
+       printf("  board name: %s\n",comedi_get_board_name(it));
+       printf("  number of subdevices: %d\n",n_subdevices=comedi_get_n_subdevices(it));
+       
+       for(i=0;i<n_subdevices;i++){
+               printf("subdevice %d:\n",i);
+               type=comedi_get_subdevice_type(it,i);
+               printf("  type: %d (%s)\n",type,subdevice_types[type]);
+               printf("  number of channels: %d\n",comedi_get_n_channels(it,i));
+               printf("  max data value: %d\n",comedi_get_maxdata(it,i,0));
+       }
+       
+       return 0;
+}
+
+char *tobinary(char *s,int bits,int n)
+{
+       int bit=1<<n;
+       char *t=s;
+       
+       for(;bit;bit>>=1)
+               *t++=(bits&bit)?'1':'0';
+       *t=0;
+       
+       return s;
+}
+
diff --git a/demo/inp.c b/demo/inp.c
new file mode 100644 (file)
index 0000000..a3e810d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+   A little input demo
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+extern int verbose_flag;
+extern int subdevice;
+extern int range;
+extern int channel;
+extern int aref;
+extern char *filename;
+
+comedi_t *device;
+
+
+int main(int argc, char *argv[])
+{
+       lsampl_t data;
+       int ret;
+
+       parse_options(argc,argv);
+
+       device=comedi_open(filename);
+       if(!device){
+               comedi_perror(filename);
+               exit(0);
+       }
+
+       if(verbose_flag){
+               printf("measuring device=%s subdevice=%d channel=%d range=%d analog reference=%d\n",
+                       filename,subdevice,channel,range,aref);
+       }
+
+       ret=comedi_data_read(device,subdevice,channel,range,aref,&data);
+       if(ret<0){
+               comedi_perror(filename);
+               exit(0);
+       }
+
+       printf("%d\n",data);
+
+       return 0;
+}
+
diff --git a/demo/inpn.c b/demo/inpn.c
new file mode 100644 (file)
index 0000000..4268121
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+   This demo opens /dev/comedi0 and looks for an analog input
+   subdevice.  If it finds one, it measures one sample on each
+   channel for each input range.  The value NaN indicates that
+   the measurement was out of range.
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+extern int verbose_flag;
+extern int subdevice;
+extern int range;
+extern int channel;
+extern int aref;
+extern char *filename;
+
+comedi_t *device;
+
+
+int main(int argc, char *argv[])
+{
+       int n_subdevs;
+       int n_chans,chan;
+       int n_ranges;
+       int range;
+       int rangetype;
+       int maxdata;
+       lsampl_t data;
+       double voltage;
+
+       parse_options(argc,argv);
+
+       device=comedi_open(filename);
+       if(!device){
+               comedi_perror(filename);
+               exit(0);
+       }
+
+       subdevice=comedi_find_subdevice_by_type(device,COMEDI_SUBD_AI,0);
+       if(subdevice<0){
+               printf("no analog input subdevice found\n");
+               exit(0);
+       }
+
+       n_chans=comedi_get_n_channels(device,subdevice);
+       for(chan=0;chan<n_chans;chan++){
+               printf("%d: ",chan);
+
+               //n_ranges=comedi_get_n_ranges(device,subdevice,chan);
+               rangetype=comedi_get_rangetype(device,subdevice,chan);
+               n_ranges=RANGE_LENGTH(rangetype);
+
+               maxdata=comedi_get_maxdata(device,subdevice,chan);
+               for(range=0;range<n_ranges;range++){
+                       comedi_data_read(device,subdevice,chan,range,aref,&data);
+                       voltage=comedi_to_phys(data,comedi_get_range(device,subdevice,chan,range),maxdata);
+                       printf("%g ",voltage);
+               }
+               printf("\n");
+       }
+       
+       exit(0);
+}
+
diff --git a/demo/main.c b/demo/main.c
new file mode 100644 (file)
index 0000000..0e67cac
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <malloc.h>
+
+
+char *filename="/dev/comedi0";
+int verbose_flag;
+comedi_t *device;
+
+int value;
+int subdevice;
+int channel;
+int aref;
+int range;
+
+
+int parse_options(int argc, char *argv[])
+{
+       int c;
+
+
+       while (optind<argc) {
+               c = getopt(argc, argv, "acsrfvdgom");
+               switch (c) {
+               case -1:
+                       /* data value */
+                       sscanf(argv[optind],"%d",&value);
+                       optind++;
+                       break;
+               case 'f':
+                       filename = argv[optind];
+                       optind++;
+                       break;
+               case 's':
+                       sscanf(argv[optind],"%d",&subdevice);
+                       optind++;
+                       break;
+               case 'c':
+                       sscanf(argv[optind],"%d",&channel);
+                       optind++;
+                       break;
+               case 'a':
+                       sscanf(argv[optind],"%d",&aref);
+                       optind++;
+                       break;
+               case 'r':
+                       sscanf(argv[optind],"%d",&range);
+                       optind++;
+                       break;
+               case 'v':
+                       verbose_flag = 1;
+                       break;
+               case 'd':
+                       aref=AREF_DIFF;
+                       break;
+               case 'g':
+                       aref=AREF_GROUND;
+                       break;
+               case 'o':
+                       aref=AREF_OTHER;
+                       break;
+               case 'm':
+                       aref=AREF_COMMON;
+                       break;
+               default:
+                       printf("bad option\n");
+                       exit(1);
+               }
+       }
+
+       return argc;
+}
+
+
+
diff --git a/demo/mode1.c b/demo/mode1.c
new file mode 100644 (file)
index 0000000..9f77244
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+       A little input demo for mode 1
+
+       Mode 1 uses a timer to acquire samples at regular intervals.
+       It scans through the channel list, and then repeats.
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#define N_SAMPLES      100000
+#define N_CHANS                4
+
+int subdevice = 0;
+int channels[N_CHANS] = { 0, 1, 2, 3 };
+double freq = 100000;
+int range = 0;
+int aref = 3;
+int external_trigger_number = 0;
+
+sampl_t data[N_SAMPLES];
+
+
+int main(int argc, char *argv[])
+{
+       char *fn = NULL;
+       comedi_trig it;
+       int err;
+       int n,i;
+       comedi_t *dev;
+       unsigned int chan[N_CHANS];
+       double actual_freq;
+       void *data_ptr;
+       int n_left;
+
+       fn = "/dev/comedi0";
+
+       dev = comedi_open(fn);
+
+       it.subdev = 0;
+       it.mode = 1;
+       it.flags = 0;
+       it.n_chan = N_CHANS;
+       it.chanlist = chan;
+       it.data = data;
+       it.n = N_SAMPLES/N_CHANS;
+       it.trigsrc = 0;
+
+       /* convert the frequency into a timer value */
+       comedi_get_timer(dev,subdevice,freq,&it.trigvar,&actual_freq);
+       printf("primary actual frequency=%g timer value=%d\n",actual_freq,it.trigvar);
+
+       /* pack the channel list */
+       for(i=0;i<N_CHANS;i++){
+               chan[i] = CR_PACK(channels[i], range, aref);
+       }
+
+       if ((err = comedi_trigger(dev, &it)) < 0) {
+               perror("ioctl");
+               exit(1);
+       }
+
+       data_ptr=data;
+       n_left=N_SAMPLES*sizeof(sampl_t);
+       while(n_left>0){
+               if((n=read(comedi_fileno(dev),data_ptr,n_left))<0){
+                       perror("read");
+                       exit(1);
+               }
+               printf("read %d\n",n);
+               n_left-=n;
+               data_ptr+=n;
+       }
+       printf("number of samples read=%d\ndata[0]=%d\ndata[N-1]=%d\n",
+               n/sizeof(sampl_t),data[0],data[N_SAMPLES-1]);
+
+       for(i=0;i<N_SAMPLES;i++){
+               fprintf(stderr,"%d\n",data[i]);
+       }
+
+       return 0;
+}
+
diff --git a/demo/mode2.c b/demo/mode2.c
new file mode 100644 (file)
index 0000000..45013e3
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+   A little input demo for mode 2
+
+   Mode 2 uses two different timers to convert samples.
+   The primary timer determines the time between scans,
+   and the secondary timer determines the time between
+   samples in a scan.
+
+   The time between scans is in trigval; the time
+   between samples is selected by trigval1.  Conversion
+   from seconds or Hz is done using the standard timer
+   routines.
+
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#define N_SCANS                10
+#define N_CHANS                16
+
+int subdevice = 0;
+int chan=0;
+int range = 0;
+int aref = AREF_GROUND;
+double freq = 1000;
+
+#define N_SAMPLES      1000
+
+double data[N_SAMPLES];
+
+
+int main(int argc, char *argv[])
+{
+       char *fn = NULL;
+       int i;
+       comedi_t *dev;
+
+       fn = "/dev/comedi0";
+
+       dev = comedi_open(fn);
+
+#if 0
+       for(i=0;i<10;i++){
+               range=comedi_find_range(dev,subdevice,chan,0,-i,i);
+               printf("%d\n",range);
+       }
+#endif
+       comedi_timed_1chan(dev,subdevice,chan,range,aref,freq,N_SAMPLES,data);
+
+       for(i=0;i<N_SAMPLES;i++){
+               printf("%g\n",data[i]);
+       }
+
+       return 0;
+}
+
diff --git a/demo/mode3.c b/demo/mode3.c
new file mode 100644 (file)
index 0000000..d0745d6
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+   A little input demo for mode 3
+
+   Mode 3 uses an external trigger to repeatedly trigger
+   acquisition of samples.  If you select n_chan>1, then
+   a different channel is captured at each external trigger.
+
+   If you have multiple external trigger lines, the
+   particular trigger line is selected by trigval.
+
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#define N_SAMPLES      100
+#define N_CHANS                4
+
+int subdevice = 0;
+int channels[N_CHANS] = { 0, 1, 2, 3 };
+double freq = 100000;
+int range = 0;
+int aref = 3;
+int external_trigger_number = 0;
+
+sampl_t data[N_SAMPLES];
+
+
+int main(int argc, char *argv[])
+{
+       char *fn = NULL;
+       comedi_trig it;
+       int err;
+       int n,i;
+       comedi_t *dev;
+       unsigned int chan[N_CHANS];
+
+       fn = "/dev/comedi0";
+
+       dev = comedi_open(fn);
+
+       it.subdev = 0;
+       it.mode = 3;
+       it.flags = 0;
+       it.n_chan = N_CHANS;
+       it.chanlist = chan;
+       it.data = data;
+       it.n = N_SAMPLES/N_CHANS;
+       it.trigsrc = 0;
+
+       /* external trigger number */
+       it.trigvar = external_trigger_number;
+
+       /* pack the channel list */
+       for(i=0;i<N_CHANS;i++){
+               chan[i] = CR_PACK(channels[i], range, aref);
+       }
+
+       if ((err = comedi_trigger(dev, &it)) < 0) {
+               perror("ioctl");
+               exit(1);
+       }
+
+       if((n=read(comedi_fileno(dev),data,N_SAMPLES*sizeof(sampl_t)))<0){
+               perror("read");
+               exit(1);
+       }
+       printf("number of samples read=%d\ndata[0]=%d\ndata[N-1]=%d\n",
+               n/sizeof(sampl_t),data[0],data[N_SAMPLES-1]);
+
+       return 0;
+}
+
diff --git a/demo/mode4.c b/demo/mode4.c
new file mode 100644 (file)
index 0000000..fcd0414
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+   A little input demo for mode 4
+
+   Mode 4 uses an external trigger to repeatedly trigger a
+   scan of samples.  (This is different from mode 3, which
+   triggers an individual sample.)  Thus, for each external
+   trigger, n_chan samples are converted.
+
+   If you have multiple external trigger lines, the
+   particular trigger line is selected by trigval.
+
+   The time between samples in a scan is selected
+   by trigval1.  Conversion from seconds or Hz is done
+   using the standard timer routines.
+
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#define N_SCANS                4
+#define N_CHANS                4
+
+int subdevice = 0;
+int channels[N_CHANS] = { 0, 1, 2, 3 };
+double freq = 1000;
+int range = 0;
+int aref = 3;
+int external_trigger_number = 0;
+
+#define N_SAMPLES      (N_CHANS*N_SCANS)
+
+sampl_t data[N_SAMPLES];
+
+
+int main(int argc, char *argv[])
+{
+       char *fn = NULL;
+       comedi_trig it;
+       int err;
+       int n,i;
+       comedi_t *dev;
+       double actual_freq;
+       unsigned int chan[N_CHANS];
+
+       fn = "/dev/comedi0";
+
+       dev = comedi_open(fn);
+
+       it.subdev = 0;
+       it.mode = 4;
+       it.flags = 0;
+       it.n_chan = N_CHANS;
+       it.chanlist = chan;
+       it.data = data;
+       it.n = N_SCANS;
+       it.trigsrc = 0;
+
+       /* external trigger number */
+       it.trigvar = external_trigger_number;
+
+       /* convert the frequency into a timer value */
+       comedi_get_timer(dev,subdevice,freq,&it.trigvar1,&actual_freq);
+       printf("actual frequency=%g timer value=%d\n",actual_freq,it.trigvar1);
+
+       /* pack the channel list */
+       for(i=0;i<N_CHANS;i++){
+               chan[i] = CR_PACK(channels[i], range, aref);
+       }
+
+       if ((err = comedi_trigger(dev, &it)) < 0) {
+               perror("ioctl");
+               exit(1);
+       }
+
+       if((n=read(comedi_fileno(dev),data,N_SAMPLES*sizeof(sampl_t)))<0){
+               perror("read");
+               exit(1);
+       }
+       printf("number of samples read=%d\ndata[0]=%d\ndata[N-1]=%d\n",
+               n/sizeof(sampl_t),data[0],data[N_SAMPLES-1]);
+
+       return 0;
+}
+
diff --git a/demo/outp.c b/demo/outp.c
new file mode 100644 (file)
index 0000000..ca7a5f0
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+   A little output demo
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+extern int verbose_flag;
+extern int subdevice;
+extern int range;
+extern int channel;
+extern int aref;
+extern int value;
+extern char *filename;
+
+comedi_t *device;
+
+
+int main(int argc, char *argv[])
+{
+       lsampl_t data;
+       int ret;
+
+       parse_options(argc,argv);
+
+       device=comedi_open(filename);
+       if(!device){
+               comedi_perror(filename);
+               exit(0);
+       }
+
+       data = value; 
+       if(verbose_flag){
+               printf("writing %d to device=%s subdevice=%d channel=%d range=%d analog reference=%d\n",
+                       data,filename,subdevice,channel,range,aref);
+       }
+
+       ret=comedi_data_write(device,subdevice,channel,range,aref,data);
+       if(ret<0){
+               comedi_perror(filename);
+               exit(0);
+       }
+
+       printf("%d\n",data);
+
+       return 0;
+}
+
diff --git a/demo/rt/Makefile b/demo/rt/Makefile
new file mode 100644 (file)
index 0000000..2cf8771
--- /dev/null
@@ -0,0 +1,18 @@
+
+
+# change this to your rtlinux include directory.
+# I don't have RTLinux installed on the computer
+# that I use to compile, so I need this:
+
+RTINCDIR = -I/b/ds/stuff/rtlinux/r-9J/rtl/include
+
+CFLAGS = -I../../include $(RTINCDIR) -O2 -Wall -D__RT__ -D__KERNEL__ -DMODULE
+
+all: it.o ai.o
+
+it.o:  it.c
+       $(CC) $(CFLAGS) -o it.o -c it.c
+
+ai.o:  ai.c
+       $(CC) $(CFLAGS) -o ai.o -c ai.c
+
diff --git a/demo/rt/README b/demo/rt/README
new file mode 100644 (file)
index 0000000..c6e39f3
--- /dev/null
@@ -0,0 +1,16 @@
+
+I'm using Linux-2.0.36/RTLinux 9J
+
+
+it.o toggles a digital output bit, like the rectangle demo
+in the RTLinux source
+
+ai.o does a timed acquisition on an analog input subdevice,
+utilizing a callback function to copy the analog input value
+to the analog output channel, in effect, creating a "follower".
+
+Both demos are configured for an NI AT-MIO E series board.
+To use another driver/board, the source will need to be edited.
+
+
+
diff --git a/demo/rt/ai.c b/demo/rt/ai.c
new file mode 100644 (file)
index 0000000..a2ec179
--- /dev/null
@@ -0,0 +1,97 @@
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <rtl_sched.h>
+#include <asm/rt_time.h>
+#include <comedi.h>
+
+
+RT_TASK mytask;
+
+/* this is the dev,subdev for analog input on my atmio-E board */
+unsigned int ai_dev=0;
+unsigned int ai_subdev=0;
+unsigned int ai_chan[2]={CR_PACK(6,0,0),CR_PACK(1,0,0)};
+
+comedi_trig ai_trig;
+
+/* this is the dev,subdev for analog output on my atmio-E board */
+unsigned int ao_dev=0;
+unsigned int ao_subdev=1;
+unsigned int ao_chan=CR_PACK(0,0,0);
+
+comedi_trig ao_trig;
+
+sampl_t data2=0;
+
+sampl_t data[2];
+
+
+int callback(void *arg)
+{
+       data2^=0x800;
+       data[0]^=0x800;
+       data[0]&=0xfff;
+
+       comedi_trig_ioctl(ao_dev,ao_subdev,&ao_trig);
+
+       /* returning 1 is a little hack to reset user_ptr */
+       return 1;
+}
+
+int init_module(void)
+{
+       int ret;
+
+       /* set up input structure */
+       ai_trig.subdev=ai_subdev;
+       ai_trig.mode=2;
+       ai_trig.flags=0;
+       ai_trig.n_chan=2;
+       ai_trig.chanlist=ai_chan;
+       ai_trig.data=data;
+       ai_trig.n=2000;
+       ai_trig.trigsrc=0;
+       ai_trig.trigvar=99999;
+       ai_trig.trigvar1=1999;
+
+       /* IMPORTANT next step: lock the subdevice */
+       comedi_lock_ioctl(ai_dev,ai_subdev);
+
+       /* register our callback function */
+       ret=comedi_register_callback(ai_dev,ai_subdev,COMEDI_CB_EOS,callback,(void *)0);
+       printk("comedi_register_callback() returned %d\n",ret);
+
+       /* set up output structure */
+       ao_trig.subdev=ao_subdev;
+       ao_trig.mode=0;
+       ao_trig.flags=0;
+       ao_trig.n_chan=1;
+       ao_trig.chanlist=&ao_chan;
+       ao_trig.data=data;
+       ao_trig.n=1;
+       ao_trig.trigsrc=0;
+       ao_trig.trigvar=0;
+       ao_trig.trigvar1=0;
+
+       /* IMPORTANT next step: lock the subdevice */
+       comedi_lock_ioctl(ao_dev,ao_subdev);
+
+       /* start acq. */
+       ret=comedi_trig_ioctl(ai_dev,ai_subdev,&ai_trig);
+       printk("comedi_trig_ioctl() returned %d\n",ret);
+
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_cancel_ioctl(ai_dev,ai_subdev);
+
+       comedi_unlock_ioctl(ai_dev,ai_subdev);
+       comedi_unlock_ioctl(ao_dev,ao_subdev);
+}
+
+
diff --git a/demo/rt/it.c b/demo/rt/it.c
new file mode 100644 (file)
index 0000000..642598a
--- /dev/null
@@ -0,0 +1,78 @@
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <rtl_sched.h>
+#include <asm/rt_time.h>
+#include <comedi.h>
+
+
+RT_TASK mytask;
+
+/* this is the dev,subdev for digital I/O on my atmio-E board */
+unsigned int dev=0;
+unsigned int subdev=2;
+
+unsigned int channel;
+sampl_t data;
+comedi_trig trig;
+
+void do_comedi_toggle(int t)
+{
+       while(1){
+               data^=1;
+               comedi_trig_ioctl(dev,subdev,&trig);
+               rt_task_wait();
+       }
+}
+
+int init_module(void)
+{
+       int ret;
+       RTIME now=rt_get_time();
+
+       /* set up trigger structure */
+       trig.subdev=subdev;
+       trig.mode=0;
+       trig.flags=0;
+       trig.n_chan=1;
+       trig.chanlist=&channel;
+       trig.data=&data;
+       trig.n=1;
+       trig.trigsrc=0;
+       trig.trigvar=0;
+       trig.trigvar1=0;
+
+       channel=CR_PACK(0,0,0);
+
+       /* IMPORTANT next step: lock the subdevice */
+       comedi_lock_ioctl(dev,subdev);
+
+       /* configure DIO 0 for output */
+       trig.flags=TRIG_CONFIG;
+       data=COMEDI_OUTPUT;
+       ret=comedi_trig_ioctl(dev,subdev,&trig);
+       printk("comedi_trig_ioctl() returned %d\n",ret);
+       trig.flags=0;
+       data=1;
+
+       /* a little test */
+       ret=comedi_trig_ioctl(dev,subdev,&trig);
+       printk("comedi_trig_ioctl() returned %d\n",ret);
+
+       rt_task_init(&mytask,do_comedi_toggle, 0xffff, 3000, 4);
+       
+       rt_task_make_periodic(&mytask,now+3000,50000);
+
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       rt_task_delete(&mytask);
+
+       comedi_unlock_ioctl(dev,subdev);
+}
+
+
diff --git a/demo/sv.c b/demo/sv.c
new file mode 100644 (file)
index 0000000..1f1a9b8
--- /dev/null
+++ b/demo/sv.c
@@ -0,0 +1,60 @@
+/*
+   Demo of the comedi_sv_*() functions
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+extern int verbose_flag;
+extern int subdevice;
+extern int range;
+extern int channel;
+extern int aref;
+extern char *filename;
+
+comedi_t *device;
+
+
+int main(int argc, char *argv[])
+{
+       lsampl_t data;
+       int ret;
+       comedi_sv_t sv;
+       double volts;
+
+       parse_options(argc,argv);
+
+       device=comedi_open(filename);
+       if(!device){
+               comedi_perror(filename);
+               exit(0);
+       }
+
+       if(verbose_flag){
+               printf("measuring device=%s subdevice=%d channel=%d range=%d analog reference=%d\n",
+                       filename,subdevice,channel,range,aref);
+       }
+
+       comedi_sv_init(&sv,device,subdevice,channel);
+
+       sv.range=range;
+       sv.aref=aref;
+       sv.n=100;
+
+       ret=comedi_sv_measure(&sv,&volts);
+       if(ret<0){
+               comedi_perror("comedi_sv_measure()");
+               exit(0);
+       }
+
+       printf("%g\n",volts);
+
+       return 0;
+}
+
diff --git a/demo/tut1.c b/demo/tut1.c
new file mode 100644 (file)
index 0000000..15dcd6a
--- /dev/null
@@ -0,0 +1,24 @@
+
+#include <stdio.h>     /* for printf() */
+#include <comedilib.h>
+
+int subdev = 0;                /* change this to your input subdevice */
+int chan = 0;          /* change this to your channel */
+int range = 0;         /* more on this later */
+int aref = AREF_GROUND;        /* more on this later */
+
+int main(int argc,char *argv[])
+{
+       comedi_t *it;
+       int chan=0;
+       lsampl_t data;
+       
+       it=comedi_open("/dev/comedi0");
+       
+       comedi_data_read(it,subdev,chan,range,aref,&data);
+       
+       printf("%d\n",data);
+       
+       return 0;
+}
+
diff --git a/demo/tut2.c b/demo/tut2.c
new file mode 100644 (file)
index 0000000..abae04c
--- /dev/null
@@ -0,0 +1,35 @@
+
+#include <stdio.h>      /* for printf() */
+#include <comedi.h>     /* also included by comedilib.h */
+#include <comedilib.h>  /* for comedi_get() */
+
+int subdev = 0;         /* change this to your input subdevice */
+int chan = 0;           /* change this to your channel */
+int range = 3;          /* more on this later */
+int aref = 0;           /* more on this later */
+
+int main(int argc,char *argv[])
+{
+        comedi_t *cf;
+        int chan=0;
+        int data;
+        int maxdata,rangetype;
+        double volts;
+       comedi_range *cr;
+
+        cf=comedi_open("/dev/comedi0");
+
+        maxdata=comedi_get_maxdata(cf,subdev,chan);
+
+        rangetype=comedi_get_rangetype(cf,subdev,chan);
+
+       cr=comedi_get_range(cf,subdev,chan,range);
+
+        comedi_data_read(cf,subdev,chan,range,aref,&data);
+
+        volts=comedi_to_phys(data,cr,maxdata);
+
+        printf("%d %g\n",data,volts);
+
+        return 0;
+}
diff --git a/doc/FAQ b/doc/FAQ
new file mode 100644 (file)
index 0000000..611d686
--- /dev/null
+++ b/doc/FAQ
@@ -0,0 +1,36 @@
+
+1.  Should I use CONFIG_MODVERSIONS when compiling a Linux kernel?
+
+Short answer:  No.  Long answer: MODVERSIONS allows you to safely
+use a module on multiple kernel versions.  However, making comedi
+work with CONFIG_MODVERSIONS is a real pain.  Sometimes it works,
+sometimes it doesn't.
+
+
+2. I get the error message:
+
+Makefile:14: /usr/src/linux/.config: No such file or directory
+make: *** No rule to make target `/usr/src/linux/.config'.  Stop.
+
+
+It means you don't have the file /usr/src/linux/.config.  This file
+contains all the configuration information about your linux kernel,
+which comedi needs in order to compile correctly.  Some distributions
+don't contain this file, so you will need to create your own, by
+compiling your own kernel.  This file is automatically created
+when you run 'make config', the first step in compiling a kernel.
+
+
+3. When compiling programs that use libcomedi, I get error message:
+
+/usr/lib/libcomedi.a(range.o): In function `comedi_from_phys':
+range.o(.text+0x169): undefined reference to `floor'
+
+
+This error messsage indicates that the linker cannot resolve the
+reference to floor(), which is a function in the math library
+that is used in the function comedi_from_phys().  This only
+happens when you link statically.  Add '-lm' to your linking
+command.
+
+
diff --git a/doc/comedilib.sgml b/doc/comedilib.sgml
new file mode 100644 (file)
index 0000000..788f970
--- /dev/null
@@ -0,0 +1,928 @@
+<!doctype linuxdoc system>
+
+<article>
+<title>Comedi Documentation
+<author>David Schleef, <tt/ds@stm.lbl.gov/
+
+
+<sect>Introduction
+
+<p>
+This is preliminary documentation for Comedi and Comedilib.
+
+<sect>Installation and configuration
+<p>
+
+This section covers compiling, installing, and configuring
+comedi and comedlib.
+
+
+<sect1>Compiling and Installing
+<p>
+
+This section has not been written.
+<p>
+<sect1>Insmod'ding the kernel module
+<p>
+
+This section has not been written.
+
+<p>
+<sect1>Configuring comedi for your hardware
+<p>
+
+
+I assume that your hardware device is in your computer, and that
+you know the relevant details about it, i.e., what kind of card
+it is, the I/O base, the IRQ, jumper settings related to input
+ranges, etc.
+
+To tell the comedi kernel module that you have a particular device, and
+some information about it, you will be running the <tt>comedi_config</tt>
+command.  Perhaps you should read the man page now.
+
+In this tutorial, I will go through the process of configuring comedi
+for two devices, a National Instruments AT-MIO-16E-10
+and a Data Translation DT2821-F-8DI.
+
+The NI board is plug-and-play, and the man page tells me that I need
+to configure the PnP part of the board with isapnptools.  The isapnptools
+package is a little cryptic, but the concepts are simple.  Once I
+learned how to use it, I settled on a /etc/isapnp.conf file that
+contained the lines:
+
+
+<tscreen><verb>
+# ANSI string -->National Instruments, AT-MIO-16E-10<--
+(CONFIGURE NIC2400/10725401 (LD 0
+       (IO 0 (BASE 0x0260))
+       (INT 0 (IRQ 3 (MODE +E)))
+#      (DMA 0 (CHANNEL 5))
+#      (DMA 1 (CHANNEL 6))
+       (ACT Y)
+))
+</verb></tscreen>
+
+
+It also contains a few lines about overall configuration and about my
+sound card.  I found out after a bit of trial-and-error that the NI
+board does not always work with interrupts other than IRQ 3.  YMMV.
+Currently, the driver doesn't use DMA, but it may in the future, so
+I commented out the DMA lines.  It is a curious fact that the device
+ignores the IRQ and DMA information given here, however, I keep the
+information here to remind myself that the numbers aren't arbitrary.
+
+When I run comedi_config (as root, of course), I provide the same
+information.  Since I want to have the board configured every time
+I boot, I put the line
+
+<tscreen><verb>
+/usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,3
+</verb></tscreen>
+
+into <tt>/etc/rc.d/rc.local</tt>.  You can, of course, run this command at
+a command prompt.  The man page tells me that the option list
+is supposed to be "(I/O base),(IRQ)", so I used the same numbers
+as I put in /etc/isapnp.conf, i.e., 0x260,3.
+
+For the Data Translation board, I need to have a list of the
+jumper settings.  Fortunately, I wrote them all down in the
+manual -- I hope they are still correct.  However, I had to
+open the case to figure out which board in the series I had.
+It is a DT2821-f-8di.  The man page of comedi_config tells
+me that I need to know the I/O base, IRQ, DMA 1, DMA 2.  However,
+since I wrote the driver, I know that it also recognizes the
+differential/single-ended and unipolar/bipolar jumpers.  As always,
+the source is the final authority, and looking in module/dt282x.c
+tells me that the options list is interpreted as:
+
+<itemize>
+<item>I/O base
+<item>IRQ
+<item>1=differential, 0=single ended
+<item>ai 0=unipolar, 1=bipolar
+<item>ao0 0=unipolar, 1=bipolar
+<item>ao1 0=unipolar, 1=bipolar
+<item>dma1
+<item>dma2
+</itemize>
+
+(ai=analog input, ao=analog output.)  From this, I decide that
+the appropriate options list is
+
+<tscreen><verb>
+0x200,4,,1,1,1
+</verb></tscreen>
+
+I left the differential/single-ended number blank, since the
+driver already knowns (from the board name), that it is
+differential.  I  also left the DMA numbers blank, since I
+don't want the driver to use DMA.  (Don't want it to interfere
+with my sound card -- life is full of difficult choices.)
+Keep in mind that things commented in the source, but not in
+the documentation are about as likely to change as the weather,
+so I put good comments next to the following line when I put
+it in rc.local.
+
+<tscreen><verb>
+/usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1
+</verb></tscreen>
+
+So now I think that I have my boards configured correctly.
+Since data acquisition boards are not typically well-engineered,
+comedi sometimes can't figure out if the board is actually there.
+If it can't, it assumes you are right.  Both of these boards
+are well-made, so comedi will give me an error message if it
+can't find them.  The comedi kernel module, since it is a part
+of the kernel, prints messages to the kernel logs, which you
+can access through the command 'dmesg' or /var/log/messages.
+Here is a configuration failure (from dmesg):
+
+<tscreen><verb>
+comedi0: ni_E: 0x0200 can't find board
+</verb></tscreen>
+
+When it does work, I get:
+
+<tscreen><verb>
+comedi0: ni_E: 0x0260 at-mio-16e-10 ( irq = 3 )
+</verb></tscreen>
+
+Note that it also correctly identified my board.
+
+
+<p>
+<sect1>Getting information from comedi
+<p>
+
+
+So now that we have comedi talking to the hardware, we want to
+talk to comedi.  Here's some pretty low-level information --
+it's sometimes useful for debugging:
+
+<p>
+
+<tscreen><verb>
+cat /proc/comedi
+</verb></tscreen>
+
+Right now, on my computer, this command gives:
+
+<tscreen><verb>
+comedi version 0.6.4
+format string
+ 0: atmio-E              at-mio-16e-10           7
+ 1: dt282x               dt2821-f-8di            4
+</verb></tscreen>
+
+This is a feature that is not well-developed yet.  Basically, it
+currently tells you driver name, device name, and number of
+subdevices.
+
+In the <tt>demo/</tt> directory, there is a command called
+<tt>info</tt>, which provides information about each subdevice on the
+board.  The output of it is rather long, since I have 7
+subdevices  (4 or fewer is common for other boards.)
+Here's part of the output of the NI board (which
+is on <tt>/dev/comedi0</tt>.)  ('demo/info /dev/comedi0')
+
+<tscreen><verb>
+overall info:
+  version code: 0x000604
+  driver name: atmio-E
+  board name: at-mio-16e-10
+  number of subdevices: 7
+subdevice 0:
+  type: 1 (unknown)
+  number of channels: 16
+  max data value: 4095
+</verb>
+...
+</tscreen>
+
+The overall info gives information about the device -- basically
+the same information as /proc/comedi.
+
+This board has 7 subdevices.  Devices are separated into
+subdevices that each have a distinct purpose -- e.g., analog
+input, analog output, digital input/output.  This board also
+has an EEPROM and calibration DACs that are also subdevices.
+
+Subdevice 0 is the analog input subdevice.  You would have
+known this from the 'type: 1 (unknown)' line, if I've updated
+demo/info recently, because it would say 'type: 1 (analog input)'
+instead.  The other lines should be self-explanitory.  Comedi
+has more information about the device, but demo/info doesn't
+currently display this.
+
+<p>
+<sect>Individual drivers
+<p>
+
+This section contains information that is specific to each
+hardware driver.  The most current information about a driver
+is included in the comedi source.
+
+<sect1>National Instruments AT-MIO E series
+<p>
+
+
+<sect1>Data Translation
+<p>
+
+
+
+<sect>Writing programs that use comedi and comedilib
+<p>
+
+<sect1>Your first comedi program
+<p>
+
+This example requires a card that has analog or
+digital input.  Right to the source:
+
+<tscreen><verb>
+#include <stdio.h>     /* for printf() */
+#include <comedilib.h>
+
+int subdev = 0;                /* change this to your input subdevice */
+int chan = 0;          /* change this to your channel */
+int range = 0;         /* more on this later */
+int aref = AREF_GROUND;        /* more on this later */
+
+int main(int argc,char *argv[])
+{
+       comedi_t *it;
+       int chan=0;
+       lsampl_t data;
+       
+       it=comedi_open("/dev/comedi0");
+       
+       comedi_data_read(it,subdev,chan,range,aref,& data);
+       
+       printf("%d\n",data);
+       
+       return 0;
+}
+</verb></tscreen>
+
+
+Should be understandable.  Open the device, get the data,
+print it out.  This is basically the guts of <tt>demo/inp.c</tt>,
+without error checking or fancy options.  Including all
+the appropriate headers is sometimes a little tricky.
+Compile it using 
+
+<tscreen><verb>
+cc tut1.c -lcomedi -o tut1
+</verb></tscreen>
+
+Hopefully it works.
+
+A few notes:  The range variable tells comedi which gain
+to use when measuring an analog voltage.  Since we don't
+know (yet) which numbers are valid, or what each means,
+we'll use 0, because it won't cause errors.  Likewise with
+aref, which determines the analog reference used.
+
+
+<p>
+<sect1>Converting samples to voltages
+<p>
+
+If you selected an analog input subdevice, you should notice
+that the output of <tt>tut1</tt> is a number between
+0 and 4095, or 0 and 65535, depending on the number of bits
+in the A/D converter.  Comedi samples are <bf>always</bf> unsigned,
+with 0 representing the lowest voltage of the ADC, and 4095
+the highest.  Comedi compensates for
+anything else the manual for your device says.  However,
+you probably prefer to have this number translated to
+a voltage.  Naturally, as a good programmer, your first
+question is: "How do I do this in a device-independent
+manner?"
+
+For each subdevice, the comedi kernel module keeps a
+'range_type' variable.  This variable contains the number
+of available ranges (i.e., gains) that you can select,
+along with an offset in a list of range information
+structures.  If you know the range_type variable, you
+can use these macros:
+
+   RANGE_OFFSET(range_type)
+   RANGE_LENGTH(range_type)
+
+to extract such information.  However, you want the
+actual voltage information, not some integer offset
+in a table.  Rather than messing with the library
+internals, use the function
+
+   ptr=comedi_get_range(comedi_file,subdevice,channel,
+       range)
+
+which returns a pointer to a comedi_range structure.
+The comedi_range structure looks like
+
+<p>
+<tscreen><verb>
+typedef struct{
+        double min;
+        double max;
+        unsigned int unit;
+}comedi_range;
+</verb></tscreen>
+
+As you might expect, ptr[range] is for range 'range',
+which you provided to comedi_data_read() above.  'min' represents
+the voltage corresponding to comedi_data_read() returning 0,
+and 'max' represents comedi_data_read() returning 'maxdata',
+(i.e., 4095 for 12 bit A/C converters, 65535 for 16 bit,
+or, 1 for digital input -- more on this in a bit.)  The
+'unit' entry tells you if min and
+max refer to voltage, current, etc.
+
+"Could it get easier?", you say.  Well, yes.  Use
+the function comedi_to_phys(), which converts data
+values to physical units.  Call it using something like
+
+<tscreen><verb>
+volts=comedi_to_phys(it,data,range,maxdata);
+</verb></tscreen>
+
+and the opposite
+
+<tscreen><verb>
+data=comedi_from_phys(it,volts,range,maxdata);
+</verb></tscreen>
+
+You probably noticed (and were worried) that we haven't
+discussed how to determine maxdata and range_type.  Well,
+you could ask the kernel this information each time you need
+it, but since there are other variables, special cases,
+and several subdevices to worry about, it would be nice
+if the library could take care of this...  (read on...)
+
+
+<p>
+<sect1>Another section
+<p>
+
+
+In addition to providing low level routines for data
+access, the comedi library provides higher-level access,
+much like the standard C library provides fopen(), etc.
+as a high-level (and portable) alternative to the direct
+UNIX system calls open(), etc.  Similarily to fopen(),
+we have comedi_open():
+
+<p>
+<tscreen><verb>
+file=comedi_open("/dev/comedi0");
+</verb></tscreen>
+
+where file is of type <tt>(comedi_t *)</tt>.  This function
+calls <tt>open()</tt>, like we did explicitly in a previous
+section, but also fills the <tt>comedi_t</tt> structure with
+lots of goodies -- information that we will need to use
+soon.
+
+Specifically, we needed to know maxdata for a specific
+subdevice/channel.  How about:
+
+<tscreen><verb>
+maxdata=comedi_get_maxdata(file,subdevice,channel);
+</verb></tscreen>
+
+Wow.  How easy.  And the range type?
+
+<tscreen><verb>
+range_type=comedi_get_rangetype(file,subdevice,channel);
+</verb></tscreen>
+
+Cool.  Other information you need to know about a channel
+can be gotten in a similar way.
+
+
+
+<sect1>Your second comedi program
+<p>
+
+
+Actually, this is the first comedi program again, just
+that we've added what we've learned.
+
+
+<tscreen><verb>
+#include <stdio.h>      /* for printf() */
+#include <comedi.h>     /* also included by comedilib.h */
+#include <comedilib.h>  /* for comedi_get() */
+
+int subdev = 0;         /* change this to your input subdevice */
+int chan = 0;           /* change this to your channel */
+int range = 0;          /* more on this later */
+int aref = 0;           /* more on this later */
+
+int main(int argc,char *argv[])
+{
+        comedi_t *cf;
+        int chan=0;
+        int data;
+       int maxdata,rangetype;
+       double volts;
+
+        cf=comedi_open("/dev/comedi0");
+
+       maxdata=comedi_get_maxdata(cf,subdev,chan);
+
+       rangetype=comedi_get_rangetype(cf,subdev,chan);
+
+        data=comedi_get(cf->fd,subdev,chan,range,aref);
+
+       volts=comedi_to_phys(data,rangetype,range,maxdata);
+
+        printf("%d %g\n",data,volts);
+
+        return 0;
+}
+</verb></tscreen>
+
+
+By now, the <tt>comedi_read_data()</tt> line looks a little archaic, using
+the UNIX file descriptor cf->fd instead of just cf.  (By the
+way, somewhere in the heart of <tt>comedi_open()</tt> is the line
+<tt>cf->fd=open(filename,O_RDWR)</tt>.)  Well, there isn't one good
+replacement, since it highly depends on your application
+what additional features you might want in a <tt>comedi_get()</tt>
+replacement.  But this is the topic of a different section.
+
+
+<p>
+<sect>Application-specific functions
+<p>
+
+<sect1>Digital Input/Output
+<p>
+
+Many boards supported by comedi have digital input and output
+channels.  Some boards allow the direction of a channel to be
+specified in software.
+
+Comedi groups digital channels into subdevice, which is a group
+of digital channels that have the same characteristics.  For
+example, digital output lines will be grouped into a digital
+output subdevice, bidirectional digital lines will be grouped
+into a digital I/O subdevice.  Thus, there can be multiple
+digital subdevices on a particular board.
+
+
+<sect1>Timed Input/Output
+<p>
+
+<p>
+<sect1>Slowly-varying inputs
+<p>
+
+
+Sometimes, your input channels change slowly enough that
+you are able to average many sucessive input values to get a
+more accurate measurement of the actual value.  In general,
+the more samples you average, the better your estimate
+gets, roughly by a factor of sqrt(number_of_samples).
+Obviously, there are limitations to this:
+
+<p>
+<itemize>
+<item>
+you are ultimately limited by "spurious free dynamic range"
+
+<item>
+you need to have _some_ noise on the input channel,
+otherwise you will be averaging the same number N times.
+
+<item>
+the more noise you have, the greater your SFDR, but it
+takes many more samples to compensate for the increased
+noise
+
+<item>
+if you feel the need to average samples for 2 seconds,
+your signal will need to be _very_ slowly-varying, i.e.,
+not varying more than your target uncertainty for the
+entire 2 seconds.
+
+</itemize>
+
+As you might have guessed, the comedi library has functions
+to help you in your quest to accurately measure slowly varying
+inputs.  I use these functions to measure thermocouple voltages
+-- actually, the library functions came from a section of code
+that was previously part of the thermocouple reading program.
+
+The comedi self-calibration utility also uses these functions.
+On some hardware, it is possible to tell it to measure an
+internal stable voltage reference, which is typically going
+to be very slowly varying -- on the kilosecond time scale
+or more.  So it is reasonable to measure millions of samples,
+to get a very accurate measurement of the A/D converter output
+value that corresponds to the voltage reference.  Sometimes,
+however, this is overkill, since there is no need to
+perform a part-per-million calibration to a standard that
+is only accurate to part-per-thousand.
+
+
+
+<p>
+
+<sect>Comedilib reference
+
+<p>
+Reference of structures:
+
+<tscreen><verb>
+typedef struct comedi_t_struct comedi_t;
+
+typedef struct{
+       double min;
+       double max;
+       unsigned int unit;
+}comedi_range;
+
+typedef struct comedi_sv_struct{
+       comedi_t *dev;
+       unsigned int subdevice;
+       unsigned int chan;
+
+       /* range policy */
+       int range;
+       int aref;
+       
+       /* number of measurements to average (for ai) */
+       int n;
+
+       lsampl_t maxdata;
+}comedi_sv_t;
+</verb></tscreen>
+
+<sect1>comedi_loglevel()
+
+<p>
+
+int comedi_loglevel(int loglevel);
+
+<p>
+
+This function affects the output of debugging and error messages
+from comedlib.  By increasing the loglevel, additional debugging
+information will be printed.  This function returns the previous
+loglevel.  Some debugging information will only be printed if
+comedilib was compiled with this debugging information included.
+The loglevel can also be affected by the environment
+variable COMEDI_LOGLEVEL.  The meaning of the loglevels is as
+follows:
+
+COMEDILIB_LOGLEVEL=0
+
+Comedilib prints nothing.
+
+COMEDILIB_LOGLEVEL=1  (default)
+
+Comedilib only prints error messages when there is a
+self-consistency error.
+
+COMEDILIB_LOGLEVEL=2
+
+Comedilib prints an error message whenever an invalid
+parameter is passed to comedilib.
+
+COMEDILIB_LOGLEVEL=3
+
+Comedilib prints an error message whenever an error is generated
+in the comedilib library or is generated in the C library when
+called by comedilib.
+
+COMEDILIB_LOGLEVEL=4
+
+Comedilib prints a lot of debugging messages.
+
+<p>
+
+<sect1>comedi_open
+
+<p>
+
+comedi_t *comedi_open(char *fn);
+
+Opens a comedi device specified by the filename fn.  Returns NULL
+on error.  Returns a handle that is given as a parameter to other
+comedilib functions.
+
+You are not supposed to have access to the structure comedi_t.
+
+void comedi_close(comedi_t *it);
+
+Closes a device previously opened by comedi_open().
+
+void comedi_perror(const char *s);
+char *comedi_strerror(int errnum);
+int comedi_errno(void);
+
+When a comedilib function fails, it usually returns -1 or
+NULL, depending on the return type.  An internal library
+variable stores an error number, which can be retrieved with
+<tt>comedi_errno()</tt>.  This error number can be
+converted to a human-readable form by the functions
+<tt>comedi_perror()</tt> and <tt>comedi_strerror()</tt>.
+
+These functions are intended to mimic the behavior of the
+standard C library functions <tt>perror()</tt>,
+<tt>strerror</tt>, and <tt>errno()</tt>.  In particular,
+comedilib functions sometimes return an error that is generated
+inside the C library; the comedi error message in this case
+is the same as the C library.
+
+The function <tt>comedi_perror()</tt> prints an error
+message to stderr.  The error message consists of the
+argument string, a colon, a space, a description of the error
+condition, and a new line.
+
+The function <tt>comedi_strerror()</tt> returns a pointer to a
+character string
+describing the comedilib error <tt>errnum</tt>.  The persistence
+of the returned pointer is undefined, and should not be trusted
+after the next comedilib call.  An unrecognized error number will
+return a pointer to the string "undefined error", or similar.
+
+The function <tt>comedi_errno()</tt>
+returns an integer describing the most recent comedilib error.  This
+integer may be used as the <tt>errnum</tt> parameter for
+<tt>comedi_strerror()</tt>.
+
+<p>
+<sect1>comedi_fileno()
+
+<p>
+
+int comedi_fileno(comedi_t *it);
+
+The function <tt>comedi_fileno</tt>
+returns the integer descriptor for the handle <tt>it</tt>.  If
+<tt>it</tt> is an invalid <tt>comedi_t</tt> pointer, the function
+returns -1 and sets the appropriate comedilib error value.
+
+
+<p>
+<sect1>comedi_get_n_subdevices()
+<p>
+
+int comedi_get_n_subdevices(comedi_t *it);
+
+The function <tt>comedi_get_n_subdevices</tt> returns the
+number of subdevices associated with the comedi descriptor
+<tt>it</tt>, or -1 if there is an error.
+
+<p>
+<sect1>comedi_get_version_code()
+<p>
+
+int comedi_get_version_code(comedi_t *it);
+
+The function <tt>comedi_get_version_code()</tt> returns the
+version code of the currently running comedi module.  The version
+code is of the form 0x010203, which is the version code for
+version 1.2.3.
+
+<p>
+<sect1>comedi_get_driver_name()
+<p>
+
+char *comedi_get_driver_name(comedi_t *it);
+
+The function <tt>comedi_get_driver_name</tt> returns a pointer
+to a string containing the name of the driver being used by comedi
+for the comedi device represented by <tt>it</tt>.  This pointer is
+valid until the comedi descriptor <tt>it</tt> is closed.  This
+function returns NULL if there is an error.
+
+<p>
+<sect1>comedi_get_board_name()
+<p>
+
+char *comedi_get_board_name(comedi_t *it);
+
+The function <tt>comedi_get_board_name</tt> returns a pointer
+to a string containing the name of the device.  This pointer is
+valid until the comedi descriptor <tt>it</tt> is closed.  This
+function returns NULL if there is an error.
+
+<p>
+<sect1>comedi_get_subdevice_type()
+<p>
+
+int comedi_get_subdevice_type(comedi_t *it,unsigned int subdevice);
+
+The function <tt>comedi_get_subdevice_type()</tt> returns an
+integer describing the type of subdevice that belongs to the comedi
+device <tt>it</tt> and has the index <tt>subdevice</tt>.  The
+function returns -1 is there is an error.
+
+Valid subdevice types are:
+
+<itemize>
+<item><tt>COMEDI_SUBD_UNUSED</tt>
+Subdevice has no functionality, i.e., a place-holder.
+<item><tt>COMEDI_SUBD_AI</tt>  Analog input
+<item><tt>COMEDI_SUBD_AO</tt>  Analog output
+<item><tt>COMEDI_SUBD_DI</tt>  Digital input
+<item><tt>COMEDI_SUBD_DO</tt>  Digital output
+<item><tt>COMEDI_SUBD_DIO</tt>
+Digital input/output.  Channels are configurable as to whether they
+are inputs or outputs.
+<item><tt>COMEDI_SUBD_COUNTER</tt>     Counter
+<item><tt>COMEDI_SUBD_TIMER</tt>       Timer
+<item><tt>COMEDI_SUBD_MEMORY</tt>
+Memory, e.g., EEPROM or dual-ported RAM
+<item><tt>COMEDI_SUBD_CALIB</tt>
+Calibration DACs
+<item><tt>COMEDI_SUBD_PROC</tt>
+Processor or DSP
+</itemize>
+
+
+<p>
+<sect1>comedi_find_subdevice_by_type()
+<p>
+
+int comedi_find_subdevice_by_type(comedi_t *it,int type,unsigned int start_subdevice)
+
+The function <tt>comedi_find_subdevice_by_type</tt> tries to
+locate a subdevice belonging to comedi device <tt>it</tt>,
+having type <tt>type</tt>, starting with the subdevice
+<tt>start_subdevice</tt>.  If it finds the requested subdevice,
+it returns its index.  If it does not locate the requested
+subdevice, it returns -1 and sets the comedi error number to
+"subdevice not found".  If there is an error, the function
+returns -1 and sets the appropriate error.
+
+For subdevice types, see the manual page for the function
+<tt>comedi_get_subdevice_type()</tt>.
+
+<p>
+<sect1>comedi_get_n_channels()
+<p>
+
+
+int comedi_get_n_channels(comedi_t *it,unsigned int subdevice);
+
+The function <tt>comedi_get_n_channels()</tt> returns the number
+of channels of the subdevice belonging to the comedi device <tt>it</tt>
+and having index <tt>subdevice</tt>.  This function returns -1 on error.
+
+<p>
+<sect1>comedi_get_maxdata()
+<p>
+
+lsampl_t comedi_get_maxdata(comedi_t *it,unsigned int subdevice,unsigned int chan);
+
+The function <tt>comedi_get_maxdata()</tt> returns the maximum
+valid data value for channel <tt>chan</tt> of subdevice
+<tt>subdevice</tt> belonging to the comedi device <tt>it</tt>
+This function returns 0 on error.
+
+<p>
+<sect1>comedi_get_rangetype()
+<p>
+
+int comedi_get_rangetype(comedi_t *it,unsigned int subdevice,unsigned int chan);
+
+The function <tt>comedi_get_rangetype()</tt> returns an integer
+that represents the number of range specifications available for a
+particular channel, as well as a conversion table to convert sample
+values to/from physical units.  The macro
+<tt>RANGE_LENGTH(rangetype)</tt>
+can be used to determine the number of range specifications for a given
+range type.
+
+<p>
+<sect1>comedi_get_range()
+<p>
+
+comedi_range * comedi_get_range(comedi_t *it,unsigned int subdevice,unsigned int chan,unsigned int range);
+
+The function <tt>comedi_get_range</tt> returns a pointer to a
+comedi_range structure that contains information that can be used to
+convert sample values to or from physical units.  The pointer is valid
+until the comedi device <tt>it</tt> is closed.  If there is an
+error, NULL is returned.
+
+<p>
+<sect1>comedi_trigger()
+<p>
+
+int comedi_trigger(comedi_t *it,comedi_trig *trig);
+
+The function <tt>comedi_trigger()</tt> instructs comedi to
+perform the command specified by the trigger structure
+<tt>trig</tt>.  Results depend on the particular command
+being issued.  If there is an error, -1 is returned.
+
+Complete information about comedi commands is given in the
+manual page comedi(8).
+
+double comedi_to_phys(lsampl_t data,comedi_range *rng,lsampl_t maxdata);
+lsampl_t comedi_from_phys(double data,comedi_range *rng,lsampl_t maxdata);
+
+The functions <tt>comedi_to_phys()</tt> and
+<tt>comedi_from_phys()</tt> convert sample values to/from physical
+units.  The parameter <tt>rng</tt> represents the conversion
+information to use, and the parameter <tt>maxdata</tt> represents
+the maximum possible data value for the channel that the data was read/
+will be written to.
+
+
+<p>
+<sect1>comedi_data_read()
+<p>
+
+int comedi_data_read(comedi_t *it,unsigned int subd,unsigned int chan,
+       unsigned int range,unsigned int aref,lsampl_t *data);
+int comedi_data_write(comedi_t *it,unsigned int subd,unsigned int chan,
+       unsigned int range,unsigned int aref,lsampl_t data);
+
+These functions read or write a single sample on the channel that
+is specified by the comedi device <tt>it</tt>, the
+subdevice <tt>subd</tt>, and the channel <tt>chan</tt>.
+For the operation,
+the device is configured to use range specification
+<tt>range</tt> and (if appropriate) analog reference type
+<tt>aref</tt>.  Analog reference types that are not supported
+by the device are silently ignored.
+
+The function <tt>comedi_data_read()</tt> reads one data value from
+the specified channel and places the 
+data value that is read in the location pointed to by
+<tt>data</tt>.
+
+The function <tt>comedi_data_write()</tt> writes the data value
+specified by the argument <tt>data</tt> to
+the specified channel.
+
+On sucess, these functions return 0.  If there is an error, -1 is
+returned.
+
+Valid analog reference numbers are:
+
+<itemize>
+<item>AREF_GROUND      Reference to analog ground
+<item>AREF_COMMON      Reference to analog common
+<item>AREF_DIFF                Differential reference
+<item>AREF_OTHER       Board-specific meaning
+</itemize>
+
+Valid data values used by these functions is an unsigned integer
+less than or equal to <tt>maxdata</tt>, which is channel-dependent.
+Conversion of these data values to physical units can be performed
+by <tt>comedi_to_phys()</tt> and <tt>comedi_from_phys()</tt>.
+
+
+<p>
+<sect1>comedi_sv_init()
+<p>
+
+<tscreen><verb>
+int comedi_sv_init(comedi_sv_t *it,comedi_t *dev,unsigned int subd,unsigned int chan);
+int comedi_sv_update(comedi_sv_t *it);
+int comedi_sv_measure(comedi_sv_t *it,double *data);
+</verb></tscreen>
+
+The special functions <tt>comedi_sv_*()</tt> are designed to
+make it easy to accurately measure slowly varying analog inputs.
+A slowly
+varying input is one that is effectively constant over the course
+of approximately 100 A/D conversions.  However, since these
+conversions can sometimes be pre-empted by scheduling, for most
+purposes, a slowly varying signal should be effectively constant
+for greater than 20 ms (the default Linux timeslice).
+
+By averaging many A/D conversions of a relatively constant
+signal, it is possible to get a better measurement of the signal
+than a single A/D conversion.  In general, the uncertainty of the
+measurement decreases as the square root of the number of samples.
+This is limited by the rate that which the signal varies, and
+ultimately by the spurious free dynamic range of the A/D converter.
+
+
+<p>
+<sect1>comedi_get_timer()
+<p>
+
+<tscreen><verb>
+int comedi_get_timer(comedi_t *it,unsigned int subdev,double freq,unsigned int *trigvar,
+       double *actual_freq);
+</verb></tscreen>
+
+
+<p>
+
+</article>
+
diff --git a/doc/html/comedilib-1.html b/doc/html/comedilib-1.html
new file mode 100644 (file)
index 0000000..f3ba92c
--- /dev/null
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation: Introduction</TITLE>
+ <LINK HREF="comedilib-2.html" REL=next>
+
+ <LINK HREF="comedilib.html#toc1" REL=contents>
+</HEAD>
+<BODY>
+<A HREF="comedilib-2.html">Next</A>
+Previous
+<A HREF="comedilib.html#toc1">Contents</A>
+<HR>
+<H2><A NAME="s1">1. Introduction</A></H2>
+
+<P>This is preliminary documentation for Comedi and Comedilib.
+<P>
+<HR>
+<A HREF="comedilib-2.html">Next</A>
+Previous
+<A HREF="comedilib.html#toc1">Contents</A>
+</BODY>
+</HTML>
diff --git a/doc/html/comedilib-2.html b/doc/html/comedilib-2.html
new file mode 100644 (file)
index 0000000..4417140
--- /dev/null
@@ -0,0 +1,231 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation: Installation and configuration</TITLE>
+ <LINK HREF="comedilib-3.html" REL=next>
+ <LINK HREF="comedilib-1.html" REL=previous>
+ <LINK HREF="comedilib.html#toc2" REL=contents>
+</HEAD>
+<BODY>
+<A HREF="comedilib-3.html">Next</A>
+<A HREF="comedilib-1.html">Previous</A>
+<A HREF="comedilib.html#toc2">Contents</A>
+<HR>
+<H2><A NAME="s2">2. Installation and configuration</A></H2>
+
+<P>
+<P>This section covers compiling, installing, and configuring
+comedi and comedlib.
+<P>
+<P>
+<H2><A NAME="ss2.1">2.1 Compiling and Installing</A>
+</H2>
+
+<P>
+<P>This section has not been written.
+<P>
+<P>
+<H2><A NAME="ss2.2">2.2 Insmod'ding the kernel module</A>
+</H2>
+
+<P>
+<P>This section has not been written.
+<P>
+<P>
+<H2><A NAME="ss2.3">2.3 Configuring comedi for your hardware</A>
+</H2>
+
+<P>
+<P>
+<P>I assume that your hardware device is in your computer, and that
+you know the relevant details about it, i.e., what kind of card
+it is, the I/O base, the IRQ, jumper settings related to input
+ranges, etc.
+<P>To tell the comedi kernel module that you have a particular device, and
+some information about it, you will be running the <CODE>comedi_config</CODE>
+command.  Perhaps you should read the man page now.
+<P>In this tutorial, I will go through the process of configuring comedi
+for two devices, a National Instruments AT-MIO-16E-10
+and a Data Translation DT2821-F-8DI.
+<P>The NI board is plug-and-play, and the man page tells me that I need
+to configure the PnP part of the board with isapnptools.  The isapnptools
+package is a little cryptic, but the concepts are simple.  Once I
+learned how to use it, I settled on a /etc/isapnp.conf file that
+contained the lines:
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+# ANSI string -->National Instruments, AT-MIO-16E-10&lt;--
+(CONFIGURE NIC2400/10725401 (LD 0
+        (IO 0 (BASE 0x0260))
+        (INT 0 (IRQ 3 (MODE +E)))
+#       (DMA 0 (CHANNEL 5))
+#       (DMA 1 (CHANNEL 6))
+        (ACT Y)
+))
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>
+<P>It also contains a few lines about overall configuration and about my
+sound card.  I found out after a bit of trial-and-error that the NI
+board does not always work with interrupts other than IRQ 3.  YMMV.
+Currently, the driver doesn't use DMA, but it may in the future, so
+I commented out the DMA lines.  It is a curious fact that the device
+ignores the IRQ and DMA information given here, however, I keep the
+information here to remind myself that the numbers aren't arbitrary.
+<P>When I run comedi_config (as root, of course), I provide the same
+information.  Since I want to have the board configured every time
+I boot, I put the line
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+/usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,3
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>into <CODE>/etc/rc.d/rc.local</CODE>.  You can, of course, run this command at
+a command prompt.  The man page tells me that the option list
+is supposed to be "(I/O base),(IRQ)", so I used the same numbers
+as I put in /etc/isapnp.conf, i.e., 0x260,3.
+<P>For the Data Translation board, I need to have a list of the
+jumper settings.  Fortunately, I wrote them all down in the
+manual -- I hope they are still correct.  However, I had to
+open the case to figure out which board in the series I had.
+It is a DT2821-f-8di.  The man page of comedi_config tells
+me that I need to know the I/O base, IRQ, DMA 1, DMA 2.  However,
+since I wrote the driver, I know that it also recognizes the
+differential/single-ended and unipolar/bipolar jumpers.  As always,
+the source is the final authority, and looking in module/dt282x.c
+tells me that the options list is interpreted as:
+<P>
+<UL>
+<LI>I/O base</LI>
+<LI>IRQ</LI>
+<LI>1=differential, 0=single ended</LI>
+<LI>ai 0=unipolar, 1=bipolar</LI>
+<LI>ao0 0=unipolar, 1=bipolar</LI>
+<LI>ao1 0=unipolar, 1=bipolar</LI>
+<LI>dma1</LI>
+<LI>dma2</LI>
+</UL>
+<P>(ai=analog input, ao=analog output.)  From this, I decide that
+the appropriate options list is
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+0x200,4,,1,1,1
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>I left the differential/single-ended number blank, since the
+driver already knowns (from the board name), that it is
+differential.  I  also left the DMA numbers blank, since I
+don't want the driver to use DMA.  (Don't want it to interfere
+with my sound card -- life is full of difficult choices.)
+Keep in mind that things commented in the source, but not in
+the documentation are about as likely to change as the weather,
+so I put good comments next to the following line when I put
+it in rc.local.
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+/usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>So now I think that I have my boards configured correctly.
+Since data acquisition boards are not typically well-engineered,
+comedi sometimes can't figure out if the board is actually there.
+If it can't, it assumes you are right.  Both of these boards
+are well-made, so comedi will give me an error message if it
+can't find them.  The comedi kernel module, since it is a part
+of the kernel, prints messages to the kernel logs, which you
+can access through the command 'dmesg' or /var/log/messages.
+Here is a configuration failure (from dmesg):
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+comedi0: ni_E: 0x0200 can't find board
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>When it does work, I get:
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+comedi0: ni_E: 0x0260 at-mio-16e-10 ( irq = 3 )
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>Note that it also correctly identified my board.
+<P>
+<P>
+<P>
+<H2><A NAME="ss2.4">2.4 Getting information from comedi</A>
+</H2>
+
+<P>
+<P>
+<P>So now that we have comedi talking to the hardware, we want to
+talk to comedi.  Here's some pretty low-level information --
+it's sometimes useful for debugging:
+<P>
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+cat /proc/comedi
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>Right now, on my computer, this command gives:
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+comedi version 0.6.4
+format string
+ 0: atmio-E              at-mio-16e-10           7
+ 1: dt282x               dt2821-f-8di            4
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>This is a feature that is not well-developed yet.  Basically, it
+currently tells you driver name, device name, and number of
+subdevices.
+<P>In the <CODE>demo/</CODE> directory, there is a command called
+<CODE>info</CODE>, which provides information about each subdevice on the
+board.  The output of it is rather long, since I have 7
+subdevices  (4 or fewer is common for other boards.)
+Here's part of the output of the NI board (which
+is on <CODE>/dev/comedi0</CODE>.)  ('demo/info /dev/comedi0')
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+overall info:
+  version code: 0x000604
+  driver name: atmio-E
+  board name: at-mio-16e-10
+  number of subdevices: 7
+subdevice 0:
+  type: 1 (unknown)
+  number of channels: 16
+  max data value: 4095
+</PRE>
+
+...
+</CODE></BLOCKQUOTE>
+<P>The overall info gives information about the device -- basically
+the same information as /proc/comedi.
+<P>This board has 7 subdevices.  Devices are separated into
+subdevices that each have a distinct purpose -- e.g., analog
+input, analog output, digital input/output.  This board also
+has an EEPROM and calibration DACs that are also subdevices.
+<P>Subdevice 0 is the analog input subdevice.  You would have
+known this from the 'type: 1 (unknown)' line, if I've updated
+demo/info recently, because it would say 'type: 1 (analog input)'
+instead.  The other lines should be self-explanitory.  Comedi
+has more information about the device, but demo/info doesn't
+currently display this.
+<P>
+<P>
+<HR>
+<A HREF="comedilib-3.html">Next</A>
+<A HREF="comedilib-1.html">Previous</A>
+<A HREF="comedilib.html#toc2">Contents</A>
+</BODY>
+</HTML>
diff --git a/doc/html/comedilib-3.html b/doc/html/comedilib-3.html
new file mode 100644 (file)
index 0000000..e0c7981
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation: Individual drivers</TITLE>
+ <LINK HREF="comedilib-4.html" REL=next>
+ <LINK HREF="comedilib-2.html" REL=previous>
+ <LINK HREF="comedilib.html#toc3" REL=contents>
+</HEAD>
+<BODY>
+<A HREF="comedilib-4.html">Next</A>
+<A HREF="comedilib-2.html">Previous</A>
+<A HREF="comedilib.html#toc3">Contents</A>
+<HR>
+<H2><A NAME="s3">3. Individual drivers</A></H2>
+
+<P>
+<P>This section contains information that is specific to each
+hardware driver.  The most current information about a driver
+is included in the comedi source.
+<P>
+<H2><A NAME="ss3.1">3.1 National Instruments AT-MIO E series</A>
+</H2>
+
+<P>
+<P>
+<P>
+<H2><A NAME="ss3.2">3.2 Data Translation</A>
+</H2>
+
+<P>
+<P>
+<P>
+<P>
+<HR>
+<A HREF="comedilib-4.html">Next</A>
+<A HREF="comedilib-2.html">Previous</A>
+<A HREF="comedilib.html#toc3">Contents</A>
+</BODY>
+</HTML>
diff --git a/doc/html/comedilib-4.html b/doc/html/comedilib-4.html
new file mode 100644 (file)
index 0000000..689e47b
--- /dev/null
@@ -0,0 +1,251 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation: Writing programs that use comedi and comedilib</TITLE>
+ <LINK HREF="comedilib-5.html" REL=next>
+ <LINK HREF="comedilib-3.html" REL=previous>
+ <LINK HREF="comedilib.html#toc4" REL=contents>
+</HEAD>
+<BODY>
+<A HREF="comedilib-5.html">Next</A>
+<A HREF="comedilib-3.html">Previous</A>
+<A HREF="comedilib.html#toc4">Contents</A>
+<HR>
+<H2><A NAME="s4">4. Writing programs that use comedi and comedilib</A></H2>
+
+<P>
+<P>
+<H2><A NAME="ss4.1">4.1 Your first comedi program</A>
+</H2>
+
+<P>
+<P>This example requires a card that has analog or
+digital input.  Right to the source:
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+#include &lt;stdio.h>   /* for printf() */
+#include &lt;comedilib.h>
+
+int subdev = 0;         /* change this to your input subdevice */
+int chan = 0;           /* change this to your channel */
+int range = 0;          /* more on this later */
+int aref = AREF_GROUND; /* more on this later */
+
+int main(int argc,char *argv[])
+{
+        comedi_t *it;
+        int chan=0;
+        lsampl_t data;
+        
+        it=comedi_open("/dev/comedi0");
+        
+        comedi_data_read(it,subdev,chan,range,aref,&amp; data);
+        
+        printf("%d\n",data);
+        
+        return 0;
+}
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>
+<P>Should be understandable.  Open the device, get the data,
+print it out.  This is basically the guts of <CODE>demo/inp.c</CODE>,
+without error checking or fancy options.  Including all
+the appropriate headers is sometimes a little tricky.
+Compile it using 
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+cc tut1.c -lcomedi -o tut1
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>Hopefully it works.
+<P>A few notes:  The range variable tells comedi which gain
+to use when measuring an analog voltage.  Since we don't
+know (yet) which numbers are valid, or what each means,
+we'll use 0, because it won't cause errors.  Likewise with
+aref, which determines the analog reference used.
+<P>
+<P>
+<P>
+<H2><A NAME="ss4.2">4.2 Converting samples to voltages</A>
+</H2>
+
+<P>
+<P>If you selected an analog input subdevice, you should notice
+that the output of <CODE>tut1</CODE> is a number between
+0 and 4095, or 0 and 65535, depending on the number of bits
+in the A/D converter.  Comedi samples are <B>always</B> unsigned,
+with 0 representing the lowest voltage of the ADC, and 4095
+the highest.  Comedi compensates for
+anything else the manual for your device says.  However,
+you probably prefer to have this number translated to
+a voltage.  Naturally, as a good programmer, your first
+question is: "How do I do this in a device-independent
+manner?"
+<P>For each subdevice, the comedi kernel module keeps a
+'range_type' variable.  This variable contains the number
+of available ranges (i.e., gains) that you can select,
+along with an offset in a list of range information
+structures.  If you know the range_type variable, you
+can use these macros:
+<P>RANGE_OFFSET(range_type)
+RANGE_LENGTH(range_type)
+<P>to extract such information.  However, you want the
+actual voltage information, not some integer offset
+in a table.  Rather than messing with the library
+internals, use the function
+<P>ptr=comedi_get_range(comedi_file,subdevice,channel,
+range)
+<P>which returns a pointer to a comedi_range structure.
+The comedi_range structure looks like
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+typedef struct{
+        double min;
+        double max;
+        unsigned int unit;
+}comedi_range;
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>As you might expect, ptr[range] is for range 'range',
+which you provided to comedi_data_read() above.  'min' represents
+the voltage corresponding to comedi_data_read() returning 0,
+and 'max' represents comedi_data_read() returning 'maxdata',
+(i.e., 4095 for 12 bit A/C converters, 65535 for 16 bit,
+or, 1 for digital input -- more on this in a bit.)  The
+'unit' entry tells you if min and
+max refer to voltage, current, etc.
+<P>"Could it get easier?", you say.  Well, yes.  Use
+the function comedi_to_phys(), which converts data
+values to physical units.  Call it using something like
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+volts=comedi_to_phys(it,data,range,maxdata);
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>and the opposite
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+data=comedi_from_phys(it,volts,range,maxdata);
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>You probably noticed (and were worried) that we haven't
+discussed how to determine maxdata and range_type.  Well,
+you could ask the kernel this information each time you need
+it, but since there are other variables, special cases,
+and several subdevices to worry about, it would be nice
+if the library could take care of this...  (read on...)
+<P>
+<P>
+<P>
+<H2><A NAME="ss4.3">4.3 Another section</A>
+</H2>
+
+<P>
+<P>
+<P>In addition to providing low level routines for data
+access, the comedi library provides higher-level access,
+much like the standard C library provides fopen(), etc.
+as a high-level (and portable) alternative to the direct
+UNIX system calls open(), etc.  Similarily to fopen(),
+we have comedi_open():
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+file=comedi_open("/dev/comedi0");
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>where file is of type <CODE>(comedi_t *)</CODE>.  This function
+calls <CODE>open()</CODE>, like we did explicitly in a previous
+section, but also fills the <CODE>comedi_t</CODE> structure with
+lots of goodies -- information that we will need to use
+soon.
+<P>Specifically, we needed to know maxdata for a specific
+subdevice/channel.  How about:
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+maxdata=comedi_get_maxdata(file,subdevice,channel);
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>Wow.  How easy.  And the range type?
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+range_type=comedi_get_rangetype(file,subdevice,channel);
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>Cool.  Other information you need to know about a channel
+can be gotten in a similar way.
+<P>
+<P>
+<P>
+<H2><A NAME="ss4.4">4.4 Your second comedi program</A>
+</H2>
+
+<P>
+<P>
+<P>Actually, this is the first comedi program again, just
+that we've added what we've learned.
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+#include &lt;stdio.h>      /* for printf() */
+#include &lt;comedi.h>     /* also included by comedilib.h */
+#include &lt;comedilib.h>  /* for comedi_get() */
+
+int subdev = 0;         /* change this to your input subdevice */
+int chan = 0;           /* change this to your channel */
+int range = 0;          /* more on this later */
+int aref = 0;           /* more on this later */
+
+int main(int argc,char *argv[])
+{
+        comedi_t *cf;
+        int chan=0;
+        int data;
+        int maxdata,rangetype;
+        double volts;
+
+        cf=comedi_open("/dev/comedi0");
+
+        maxdata=comedi_get_maxdata(cf,subdev,chan);
+
+        rangetype=comedi_get_rangetype(cf,subdev,chan);
+
+        data=comedi_get(cf->fd,subdev,chan,range,aref);
+
+        volts=comedi_to_phys(data,rangetype,range,maxdata);
+
+        printf("%d %g\n",data,volts);
+
+        return 0;
+}
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>
+<P>By now, the <CODE>comedi_read_data()</CODE> line looks a little archaic, using
+the UNIX file descriptor cf->fd instead of just cf.  (By the
+way, somewhere in the heart of <CODE>comedi_open()</CODE> is the line
+<CODE>cf->fd=open(filename,O_RDWR)</CODE>.)  Well, there isn't one good
+replacement, since it highly depends on your application
+what additional features you might want in a <CODE>comedi_get()</CODE>
+replacement.  But this is the topic of a different section.
+<P>
+<P>
+<P>
+<HR>
+<A HREF="comedilib-5.html">Next</A>
+<A HREF="comedilib-3.html">Previous</A>
+<A HREF="comedilib.html#toc4">Contents</A>
+</BODY>
+</HTML>
diff --git a/doc/html/comedilib-5.html b/doc/html/comedilib-5.html
new file mode 100644 (file)
index 0000000..92e74ba
--- /dev/null
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation: Application-specific functions</TITLE>
+ <LINK HREF="comedilib-6.html" REL=next>
+ <LINK HREF="comedilib-4.html" REL=previous>
+ <LINK HREF="comedilib.html#toc5" REL=contents>
+</HEAD>
+<BODY>
+<A HREF="comedilib-6.html">Next</A>
+<A HREF="comedilib-4.html">Previous</A>
+<A HREF="comedilib.html#toc5">Contents</A>
+<HR>
+<H2><A NAME="s5">5. Application-specific functions</A></H2>
+
+<P>
+<P>
+<H2><A NAME="ss5.1">5.1 Digital Input/Output</A>
+</H2>
+
+<P>
+<P>Many boards supported by comedi have digital input and output
+channels.  Some boards allow the direction of a channel to be
+specified in software.
+<P>Comedi groups digital channels into subdevice, which is a group
+of digital channels that have the same characteristics.  For
+example, digital output lines will be grouped into a digital
+output subdevice, bidirectional digital lines will be grouped
+into a digital I/O subdevice.  Thus, there can be multiple
+digital subdevices on a particular board.
+<P>
+<P>
+<H2><A NAME="ss5.2">5.2 Timed Input/Output</A>
+</H2>
+
+<P>
+<P>
+<P>
+<H2><A NAME="ss5.3">5.3 Slowly-varying inputs</A>
+</H2>
+
+<P>
+<P>
+<P>Sometimes, your input channels change slowly enough that
+you are able to average many sucessive input values to get a
+more accurate measurement of the actual value.  In general,
+the more samples you average, the better your estimate
+gets, roughly by a factor of sqrt(number_of_samples).
+Obviously, there are limitations to this:
+<P>
+<P>
+<UL>
+<LI>you are ultimately limited by "spurious free dynamic range"
+</LI>
+<LI>you need to have _some_ noise on the input channel,
+otherwise you will be averaging the same number N times.
+</LI>
+<LI>the more noise you have, the greater your SFDR, but it
+takes many more samples to compensate for the increased
+noise
+</LI>
+<LI>if you feel the need to average samples for 2 seconds,
+your signal will need to be _very_ slowly-varying, i.e.,
+not varying more than your target uncertainty for the
+entire 2 seconds.
+</LI>
+</UL>
+<P>As you might have guessed, the comedi library has functions
+to help you in your quest to accurately measure slowly varying
+inputs.  I use these functions to measure thermocouple voltages
+-- actually, the library functions came from a section of code
+that was previously part of the thermocouple reading program.
+<P>The comedi self-calibration utility also uses these functions.
+On some hardware, it is possible to tell it to measure an
+internal stable voltage reference, which is typically going
+to be very slowly varying -- on the kilosecond time scale
+or more.  So it is reasonable to measure millions of samples,
+to get a very accurate measurement of the A/D converter output
+value that corresponds to the voltage reference.  Sometimes,
+however, this is overkill, since there is no need to
+perform a part-per-million calibration to a standard that
+is only accurate to part-per-thousand.
+<P>
+<P>
+<P>
+<P>
+<P>
+<HR>
+<A HREF="comedilib-6.html">Next</A>
+<A HREF="comedilib-4.html">Previous</A>
+<A HREF="comedilib.html#toc5">Contents</A>
+</BODY>
+</HTML>
diff --git a/doc/html/comedilib-6.html b/doc/html/comedilib-6.html
new file mode 100644 (file)
index 0000000..49afcdb
--- /dev/null
@@ -0,0 +1,384 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation: Comedilib reference</TITLE>
+ <LINK HREF="comedilib-5.html" REL=previous>
+ <LINK HREF="comedilib.html#toc6" REL=contents>
+</HEAD>
+<BODY>
+Next
+<A HREF="comedilib-5.html">Previous</A>
+<A HREF="comedilib.html#toc6">Contents</A>
+<HR>
+<H2><A NAME="s6">6. Comedilib reference</A></H2>
+
+<P>Reference of structures:
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+typedef struct comedi_t_struct comedi_t;
+
+typedef struct{
+        double min;
+        double max;
+        unsigned int unit;
+}comedi_range;
+
+typedef struct comedi_sv_struct{
+        comedi_t *dev;
+        unsigned int subdevice;
+        unsigned int chan;
+
+        /* range policy */
+        int range;
+        int aref;
+        
+        /* number of measurements to average (for ai) */
+        int n;
+
+        lsampl_t maxdata;
+}comedi_sv_t;
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>
+<H2><A NAME="ss6.1">6.1 comedi_loglevel()</A>
+</H2>
+
+<P>
+<P>int comedi_loglevel(int loglevel);
+<P>
+<P>
+<P>This function affects the output of debugging and error messages
+from comedlib.  By increasing the loglevel, additional debugging
+information will be printed.  This function returns the previous
+loglevel.  Some debugging information will only be printed if
+comedilib was compiled with this debugging information included.
+The loglevel can also be affected by the environment
+variable COMEDI_LOGLEVEL.  The meaning of the loglevels is as
+follows:
+<P>COMEDILIB_LOGLEVEL=0
+<P>Comedilib prints nothing.
+<P>COMEDILIB_LOGLEVEL=1  (default)
+<P>Comedilib only prints error messages when there is a
+self-consistency error.
+<P>COMEDILIB_LOGLEVEL=2
+<P>Comedilib prints an error message whenever an invalid
+parameter is passed to comedilib.
+<P>COMEDILIB_LOGLEVEL=3
+<P>Comedilib prints an error message whenever an error is generated
+in the comedilib library or is generated in the C library when
+called by comedilib.
+<P>COMEDILIB_LOGLEVEL=4
+<P>Comedilib prints a lot of debugging messages.
+<P>
+<P>
+<P>
+<H2><A NAME="ss6.2">6.2 comedi_open</A>
+</H2>
+
+<P>
+<P>comedi_t *comedi_open(char *fn);
+<P>Opens a comedi device specified by the filename fn.  Returns NULL
+on error.  Returns a handle that is given as a parameter to other
+comedilib functions.
+<P>You are not supposed to have access to the structure comedi_t.
+<P>void comedi_close(comedi_t *it);
+<P>Closes a device previously opened by comedi_open().
+<P>void comedi_perror(const char *s);
+char *comedi_strerror(int errnum);
+int comedi_errno(void);
+<P>When a comedilib function fails, it usually returns -1 or
+NULL, depending on the return type.  An internal library
+variable stores an error number, which can be retrieved with
+<CODE>comedi_errno()</CODE>.  This error number can be
+converted to a human-readable form by the functions
+<CODE>comedi_perror()</CODE> and <CODE>comedi_strerror()</CODE>.
+<P>These functions are intended to mimic the behavior of the
+standard C library functions <CODE>perror()</CODE>,
+<CODE>strerror</CODE>, and <CODE>errno()</CODE>.  In particular,
+comedilib functions sometimes return an error that is generated
+inside the C library; the comedi error message in this case
+is the same as the C library.
+<P>The function <CODE>comedi_perror()</CODE> prints an error
+message to stderr.  The error message consists of the
+argument string, a colon, a space, a description of the error
+condition, and a new line.
+<P>The function <CODE>comedi_strerror()</CODE> returns a pointer to a
+character string
+describing the comedilib error <CODE>errnum</CODE>.  The persistence
+of the returned pointer is undefined, and should not be trusted
+after the next comedilib call.  An unrecognized error number will
+return a pointer to the string "undefined error", or similar.
+<P>The function <CODE>comedi_errno()</CODE>
+returns an integer describing the most recent comedilib error.  This
+integer may be used as the <CODE>errnum</CODE> parameter for
+<CODE>comedi_strerror()</CODE>.
+<P>
+<P>
+<H2><A NAME="ss6.3">6.3 comedi_fileno()</A>
+</H2>
+
+<P>
+<P>int comedi_fileno(comedi_t *it);
+<P>The function <CODE>comedi_fileno</CODE>
+returns the integer descriptor for the handle <CODE>it</CODE>.  If
+<CODE>it</CODE> is an invalid <CODE>comedi_t</CODE> pointer, the function
+returns -1 and sets the appropriate comedilib error value.
+<P>
+<P>
+<P>
+<H2><A NAME="ss6.4">6.4 comedi_get_n_subdevices()</A>
+</H2>
+
+<P>
+<P>int comedi_get_n_subdevices(comedi_t *it);
+<P>The function <CODE>comedi_get_n_subdevices</CODE> returns the
+number of subdevices associated with the comedi descriptor
+<CODE>it</CODE>, or -1 if there is an error.
+<P>
+<P>
+<H2><A NAME="ss6.5">6.5 comedi_get_version_code()</A>
+</H2>
+
+<P>
+<P>int comedi_get_version_code(comedi_t *it);
+<P>The function <CODE>comedi_get_version_code()</CODE> returns the
+version code of the currently running comedi module.  The version
+code is of the form 0x010203, which is the version code for
+version 1.2.3.
+<P>
+<P>
+<H2><A NAME="ss6.6">6.6 comedi_get_driver_name()</A>
+</H2>
+
+<P>
+<P>char *comedi_get_driver_name(comedi_t *it);
+<P>The function <CODE>comedi_get_driver_name</CODE> returns a pointer
+to a string containing the name of the driver being used by comedi
+for the comedi device represented by <CODE>it</CODE>.  This pointer is
+valid until the comedi descriptor <CODE>it</CODE> is closed.  This
+function returns NULL if there is an error.
+<P>
+<P>
+<H2><A NAME="ss6.7">6.7 comedi_get_board_name()</A>
+</H2>
+
+<P>
+<P>char *comedi_get_board_name(comedi_t *it);
+<P>The function <CODE>comedi_get_board_name</CODE> returns a pointer
+to a string containing the name of the device.  This pointer is
+valid until the comedi descriptor <CODE>it</CODE> is closed.  This
+function returns NULL if there is an error.
+<P>
+<P>
+<H2><A NAME="ss6.8">6.8 comedi_get_subdevice_type()</A>
+</H2>
+
+<P>
+<P>int comedi_get_subdevice_type(comedi_t *it,unsigned int subdevice);
+<P>The function <CODE>comedi_get_subdevice_type()</CODE> returns an
+integer describing the type of subdevice that belongs to the comedi
+device <CODE>it</CODE> and has the index <CODE>subdevice</CODE>.  The
+function returns -1 is there is an error.
+<P>Valid subdevice types are:
+<P>
+<UL>
+<LI><CODE>COMEDI_SUBD_UNUSED</CODE>
+Subdevice has no functionality, i.e., a place-holder.</LI>
+<LI><CODE>COMEDI_SUBD_AI</CODE> Analog input</LI>
+<LI><CODE>COMEDI_SUBD_AO</CODE> Analog output</LI>
+<LI><CODE>COMEDI_SUBD_DI</CODE> Digital input</LI>
+<LI><CODE>COMEDI_SUBD_DO</CODE> Digital output</LI>
+<LI><CODE>COMEDI_SUBD_DIO</CODE>
+Digital input/output.  Channels are configurable as to whether they
+are inputs or outputs.</LI>
+<LI><CODE>COMEDI_SUBD_COUNTER</CODE>    Counter</LI>
+<LI><CODE>COMEDI_SUBD_TIMER</CODE>      Timer</LI>
+<LI><CODE>COMEDI_SUBD_MEMORY</CODE>
+Memory, e.g., EEPROM or dual-ported RAM</LI>
+<LI><CODE>COMEDI_SUBD_CALIB</CODE>
+Calibration DACs</LI>
+<LI><CODE>COMEDI_SUBD_PROC</CODE>
+Processor or DSP</LI>
+</UL>
+<P>
+<P>
+<P>
+<H2><A NAME="ss6.9">6.9 comedi_find_subdevice_by_type()</A>
+</H2>
+
+<P>
+<P>int comedi_find_subdevice_by_type(comedi_t *it,int type,unsigned int start_subdevice)
+<P>The function <CODE>comedi_find_subdevice_by_type</CODE> tries to
+locate a subdevice belonging to comedi device <CODE>it</CODE>,
+having type <CODE>type</CODE>, starting with the subdevice
+<CODE>start_subdevice</CODE>.  If it finds the requested subdevice,
+it returns its index.  If it does not locate the requested
+subdevice, it returns -1 and sets the comedi error number to
+"subdevice not found".  If there is an error, the function
+returns -1 and sets the appropriate error.
+<P>For subdevice types, see the manual page for the function
+<CODE>comedi_get_subdevice_type()</CODE>.
+<P>
+<P>
+<H2><A NAME="ss6.10">6.10 comedi_get_n_channels()</A>
+</H2>
+
+<P>
+<P>
+<P>int comedi_get_n_channels(comedi_t *it,unsigned int subdevice);
+<P>The function <CODE>comedi_get_n_channels()</CODE> returns the number
+of channels of the subdevice belonging to the comedi device <CODE>it</CODE>
+and having index <CODE>subdevice</CODE>.  This function returns -1 on error.
+<P>
+<P>
+<H2><A NAME="ss6.11">6.11 comedi_get_maxdata()</A>
+</H2>
+
+<P>
+<P>lsampl_t comedi_get_maxdata(comedi_t *it,unsigned int subdevice,unsigned int chan);
+<P>The function <CODE>comedi_get_maxdata()</CODE> returns the maximum
+valid data value for channel <CODE>chan</CODE> of subdevice
+<CODE>subdevice</CODE> belonging to the comedi device <CODE>it</CODE>
+This function returns 0 on error.
+<P>
+<P>
+<H2><A NAME="ss6.12">6.12 comedi_get_rangetype()</A>
+</H2>
+
+<P>
+<P>int comedi_get_rangetype(comedi_t *it,unsigned int subdevice,unsigned int chan);
+<P>The function <CODE>comedi_get_rangetype()</CODE> returns an integer
+that represents the number of range specifications available for a
+particular channel, as well as a conversion table to convert sample
+values to/from physical units.  The macro
+<CODE>RANGE_LENGTH(rangetype)</CODE>
+can be used to determine the number of range specifications for a given
+range type.
+<P>
+<P>
+<H2><A NAME="ss6.13">6.13 comedi_get_range()</A>
+</H2>
+
+<P>
+<P>comedi_range * comedi_get_range(comedi_t *it,unsigned int subdevice,unsigned int chan,unsigned int range);
+<P>The function <CODE>comedi_get_range</CODE> returns a pointer to a
+comedi_range structure that contains information that can be used to
+convert sample values to or from physical units.  The pointer is valid
+until the comedi device <CODE>it</CODE> is closed.  If there is an
+error, NULL is returned.
+<P>
+<P>
+<H2><A NAME="ss6.14">6.14 comedi_trigger()</A>
+</H2>
+
+<P>
+<P>int comedi_trigger(comedi_t *it,comedi_trig *trig);
+<P>The function <CODE>comedi_trigger()</CODE> instructs comedi to
+perform the command specified by the trigger structure
+<CODE>trig</CODE>.  Results depend on the particular command
+being issued.  If there is an error, -1 is returned.
+<P>Complete information about comedi commands is given in the
+manual page comedi(8).
+<P>double comedi_to_phys(lsampl_t data,comedi_range *rng,lsampl_t maxdata);
+lsampl_t comedi_from_phys(double data,comedi_range *rng,lsampl_t maxdata);
+<P>The functions <CODE>comedi_to_phys()</CODE> and
+<CODE>comedi_from_phys()</CODE> convert sample values to/from physical
+units.  The parameter <CODE>rng</CODE> represents the conversion
+information to use, and the parameter <CODE>maxdata</CODE> represents
+the maximum possible data value for the channel that the data was read/
+will be written to.
+<P>
+<P>
+<P>
+<H2><A NAME="ss6.15">6.15 comedi_data_read()</A>
+</H2>
+
+<P>
+<P>int comedi_data_read(comedi_t *it,unsigned int subd,unsigned int chan,
+unsigned int range,unsigned int aref,lsampl_t *data);
+int comedi_data_write(comedi_t *it,unsigned int subd,unsigned int chan,
+unsigned int range,unsigned int aref,lsampl_t data);
+<P>These functions read or write a single sample on the channel that
+is specified by the comedi device <CODE>it</CODE>, the
+subdevice <CODE>subd</CODE>, and the channel <CODE>chan</CODE>.
+For the operation,
+the device is configured to use range specification
+<CODE>range</CODE> and (if appropriate) analog reference type
+<CODE>aref</CODE>.  Analog reference types that are not supported
+by the device are silently ignored.
+<P>The function <CODE>comedi_data_read()</CODE> reads one data value from
+the specified channel and places the 
+data value that is read in the location pointed to by
+<CODE>data</CODE>.
+<P>The function <CODE>comedi_data_write()</CODE> writes the data value
+specified by the argument <CODE>data</CODE> to
+the specified channel.
+<P>On sucess, these functions return 0.  If there is an error, -1 is
+returned.
+<P>Valid analog reference numbers are:
+<P>
+<UL>
+<LI>AREF_GROUND Reference to analog ground</LI>
+<LI>AREF_COMMON Reference to analog common</LI>
+<LI>AREF_DIFF           Differential reference</LI>
+<LI>AREF_OTHER  Board-specific meaning</LI>
+</UL>
+<P>Valid data values used by these functions is an unsigned integer
+less than or equal to <CODE>maxdata</CODE>, which is channel-dependent.
+Conversion of these data values to physical units can be performed
+by <CODE>comedi_to_phys()</CODE> and <CODE>comedi_from_phys()</CODE>.
+<P>
+<P>
+<P>
+<H2><A NAME="ss6.16">6.16 comedi_sv_init()</A>
+</H2>
+
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+int comedi_sv_init(comedi_sv_t *it,comedi_t *dev,unsigned int subd,unsigned int chan);
+int comedi_sv_update(comedi_sv_t *it);
+int comedi_sv_measure(comedi_sv_t *it,double *data);
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>The special functions <CODE>comedi_sv_*()</CODE> are designed to
+make it easy to accurately measure slowly varying analog inputs.
+A slowly
+varying input is one that is effectively constant over the course
+of approximately 100 A/D conversions.  However, since these
+conversions can sometimes be pre-empted by scheduling, for most
+purposes, a slowly varying signal should be effectively constant
+for greater than 20 ms (the default Linux timeslice).
+<P>By averaging many A/D conversions of a relatively constant
+signal, it is possible to get a better measurement of the signal
+than a single A/D conversion.  In general, the uncertainty of the
+measurement decreases as the square root of the number of samples.
+This is limited by the rate that which the signal varies, and
+ultimately by the spurious free dynamic range of the A/D converter.
+<P>
+<P>
+<P>
+<H2><A NAME="ss6.17">6.17 comedi_get_timer()</A>
+</H2>
+
+<P>
+<P>
+<BLOCKQUOTE><CODE>
+<PRE>
+int comedi_get_timer(comedi_t *it,unsigned int subdev,double freq,unsigned int *trigvar,
+        double *actual_freq);
+</PRE>
+</CODE></BLOCKQUOTE>
+<P>
+<P>
+<P>
+<P>
+<HR>
+Next
+<A HREF="comedilib-5.html">Previous</A>
+<A HREF="comedilib.html#toc6">Contents</A>
+</BODY>
+</HTML>
diff --git a/doc/html/comedilib.html b/doc/html/comedilib.html
new file mode 100644 (file)
index 0000000..490972c
--- /dev/null
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+<HEAD>
+ <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
+ <TITLE>Comedi Documentation</TITLE>
+ <LINK HREF="comedilib-1.html" REL=next>
+
+
+</HEAD>
+<BODY>
+<A HREF="comedilib-1.html">Next</A>
+Previous
+Contents
+<HR>
+<H1>Comedi Documentation</H1>
+
+<H2>David Schleef, <CODE>ds@stm.lbl.gov</CODE></H2>
+<P>
+<H2><A NAME="toc1">1.</A> <A HREF="comedilib-1.html">Introduction</A></H2>
+
+<P>
+<H2><A NAME="toc2">2.</A> <A HREF="comedilib-2.html">Installation and configuration</A></H2>
+
+<UL>
+<LI><A HREF="comedilib-2.html#ss2.1">2.1 Compiling and Installing</A>
+<LI><A HREF="comedilib-2.html#ss2.2">2.2 Insmod'ding the kernel module</A>
+<LI><A HREF="comedilib-2.html#ss2.3">2.3 Configuring comedi for your hardware</A>
+<LI><A HREF="comedilib-2.html#ss2.4">2.4 Getting information from comedi</A>
+</UL>
+<P>
+<H2><A NAME="toc3">3.</A> <A HREF="comedilib-3.html">Individual drivers</A></H2>
+
+<UL>
+<LI><A HREF="comedilib-3.html#ss3.1">3.1 National Instruments AT-MIO E series</A>
+<LI><A HREF="comedilib-3.html#ss3.2">3.2 Data Translation</A>
+</UL>
+<P>
+<H2><A NAME="toc4">4.</A> <A HREF="comedilib-4.html">Writing programs that use comedi and comedilib</A></H2>
+
+<UL>
+<LI><A HREF="comedilib-4.html#ss4.1">4.1 Your first comedi program</A>
+<LI><A HREF="comedilib-4.html#ss4.2">4.2 Converting samples to voltages</A>
+<LI><A HREF="comedilib-4.html#ss4.3">4.3 Another section</A>
+<LI><A HREF="comedilib-4.html#ss4.4">4.4 Your second comedi program</A>
+</UL>
+<P>
+<H2><A NAME="toc5">5.</A> <A HREF="comedilib-5.html">Application-specific functions</A></H2>
+
+<UL>
+<LI><A HREF="comedilib-5.html#ss5.1">5.1 Digital Input/Output</A>
+<LI><A HREF="comedilib-5.html#ss5.2">5.2 Timed Input/Output</A>
+<LI><A HREF="comedilib-5.html#ss5.3">5.3 Slowly-varying inputs</A>
+</UL>
+<P>
+<H2><A NAME="toc6">6.</A> <A HREF="comedilib-6.html">Comedilib reference</A></H2>
+
+<UL>
+<LI><A HREF="comedilib-6.html#ss6.1">6.1 comedi_loglevel()</A>
+<LI><A HREF="comedilib-6.html#ss6.2">6.2 comedi_open</A>
+<LI><A HREF="comedilib-6.html#ss6.3">6.3 comedi_fileno()</A>
+<LI><A HREF="comedilib-6.html#ss6.4">6.4 comedi_get_n_subdevices()</A>
+<LI><A HREF="comedilib-6.html#ss6.5">6.5 comedi_get_version_code()</A>
+<LI><A HREF="comedilib-6.html#ss6.6">6.6 comedi_get_driver_name()</A>
+<LI><A HREF="comedilib-6.html#ss6.7">6.7 comedi_get_board_name()</A>
+<LI><A HREF="comedilib-6.html#ss6.8">6.8 comedi_get_subdevice_type()</A>
+<LI><A HREF="comedilib-6.html#ss6.9">6.9 comedi_find_subdevice_by_type()</A>
+<LI><A HREF="comedilib-6.html#ss6.10">6.10 comedi_get_n_channels()</A>
+<LI><A HREF="comedilib-6.html#ss6.11">6.11 comedi_get_maxdata()</A>
+<LI><A HREF="comedilib-6.html#ss6.12">6.12 comedi_get_rangetype()</A>
+<LI><A HREF="comedilib-6.html#ss6.13">6.13 comedi_get_range()</A>
+<LI><A HREF="comedilib-6.html#ss6.14">6.14 comedi_trigger()</A>
+<LI><A HREF="comedilib-6.html#ss6.15">6.15 comedi_data_read()</A>
+<LI><A HREF="comedilib-6.html#ss6.16">6.16 comedi_sv_init()</A>
+<LI><A HREF="comedilib-6.html#ss6.17">6.17 comedi_get_timer()</A>
+</UL>
+<HR>
+<A HREF="comedilib-1.html">Next</A>
+Previous
+Contents
+</BODY>
+</HTML>
diff --git a/doc/tutorial b/doc/tutorial
new file mode 100644 (file)
index 0000000..b468e36
--- /dev/null
@@ -0,0 +1,432 @@
+
+Comedi tutorial
+
+0.  Compiling and Installing
+0.  Insmod'ding the kernel module
+0.  Configuring comedi to use your hardware
+0.  Getting information from comedi
+0.  Your first comedi program
+0.  Converting samples to voltages
+
+
+
+0.  Compiling and Installing
+
+needs to be written
+
+
+
+
+0.  Insmod'ding the kernel module
+
+needs to be written
+
+
+
+0.  Configuring comedi to use your hardware
+
+
+I assume that your hardware device is in your computer, and that
+you know the relevant details about it, i.e., what kind of card
+it is, the I/O base, the IRQ, jumper settings related to input
+ranges, etc.
+
+To tell the comedi kernel module that you have a particular device, and
+some information about it, you will be running the 'comedi_config'
+command.  Perhaps you should read the man page now.
+
+For this tutorial, I have two devices, a National Instruments AT-MIO-16E-10
+and a Data Translation DT2821-F-8DI.
+
+The NI board is plug-and-play, and the man page tells me that I need
+to configure the PnP part of the board with isapnptools.  The isapnptools
+package is a little cryptic, but the concepts are simple.  Once I
+learned how to use it, I settled on a /etc/isapnp.conf file that
+contained the lines:
+
+
+# ANSI string -->National Instruments, AT-MIO-16E-10<--
+(CONFIGURE NIC2400/10725401 (LD 0
+       (IO 0 (BASE 0x0260))
+       (INT 0 (IRQ 3 (MODE +E)))
+#      (DMA 0 (CHANNEL 5))
+#      (DMA 1 (CHANNEL 6))
+       (ACT Y)
+))
+
+
+It also contains a few lines about overall configuration and about my
+sound card.  I found out after a bit of trial-and-error that the NI
+board does not always work with interrupts other than IRQ 3.  YMMV.
+Currently, the driver doesn't use DMA, but it may in the future, so
+I commented out the DMA lines.  It is a curious fact that the device
+ignores the IRQ and DMA information given here, however, I keep the
+information here to remind myself that the numbers aren't arbitrary.
+
+When I run comedi_config (as root, of course), I provide the same
+information.  Since I want to have the board configured every time
+I boot, I put the line
+
+   /usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,3
+
+into /etc/rc.d/rc.local.  You can, of course, run this command at
+a command prompt.  The man page tells me that the option list
+is supposed to be "<I/O base>,<IRQ>", so I used the same numbers
+as I put in /etc/isapnp.conf, i.e., 0x260,3.
+
+For the Data Translation board, I need to have a list of the
+jumper settings.  Fortunately, I wrote them all down in the
+manual -- I hope they are still correct.  However, I had to
+open the case to figure out which board in the series I had.
+It is a DT2821-f-8di.  The man page of comedi_config tells
+me that I need to know the I/O base, IRQ, DMA 1, DMA 2.  However,
+since I wrote the driver, I know that it also recognizes the
+differential/single-ended and unipolar/bipolar jumpers.  As always,
+the source is the final authority, and looking in module/dt282x.c
+tells me that the options list is interpreted as:
+
+        i/o base
+        irq
+        1=differential, 0=single ended
+        ai 0=unipolar, 1=bipolar
+        ao0 0=unipolar, 1=bipolar
+        ao1 0=unipolar, 1=bipolar
+        dma1
+        dma2
+
+(ai=analog input, ao=analog output.)  From this, I decide that
+the appropriate options list is
+
+       0x200,4,,1,1,1
+
+I left the differential/single-ended number blank, since the
+driver already knowns (from the board name), that it is
+differential.  I  also left the DMA numbers blank, since I
+don't want the driver to use DMA.  (Don't want it to interfere
+with my sound card -- life is full of difficult choices.)
+Keep in mind that things commented in the source, but not in
+the documentation are about as likely to change as the weather,
+so I put good comments next to the following line when I put
+it in rc.local.
+
+   /usr/sbin/comedi_config /dev/comedi1 dt2821-f-8di 0x200,4,,1,1,1
+
+So now I think that I have my boards configured correctly.
+Since data acquisition boards are not typically well-engineered,
+comedi sometimes can't figure out if the board is actually there.
+If it can't, it assumes you are right.  Both of these boards
+are well-made, so comedi will give me an error message if it
+can't find them.  The comedi kernel module, since it is a part
+of the kernel, prints messages to the kernel logs, which you
+can access through the command 'dmesg' or /var/log/messages.
+Here is a configuration failure (from dmesg):
+
+  comedi0: ni_E: 0x0200 can't find board
+
+When it does work, I get:
+
+  comedi0: ni_E: 0x0260 at-mio-16e-10 ( irq = 3 )
+
+Note that it also correctly identified my board.
+
+
+
+
+0.  Getting information from comedi
+
+
+So now that we have comedi talking to the hardware, we want to
+talk to comedi.  Here's some pretty low-level information --
+it's sometimes useful for debugging:
+
+   cat /proc/comedi
+
+Right now, on my computer, this command gives:
+
+   comedi version 0.6.4
+   format string
+    0: atmio-E              at-mio-16e-10           7
+    1: dt282x               dt2821-f-8di            4
+
+This is a feature that is not well-developed yet.  Basically, it
+currently tells you driver name, device name, and number of
+subdevices.
+
+In the demo/ directory, there is a command called
+'info', which provides information about each subdevice on the
+board.  The output of it is rather long, since I have 7
+subdevices  (4 or fewer is more common.)
+Here's part of the output of the NI board (which
+is on /dev/comedi0.)  ('demo/info /dev/comedi0')
+
+overall info:
+  version code: 0x000604
+  driver name: atmio-E
+  board name: at-mio-16e-10
+  number of subdevices: 7
+subdevice 0:
+  type: 1 (unknown)
+  number of channels: 16
+  max data value: 4095
+
+
+The overall info gives information about the device -- basically
+the same information as /proc/comedi.
+
+This board has 7 subdevices.  Devices are separated into
+subdevices that each have a distinct purpose -- e.g., analog
+input, analog output, digital input/output.  This board also
+has an EEPROM and calibration DACs that are also subdevices.
+
+Subdevice 0 is the analog input subdevice.  You would have
+known this from the 'type: 1 (unknown)' line, if I've updated
+demo/info recently, because it would say 'type: 1 (analog input)'
+instead.  The other lines should be self-explanitory.  Comedi
+has more information about the device, but demo/info doesn't
+currently display this.
+
+
+
+0.  Your first comedi program
+
+This example requires a card that has analog or
+digital input.  Right to the source:
+
+#include <stdio.h>     /* for printf() */
+#include <comedilib.h>
+
+int subdev = 0;                /* change this to your input subdevice */
+int chan = 0;          /* change this to your channel */
+int range = 0;         /* more on this later */
+int aref = AREF_GROUND;        /* more on this later */
+
+int main(int argc,char *argv[])
+{
+       comedi_t *it;
+       int chan=0;
+       lsampl_t data;
+       
+       it=comedi_open("/dev/comedi0");
+       
+       comedi_data_read(it,subdev,chan,range,aref,&data);
+       
+       printf("%d\n",data);
+       
+       return 0;
+}
+
+
+Should be understandable.  Open the device, get the data,
+print it out.  This is basically the guts of demo/inp.c,
+without error checking or fancy options.  Including all
+the appropriate headers is sometimes a little tricky.
+Compile it using 'cc tut1.c -lcomedi -o tut1'.  Hopefully
+it works.
+
+A few notes:  The range variable tells comedi which gain
+to use when measuring an analog voltage.  Since we don't
+know (yet) which numbers are valid, or what each means,
+we'll use 0, because it won't cause errors.  Likewise with
+aref, which determines the analog reference used.
+
+
+0.  Converting samples to voltages
+
+
+If you selected an analog input subdevice, you should notice
+that the output of tut1 is a number between
+0 and 4095, or 0 and 65535, depending on the number of bits
+in the A/D converter.  Comedi samples are *always* unsigned,
+with 0 representing the lowest voltage of the ADC, and 4095
+the highest.  The hardware driver compensates for
+anything else the manual for your device says.  However,
+you probably prefer to have this number translated to
+a voltage.  Naturally, as a good programmer, your first
+question is: "How do I do this in a device-independent
+manner?"
+
+For each subdevice, the comedi kernel module keeps a
+'range_type' variable.  This variable contains the number
+of available ranges (i.e., gains) that you can select,
+along with an offset in a list of range information
+structures.  If you know the range_type variable, you
+can use these macros:
+
+   RANGE_OFFSET(range_type)
+   RANGE_LENGTH(range_type)
+
+to extract such information.  However, you want the
+actual voltage information, not some integer offset
+in a table.  Rather than messing with the library
+internals, use the function
+
+   ptr=comedi_get_range(comedi_file,subdevice,channel,
+       range)
+
+which returns a pointer to a comedi_range structure.
+The comedi_range structure looks like
+
+   typedef struct{
+           double min;
+           double max;
+           unsigned int unit;
+   }comedi_range;
+
+As you might expect, ptr[range] is for range 'range',
+which you provided to comedi_data_read() above.  'min' represents
+the voltage corresponding to comedi_data_read() returning 0,
+and 'max' represents comedi_data_read() returning 'maxdata',
+(i.e., 4095 for 12 bit A/C converters, 65535 for 16 bit,
+or, 1 for digital input -- more on this in a bit.)  The
+'unit' entry tells you if min and
+max refer to voltage, current, etc.
+
+"Could it get easier?", you say.  Well, yes.  Use
+the function comedi_to_phys(), which converts data
+values to physical units.  Call it using something like
+
+   volts=comedi_to_phys(it,data,range,maxdata);
+
+and the opposite
+
+   data=comedi_from_phys(it,volts,range,maxdata);
+
+You probably noticed (and were worried) that we haven't
+discussed how to determine maxdata and range_type.  Well,
+you could ask the kernel this information each time you need
+it, but since there are other variables, special cases,
+and several subdevices to worry about, it would be nice
+if the library could take care of this...  (read on...)
+
+
+
+0.
+
+
+In addition to providing low level routines for data
+access, the comedi library provides higher-level access,
+much like the standard C library provides fopen(), etc.
+as a high-level (and portable) alternative to the direct
+UNIX system calls open(), etc.  Similarily to fopen(),
+we have comedi_open():
+
+       file=comedi_open("/dev/comedi0");
+
+where file is of type (comedi_t *).  This function
+calls open(), like we did explicitly in a previous
+section, but also fills the comedi_t structure with
+lots of goodies -- information that we will need to use
+soon.
+
+Specifically, we needed to know maxdata for a specific
+subdevice/channel.  How about:
+
+   maxdata=comedi_get_maxdata(file,subdevice,channel);
+
+Wow.  How easy.  And the range type?
+
+   range_type=comedi_get_rangetype(file,subdevice,channel);
+
+Cool.  Other information you need to know about a channel
+can be gotten in a similar way.
+
+
+
+0. Your second comedi program
+
+
+Actually, this is the first comedi program again, just
+that we've added what we've learned.
+
+
+#include <stdio.h>      /* for printf() */
+#include <comedi.h>     /* also included by comedilib.h */
+#include <comedilib.h>  /* for comedi_get() */
+
+int subdev = 0;         /* change this to your input subdevice */
+int chan = 0;           /* change this to your channel */
+int range = 0;          /* more on this later */
+int aref = 0;           /* more on this later */
+
+int main(int argc,char *argv[])
+{
+        comedi_t *cf;
+        int chan=0;
+        int data;
+       int maxdata,rangetype;
+       double volts;
+
+        cf=comedi_open("/dev/comedi0");
+
+       maxdata=comedi_get_maxdata(cf,subdev,chan);
+
+       rangetype=comedi_get_rangetype(cf,subdev,chan);
+
+        data=comedi_get(cf->fd,subdev,chan,range,aref);
+
+       volts=comedi_to_phys(data,rangetype,range,maxdata);
+
+        printf("%d %g\n",data,volts);
+
+        return 0;
+}
+
+
+By now, the comedi_read_data() line looks a little archaic, using
+the UNIX file descriptor cf->fd instead of just cf.  (By the
+way, somewhere in the heart of comedi_open() is the line
+cf->fd=open(filename,O_RDWR).)  Well, there isn't one good
+replacement, since it highly depends on your application
+what additional features you might want in a comedi_get()
+replacement.  But this is the topic of a different section.
+
+
+0. stuff
+
+
+
+0. Slowly-varying inputs
+
+
+Sometimes, your input channels change slowly enough that
+you are able to average many sucessive input values to get a
+more accurate measurement of the actual value.  In general,
+the more samples you average, the better your estimate
+gets, roughly by a factor of sqrt(number_of_samples).
+Obviously, there are limitations to this:
+
+ - you are ultimately limited by "spurious free dynamic range"
+
+ - you need to have _some_ noise on the input channel,
+   otherwise you will be averaging the same number N times.
+
+ - the more noise you have, the greater your SFDR, but it
+   takes many more samples to compensate for the increased
+   noise
+
+ - if you feel the need to average samples for 2 seconds,
+   your signal will need to be _very_ slowly-varying, i.e.,
+   not varying more than your target uncertainty for the
+   entire 2 seconds.
+
+As you might have guessed, the comedi library has functions
+to help you in your quest to accurately measure slowly varying
+inputs.  I use these functions to measure thermocouple voltages
+-- actually, the library functions came from a section of code
+that was previously part of the thermocouple reading program.
+
+The comedi self-calibration utility also uses these functions.
+On some hardware, it is possible to tell it to measure an
+internal stable voltage reference, which is typically going
+to be very slowly varying -- on the kilosecond time scale
+or more.  So it is reasonable to measure millions of samples,
+to get a very accurate measurement of the A/D converter output
+value that corresponds to the voltage reference.  Sometimes,
+however, this is overkill, since there is no need to
+perform a part-per-million calibration to a standard that
+is only accurate to part-per-thousand.
+
+
+
+
+
diff --git a/include/comedilib.h b/include/comedilib.h
new file mode 100644 (file)
index 0000000..2949165
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+    include/comedilib.h
+    header file for the comedi library routines
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1998 David A. Schleef <ds@stm.lbl.gov>
+
+    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 _COMEDILIB_H
+#define _COMEDILIB_H
+
+#include <comedi.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct comedi_t_struct comedi_t;
+
+typedef struct{
+       double min;
+       double max;
+       unsigned int unit;
+}comedi_range;
+
+typedef struct comedi_sv_struct{
+       comedi_t *dev;
+       unsigned int subdevice;
+       unsigned int chan;
+
+       /* range policy */
+       int range;
+       int aref;
+       
+       /* number of measurements to average (for ai) */
+       int n;
+
+       lsampl_t maxdata;
+}comedi_sv_t;
+
+
+
+
+comedi_t *comedi_open(const char *fn);
+void comedi_close(comedi_t *it);
+
+int comedi_loglevel(int loglevel);
+void comedi_perror(const char *s);
+char *comedi_strerror(int errnum);
+int comedi_errno(void);
+int comedi_fileno(comedi_t *it);
+
+/* queries */
+
+int comedi_get_n_subdevices(comedi_t *it);
+int comedi_get_version_code(comedi_t *it);
+char *comedi_get_driver_name(comedi_t *it);
+char *comedi_get_board_name(comedi_t *it);
+
+int comedi_get_subdevice_type(comedi_t *it,unsigned int subdevice);
+int comedi_find_subdevice_by_type(comedi_t *it,int type,unsigned int subd);
+int comedi_get_n_channels(comedi_t *it,unsigned int subdevice);
+lsampl_t comedi_get_maxdata(comedi_t *it,unsigned int subdevice,unsigned int chan);
+int comedi_get_rangetype(comedi_t *it,unsigned int subdevice,unsigned int chan);
+comedi_range * comedi_get_range(comedi_t *it,unsigned int subdevice,unsigned int chan,unsigned int range);
+int comedi_find_range(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int unit,double min,double max);
+int comedi_get_n_ranges(comedi_t *it,unsigned int subdevice,unsigned int chan);
+
+/* triggers and commands */
+
+int comedi_cancel(comedi_t *it,unsigned int subdevice);
+int comedi_trigger(comedi_t *it,comedi_trig *trig);
+int comedi_command(comedi_t *it,comedi_cmd *cmd);
+
+/* physical units */
+
+double comedi_to_phys(lsampl_t data,comedi_range *rng,lsampl_t maxdata);
+lsampl_t comedi_from_phys(double data,comedi_range *rng,lsampl_t maxdata);
+
+/* synchronous stuff */
+
+int comedi_data_read(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int range,
+       unsigned int aref,lsampl_t *data);
+int comedi_data_write(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int range,
+       unsigned int aref,lsampl_t data);
+
+/* slowly varying stuff */
+
+int comedi_sv_init(comedi_sv_t *it,comedi_t *dev,unsigned int subd,unsigned int chan);
+int comedi_sv_update(comedi_sv_t *it);
+int comedi_sv_measure(comedi_sv_t *it,double *data);
+
+/* dio config */
+
+int comedi_dio_config(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int dir);
+int comedi_dio_read(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int *bit);
+int comedi_dio_write(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int bit);
+int comedi_dio_bitfield(comedi_t *it,unsigned int subd,unsigned int write_mask,
+       unsigned int *bits);
+
+/* timer stuff */
+
+int comedi_get_timer(comedi_t *it,unsigned int subdev,double freq,unsigned int *trigvar,
+       double *actual_freq);
+
+int comedi_timed_1chan(comedi_t *it,unsigned int subdev,unsigned int chan,unsigned int range,
+       unsigned int aref,double freq,unsigned int n_samples,double *data);
+
+
+/*
+   The following prototypes are ALPHA, indicating that they might change
+   in future releases of comedilib, without regard to backward compatibility.
+ */
+
+enum comedi_oor_behavior {
+       COMEDI_OOR_NUMBER = 0,
+       COMEDI_OOR_NAN
+};
+
+enum comedi_oor_behavior comedi_set_global_oor_behavior(enum comedi_oor_behavior behavior);
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644 (file)
index 0000000..99af04d
--- /dev/null
@@ -0,0 +1,16 @@
+
+CFLAGS=-fPIC -Wall -O2 -I../include -I.
+
+OBJS=comedi.o timer.o sv.o range.o ioctl.o filler.o timed.o error.o \
+       dio.o data.o get.o
+
+libcomedi.a: $(OBJS)
+       #gcc -shared -Wl,-soname,libcomedi.so,-T,version_script -o libcomedi.so.${VERSION_CODE} $(OBJS) -lm
+       gcc -shared -Wl,-soname,libcomedi.so -o libcomedi.so.${VERSION_CODE} $(OBJS) -lm
+       ar rs libcomedi.a $(OBJS)
+       ln -sf libcomedi.so.${VERSION_CODE} libcomedi.so
+
+clean:
+       rm -f libcomedi.a libcomedi.so* *.o
+
+
diff --git a/lib/comedi.c b/lib/comedi.c
new file mode 100644 (file)
index 0000000..f4d7aa5
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+    lib/comedi.c
+    comedi library routines
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+    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.
+
+*/
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+int __comedi_init=0;
+
+void initialize(void)
+{
+       char *s;
+       
+       __comedi_init=1;
+
+       if( (s=getenv("COMEDILIB_LOGLEVEL")) ){
+               sscanf(s,"%d",&__comedi_loglevel);
+               fprintf(stderr,"setting loglevel to %d\n",__comedi_loglevel);
+       }
+}
+  
+comedi_t *comedi_open(const char *fn)
+{
+       comedi_t *it;
+
+       if(!__comedi_init)
+               initialize();
+
+       if(!(it=malloc(sizeof(comedi_t))))
+               goto cleanup;
+       memset(it,0,sizeof(comedi_t));
+
+       if((it->fd=open(fn,O_RDWR))<0){
+               libc_error();
+               goto cleanup;
+       }
+
+       if(ioctl_devinfo(it->fd,&it->devinfo)<0)
+               goto cleanup;
+
+       it->n_subdevices=it->devinfo.n_subdevs;
+
+       get_subdevices(it);
+
+       it->magic=COMEDILIB_MAGIC;
+
+       return it;
+cleanup:
+       if(it)
+               free(it);
+
+       return NULL;
+}
+
+#if 0
+/* this is an example of how we do versioned symbols */
+__asm__(".symver comedi_open_0,comedi_open@");
+#endif
+
+void comedi_close(comedi_t *it)
+{
+       it->magic=0;
+
+       /* XXX should free all memory */
+
+       close(it->fd);
+}
+
+int comedi_cancel(comedi_t *it,unsigned int subdevice)
+{
+       return ioctl(it->fd,COMEDI_CANCEL,subdevice);
+}
+
+int comedi_poll(comedi_t *it,unsigned int subdevice)
+{
+#ifdef HAVE_COMEDI_POLL
+       return ioctl(it->fd,COMEDI_POLL,subdevice);
+#else
+       return -1;
+#endif
+}
+
+int comedi_fileno(comedi_t *it)
+{
+       if(!it)
+               return -1;
+
+       return it->fd;
+}
+
+int comedi_trigger(comedi_t *it,comedi_trig *t)
+{
+       if(!it || !t)
+               return -1;
+
+       return ioctl_trigger(it->fd,t);
+}
+
+int comedi_command(comedi_t *it,comedi_cmd *t)
+{
+#ifdef HAVE_COMEDI_CMD
+       if(!it || !t)
+               return -1;
+
+       return ioctl_cmd(it->fd,t);
+#else
+       return -1;
+#endif
+}
+
+
+
diff --git a/lib/data.c b/lib/data.c
new file mode 100644 (file)
index 0000000..aa4922e
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+    lib/data.c
+    comedi library routines
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+    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.
+
+*/
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+
+int comedi_data_write(comedi_t *it,unsigned int subdev,unsigned int chan,unsigned int range,
+               unsigned int aref,lsampl_t data)
+{
+       comedi_trig cmd={
+               mode:           0,
+               flags:          TRIG_WRITE,
+               n_chan:         1,
+               n:              1,
+               trigsrc:        0,
+               trigvar:        0,
+               trigvar1:       0,
+       };
+       sampl_t sdata=data;
+
+       if(!valid_chan(it,subdev,chan))
+               return -1;
+       
+       chan=CR_PACK(chan,range,aref);
+
+       cmd.subdev=subdev;
+       if(it->subdevices[subdev].subd_flags & SDF_LSAMPL){
+               cmd.data=(sampl_t *)(&data);
+       }else{
+               cmd.data=&sdata;
+       }
+       cmd.chanlist=&chan;
+
+       return ioctl_trigger(it->fd,&cmd);
+}
+
+
+int comedi_data_read(comedi_t *it,unsigned int subdev,unsigned int chan,unsigned int range,
+               unsigned int aref,lsampl_t *data)
+{
+       comedi_trig cmd={
+               mode:           0,
+               flags:          0,
+               n_chan:         1,
+               n:              1,
+               trigsrc:        0,
+               trigvar:        0,
+               trigvar1:       0,
+       };
+       int ret;
+       sampl_t sdata;
+
+       if(!valid_chan(it,subdev,chan))
+               return -1;
+       
+       chan=CR_PACK(chan,range,aref);
+       
+       cmd.subdev=subdev;
+       cmd.chanlist=&chan;
+       if(it->subdevices[subdev].subd_flags & SDF_LSAMPL){
+               cmd.data=(sampl_t *)data;
+       }else{
+               cmd.data=&sdata;
+       }
+
+       ret=ioctl_trigger(it->fd,&cmd);
+       if(ret<0)
+               return ret;
+
+       if(!(it->subdevices[subdev].subd_flags & SDF_LSAMPL)){
+               *data=sdata;
+       }
+
+       return 0;
+}
+
+#if 1
+/*
+   I don't like this function, which is why it is marked out.
+   The problem is the sampl_t/lsampl_t fiasco, which is beginning
+   to be a PITA.
+ */
+int comedi_data_read_n(comedi_t *it,unsigned int subdev,unsigned int chan,unsigned int range,
+               unsigned int aref,unsigned int n,lsampl_t *data)
+{
+       comedi_trig cmd;
+       unsigned int i;
+       int ret;
+
+       if(!valid_chan(it,subdev,chan))
+               return -1;
+       
+       if(n==0)
+               return 0;
+
+       chan=CR_PACK(chan,range,aref);
+       
+       memset(&cmd,0,sizeof(cmd));
+       cmd.mode=0;
+       cmd.n_chan=1;
+       cmd.subdev=subdev;
+       cmd.chanlist=&chan;
+
+       i=0;
+       while(i<n){
+               cmd.data=(void *)(data+i);
+               cmd.n=n-i;
+
+               ret=ioctl_trigger(it->fd,&cmd);
+               if(ret<0)
+                       goto out;
+
+               i+=ret;
+       }
+out:
+       if(i==0)return -1;
+
+       return (int)i;
+}
+#endif
+
+
+
diff --git a/lib/dio.c b/lib/dio.c
new file mode 100644 (file)
index 0000000..ac2581c
--- /dev/null
+++ b/lib/dio.c
@@ -0,0 +1,152 @@
+/*
+    lib/dio.c
+    comedi library routines
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+    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.
+
+*/
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+int comedi_dio_config(comedi_t *it,unsigned int subdev,unsigned int chan,unsigned int io)
+{
+       comedi_trig trig;
+       lsampl_t data=io;
+
+       if(!valid_chan(it,subdev,chan))
+               return -1;
+       
+       if(it->subdevices[subdev].type!=COMEDI_SUBD_DIO)
+               return -1;
+
+       if(io!=COMEDI_INPUT && io!=COMEDI_OUTPUT)
+               return -1;
+
+       memset(&trig,0,sizeof(trig));
+       trig.flags=TRIG_CONFIG|TRIG_WRITE;
+       trig.n_chan=1;
+       trig.n=1;
+       trig.subdev=subdev;
+       trig.chanlist=&chan;
+       trig.data=(sampl_t *)&data;
+
+       return ioctl_trigger(it->fd,&trig);
+}
+
+int comedi_dio_read(comedi_t *it,unsigned int subdev,unsigned int chan,
+       unsigned int *val)
+{
+       comedi_trig trig;
+       lsampl_t data;
+       int ret;
+
+       if(!valid_chan(it,subdev,chan))
+               return -1;
+
+       if(it->subdevices[subdev].type!=COMEDI_SUBD_DIO &&
+          it->subdevices[subdev].type!=COMEDI_SUBD_DO &&
+          it->subdevices[subdev].type!=COMEDI_SUBD_DI)
+               return -1;
+
+       memset(&trig,0,sizeof(trig));
+       trig.n_chan=1;
+       trig.n=1;
+       trig.subdev=subdev;
+       trig.chanlist=&chan;
+       trig.data=(sampl_t *)&data;
+
+       ret=ioctl_trigger(it->fd,&trig);
+
+       if(ret>=0 && val)*val=data;
+
+       return ret;
+}
+
+int comedi_dio_write(comedi_t *it,unsigned int subdev,unsigned int chan,
+       unsigned int val)
+{
+       comedi_trig trig;
+       lsampl_t data;
+
+       if(!valid_chan(it,subdev,chan))
+               return -1;
+       
+       if(it->subdevices[subdev].type!=COMEDI_SUBD_DIO &&
+          it->subdevices[subdev].type!=COMEDI_SUBD_DO)
+               return -1;
+
+       data=val;
+
+       memset(&trig,0,sizeof(trig));
+       trig.n_chan=1;
+       trig.n=1;
+       trig.flags=TRIG_WRITE;
+       trig.subdev=subdev;
+       trig.chanlist=&chan;
+       trig.data=(sampl_t *)&data;
+
+       return ioctl_trigger(it->fd,&trig);
+}
+
+int comedi_dio_bitfield(comedi_t *it,unsigned int subdev,unsigned int mask,unsigned int *bits)
+{
+       int ret;
+       unsigned int i,n_chan;
+       unsigned int m,bit;
+       subdevice *s;
+
+       if(!valid_subd(it,subdev))
+               return -1;
+
+       if(it->subdevices[subdev].type!=COMEDI_SUBD_DIO &&
+          it->subdevices[subdev].type!=COMEDI_SUBD_DO &&
+          it->subdevices[subdev].type!=COMEDI_SUBD_DI)
+               return -1;
+       
+       s=it->subdevices+subdev;
+
+       n_chan=comedi_get_n_channels(it,subdev);
+       if(n_chan>32)n_chan=32;
+       for(i=0,m=1;i<n_chan;i++,m<<=1){
+               if(mask&m){
+                       bit=(*bits&m)?1:0;
+                       ret=comedi_dio_write(it,subdev,i,bit);
+               }else{
+                       ret=comedi_dio_read(it,subdev,i,&bit);
+                       if(bit) *bits|=m;
+                       else (*bits)&=~m;
+               }
+               if(ret<0)return ret;
+       }
+
+       return (int)n_chan;
+}
+
diff --git a/lib/error.c b/lib/error.c
new file mode 100644 (file)
index 0000000..2f8fbfa
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+    lib/error.c
+    error handling routines
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+    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.
+
+*/
+
+#include <libinternal.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+char *__comedilib_error_strings[]={
+       "No error",
+       "Unknown error",
+       "Bad comedi_t structure",
+       "Invalid subdevice",
+       "Invalid channel",
+};
+
+int __comedi_loglevel=1;
+int __comedi_errno=0;
+
+int comedi_loglevel(int loglevel)
+{
+       int old_loglevel=__comedi_loglevel;
+       
+       __comedi_loglevel=loglevel;
+       
+       return old_loglevel;
+}
+
+int comedi_errno(void)
+{
+       return __comedi_errno;
+}
+
+char *comedi_strerror(int errnum)
+{
+       if(errnum<COMEDILIB_NOERROR ||
+          errnum>=COMEDILIB_NOERROR+sizeof(__comedilib_error_strings)
+          /sizeof(__comedilib_error_strings[0]))
+               return strerror(errnum);
+
+       return __comedilib_error_strings[errnum-COMEDILIB_NOERROR];
+}
+
+void comedi_perror(const char *s)
+{
+       if(__comedi_loglevel>=3){
+               fprintf(stderr,"comedi_perror(): __comedi_errno=%d\n",__comedi_errno);
+       }
+       if(!s)s="comedilib";
+       fprintf(stderr,"%s: %s\n",s,comedi_strerror(__comedi_errno));
+}
+
+void libc_error(void)
+{
+       __comedi_errno=errno;
+       if(__comedi_loglevel>=2){
+               comedi_perror("libc error");
+       }
+}
+
+void internal_error(int err)
+{
+       __comedi_errno=err;
+       if(__comedi_loglevel>=2){
+               comedi_perror("internal error");
+       }
+}
+
+
+
+int valid_dev(comedi_t *it)
+{
+       if(!it || it->magic!=COMEDILIB_MAGIC){
+               internal_error(COMEDILIB_BADDEV);
+               return 0;
+       }
+       
+       return 1;
+}
+
+int valid_subd(comedi_t *it,unsigned int subd)
+{
+       if(!valid_dev(it))return 0;
+       if(subd>=it->n_subdevices){
+               internal_error(COMEDILIB_BADSUBD);
+               return 0;
+       }
+       
+       return 1;
+}
+
+int valid_chan(comedi_t *it,unsigned int subd,unsigned int chan)
+{
+       if(!valid_subd(it,subd))return 0;
+       if(chan>=it->subdevices[subd].n_chan){
+               internal_error(COMEDILIB_BADCHAN);
+               return 0;
+       }
+       
+       return 1;
+}
+
+
diff --git a/lib/filler.c b/lib/filler.c
new file mode 100644 (file)
index 0000000..c657fe4
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+    lib/filler.c
+    comedi library routines
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+    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.
+
+*/
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+/* these functions download information from the comedi module. */
+
+
+
+int get_subdevices(comedi_t *it)
+{
+       int i,j;
+       int ret;
+       comedi_subdinfo *s;
+       subdevice *r;
+
+       s=malloc(sizeof(comedi_subdinfo)*it->n_subdevices);
+       ret=ioctl_subdinfo(it->fd,s);
+       debug_int(ret);
+
+       r=it->subdevices=realloc(it->subdevices,
+               sizeof(subdevice)*it->n_subdevices);
+       debug_ptr(r);
+       memset(r,0,sizeof(subdevice)*it->n_subdevices);
+
+       for(i=0;i<it->n_subdevices;i++){
+               r[i].type       = s[i].type;
+               r[i].n_chan     = s[i].n_chan;
+               r[i].subd_flags = s[i].subd_flags;
+               r[i].timer_type = s[i].timer_type;
+               r[i].len_chanlist = s[i].len_chanlist;
+               r[i].maxdata    = s[i].maxdata;
+               r[i].flags      = s[i].flags;
+               r[i].range_type = s[i].range_type;
+
+               if(r[i].subd_flags&SDF_FLAGS){
+                       r[i].flags_list=malloc(sizeof(*r[i].flags_list)*r[i].n_chan);
+                       debug_ptr(r[i].flags_list);
+               }
+               if(r[i].subd_flags&SDF_MAXDATA){
+                       r[i].maxdata_list=malloc(sizeof(*r[i].maxdata_list)*r[i].n_chan);
+                       debug_ptr(r[i].maxdata_list);
+               }
+               if(r[i].subd_flags&SDF_RANGETYPE){
+                       r[i].range_type_list=malloc(sizeof(*r[i].range_type_list)*r[i].n_chan);
+                       debug_ptr(r[i].range_type_list);
+               }
+               ret=ioctl_chaninfo(it->fd,i,r[i].maxdata_list,r[i].flags_list,r[i].range_type_list);
+               debug_int(ret);
+
+               if(r[i].subd_flags&SDF_RANGETYPE){
+                       r[i].rangeinfo_list=malloc(sizeof(*r[i].rangeinfo_list)*r[i].n_chan);
+                       debug_ptr(r[i].rangeinfo_list);
+                       for(j=0;j<r[i].n_chan;j++){
+                               r[i].rangeinfo_list[j]=get_rangeinfo(it->fd,r[i].range_type_list[j]);
+                       }
+               }else{
+                       r[i].rangeinfo=get_rangeinfo(it->fd,r[i].range_type);
+               }
+       }
+
+       free(s);
+
+       return 0;
+}
+
+comedi_range *get_rangeinfo(int fd,unsigned int range_type)
+{
+       comedi_krange *kr;
+       comedi_range *r;
+       int i;
+
+       kr=malloc(sizeof(comedi_krange)*RANGE_LENGTH(range_type));
+       r=malloc(sizeof(comedi_range)*RANGE_LENGTH(range_type));
+
+       ioctl_rangeinfo(fd,range_type,kr);
+
+       for(i=0;i<RANGE_LENGTH(range_type);i++){
+               r[i].min=kr[i].min*1e-6;
+               r[i].max=kr[i].max*1e-6;
+               r[i].unit=RF_UNIT(kr[i].flags);
+       }
+       free(kr);
+
+       return r;
+}
+
+
diff --git a/lib/get.c b/lib/get.c
new file mode 100644 (file)
index 0000000..0b9fbe3
--- /dev/null
+++ b/lib/get.c
@@ -0,0 +1,145 @@
+/*
+    lib/get.c
+    comedi library routines
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+    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.
+
+*/
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+int comedi_get_n_subdevices(comedi_t *it)
+{
+       if(!valid_dev(it))
+               return -1;
+
+       return it->n_subdevices;
+}
+
+int comedi_get_version_code(comedi_t *it)
+{
+       if(!valid_dev(it))
+               return -1;
+
+       return it->devinfo.version_code;
+}
+
+char *comedi_get_driver_name(comedi_t *it)
+{
+       if(!valid_dev(it))
+               return NULL;
+
+       return it->devinfo.driver_name;
+}
+
+char *comedi_get_board_name(comedi_t *it)
+{
+       if(!valid_dev(it))
+               return NULL;
+
+       return it->devinfo.board_name;
+}
+
+int comedi_get_subdevice_type(comedi_t *it,unsigned int subd)
+{
+       if(!valid_dev(it))
+               return -1;
+
+       return it->subdevices[subd].type;
+}
+
+int comedi_find_subdevice_by_type(comedi_t *it,int type,unsigned int subd)
+{
+       if(!valid_subd(it,subd))
+               return -1;
+
+       for(;subd<it->n_subdevices;subd++){
+               if(it->subdevices[subd].type==type)
+                       return subd;
+       }
+       return -1;
+}
+
+
+int comedi_get_n_channels(comedi_t *it,unsigned int subd)
+{
+       if(!valid_subd(it,subd))
+               return -1;
+
+       return it->subdevices[subd].n_chan;
+}
+
+
+/* */
+
+lsampl_t comedi_get_maxdata(comedi_t *it,unsigned int subdevice,unsigned int chan)
+{
+       if(!valid_chan(it,subdevice,chan))
+               return 0;
+
+       if(it->subdevices[subdevice].maxdata_list)
+               return it->subdevices[subdevice].maxdata_list[chan];
+
+       return it->subdevices[subdevice].maxdata;
+}
+
+int comedi_get_rangetype(comedi_t *it,unsigned int subdevice,unsigned int chan)
+{
+       if(!valid_chan(it,subdevice,chan))
+               return -1;
+
+       if(it->subdevices[subdevice].range_type_list)
+               return it->subdevices[subdevice].range_type_list[chan];
+
+       return it->subdevices[subdevice].range_type;
+}
+
+
+comedi_range * comedi_get_range(comedi_t *it,unsigned int subdevice,unsigned int chan,unsigned int range)
+{
+       int range_type;
+
+       if(!valid_chan(it,subdevice,chan))
+               return NULL;
+
+       range_type=comedi_get_rangetype(it,subdevice,chan);
+
+       if(range>=RANGE_LENGTH(range_type))
+               return NULL;
+
+       if(it->subdevices[subdevice].rangeinfo_list)
+               return it->subdevices[subdevice].rangeinfo_list[chan]+range;
+
+       return it->subdevices[subdevice].rangeinfo+range;
+}
+
+
+
diff --git a/lib/ioctl.c b/lib/ioctl.c
new file mode 100644 (file)
index 0000000..781c07a
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+    lib/ioctl.c
+    comedi library routines
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+    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.
+
+*/
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+/* ioctl wrappers */
+
+int ioctl_devinfo(int fd,comedi_devinfo *it)
+{
+       return ioctl(fd,COMEDI_DEVINFO,it);
+}
+
+int ioctl_subdinfo(int fd,comedi_subdinfo *it)
+{
+       return ioctl(fd,COMEDI_SUBDINFO,it);
+}
+
+int ioctl_chaninfo(int fd,unsigned int subdev,lsampl_t *maxdata_list,unsigned int *flaglist,unsigned int *rangelist)
+{
+       comedi_chaninfo ci;
+
+       ci.subdev=subdev;
+       ci.flaglist=flaglist;
+       ci.rangelist=rangelist;
+       ci.maxdata_list=maxdata_list;
+
+       return ioctl(fd,COMEDI_CHANINFO,&ci);
+}
+
+int ioctl_trigger(int fd,comedi_trig *it)
+{
+       return ioctl(fd,COMEDI_TRIG,it);
+}
+
+#ifdef HAVE_COMEDI_CMD
+int ioctl_cmd(int fd,comedi_cmd *it)
+{
+       return ioctl(fd,COMEDI_CMD,it);
+}
+#endif
+
+int ioctl_lock(int fd,int subdevice)
+{
+       return ioctl(fd,COMEDI_LOCK,subdevice);
+}
+
+int ioctl_unlock(int fd,int subdevice)
+{
+       return ioctl(fd,COMEDI_UNLOCK,subdevice);
+}
+
+int ioctl_cancel(int fd,int subdevice)
+{
+       return ioctl(fd,COMEDI_CANCEL,subdevice);
+}
+
+int ioctl_rangeinfo(int fd,int range_type,comedi_krange *range_ptr)
+{
+       comedi_rangeinfo it;
+
+       it.range_type=range_type;
+       it.range_ptr=range_ptr;
+
+       return ioctl(fd,COMEDI_RANGEINFO,&it);
+}
+
+
diff --git a/lib/libinternal.h b/lib/libinternal.h
new file mode 100644 (file)
index 0000000..89dd314
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+    lib/libinternal.h
+    header file for comedilib internals
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1998 David A. Schleef <ds@stm.lbl.gov>
+
+    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 _LIBINTERNAL_H
+#define _LIBINTERNAL_H
+
+#include <comedilib.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+
+#define debug_ptr(a)    if(!(a))fprintf(stderr," ** NULL pointer: " __FILE__ ", line %d\n",__LINE__);
+#define debug_int(a)    if((a)<0)fprintf(stderr," ** error: " __FILE__ ", line %d\n",__LINE__);
+
+#define COMEDILIB_MAGIC 0xc001dafe
+
+
+extern int __comedi_init;
+extern int __comedi_loglevel;
+extern int __comedi_errno;
+
+#if 0
+
+#define libc_error()           (__comedi_errno=errno)
+#define internal_error(a)      (__comedi_errno=(a))
+
+#else
+
+void libc_error(void);
+void internal_error(int error_number);
+
+#endif
+
+
+typedef struct subdevice_struct subdevice;
+typedef struct device_struct device;
+
+struct comedi_t_struct{
+       int magic;
+
+       int fd;
+       int n_subdevices;
+
+       comedi_devinfo devinfo;
+
+       subdevice *subdevices;
+};
+
+struct subdevice_struct{
+       unsigned int type;
+       unsigned int n_chan;
+       unsigned int subd_flags;
+       unsigned int timer_type;
+       unsigned int len_chanlist;
+       lsampl_t maxdata;
+       unsigned int flags;
+       unsigned int range_type;
+
+       lsampl_t *maxdata_list;
+       unsigned int *range_type_list;
+       unsigned int *flags_list;
+
+       comedi_range *rangeinfo;
+       comedi_range **rangeinfo_list;
+};
+
+
+
+/* ioctl wrappers */
+
+int ioctl_devinfo(int fd,comedi_devinfo *it);
+int ioctl_subdinfo(int fd,comedi_subdinfo *it);
+int ioctl_chaninfo(int fd,unsigned int subdev,lsampl_t *maxdata_list,
+                               unsigned int *flaglist,unsigned int *rangelist);
+int ioctl_trigger(int fd,comedi_trig *it);
+int ioctl_cmd(int fd,comedi_cmd *it);
+int ioctl_lock(int fd,int subdevice);
+int ioctl_unlock(int fd,int subdevice);
+int ioctl_cancel(int fd,int subdevice);
+int ioctl_rangeinfo(int fd,int range_type,comedi_krange *range_ptr);
+
+/* filler routines */
+
+int get_subdevices(comedi_t *it);
+comedi_range *get_rangeinfo(int fd,unsigned int range_type);
+
+/* validators */
+
+int valid_dev(comedi_t *it);
+int valid_subd(comedi_t *it,unsigned int subdevice);
+int valid_chan(comedi_t *it,unsigned int subdevice,unsigned int chan);
+
+enum{
+       COMEDILIB_NOERROR = 0x1000,
+       COMEDILIB_UNKNOWN,
+       COMEDILIB_BADDEV,
+       COMEDILIB_BADSUBD,
+       COMEDILIB_BADCHAN,
+};
+
+
+/* comedi version compatibility */
+
+#ifndef TRIG_WRITE
+#define TRIG_WRITE     0x0040
+#endif
+
+#ifdef COMEDI_CMD
+#define HAVE_COMEDI_CMD
+#endif
+
+
+
+#endif
+
diff --git a/lib/range.c b/lib/range.c
new file mode 100644 (file)
index 0000000..3452761
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+    lib/range.c
+    comedi library routines for voltage ranges
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+    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.
+
+*/
+
+#include <stdio.h>
+
+#define __USE_GNU
+
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+/* sometimes we can't find a definition of NAN */
+
+#ifndef        NAN
+#define        NAN \
+  (__extension__ ((union { unsigned char __c[8];                             \
+                          double __d; })                                     \
+                 { { 0, 0, 0, 0, 0, 0, 0xf8, 0x7f } }).__d)
+#endif
+
+
+static enum comedi_oor_behavior comedi_oor_is_nan = COMEDI_OOR_NAN;
+
+enum comedi_oor_behavior comedi_set_global_oor_behavior(
+       enum comedi_oor_behavior behavior)
+{
+       int old_behavior=comedi_oor_is_nan;
+
+       comedi_oor_is_nan=behavior;
+
+       return old_behavior;
+}
+
+
+double comedi_to_phys(lsampl_t data,comedi_range *rng,lsampl_t maxdata)
+{
+       double x;
+
+       if(!rng)return NAN;
+       if(!maxdata)return NAN;
+
+       if(comedi_oor_is_nan==COMEDI_OOR_NAN && (data==0 || data==maxdata))
+               return NAN;
+
+       x=data;
+       x/=maxdata;
+       x*=(rng->max-rng->min);
+       x+=rng->min;
+
+       return x;
+}
+
+lsampl_t comedi_from_phys(double data,comedi_range *rng,lsampl_t maxdata)
+{
+       double s;
+
+       if(!rng)return 0;
+       if(!maxdata)return 0;
+
+       s=(data-rng->min)/(rng->max-rng->min)*maxdata;
+       if(s<0)return 0;
+       if(s>maxdata)return maxdata;
+
+       return (lsampl_t)(floor(s+0.5));
+}
+
+int comedi_find_range(comedi_t *it,unsigned int subd,unsigned int chan,unsigned int unit,double min,double max)
+{
+       unsigned int range_type;
+       int best;
+       comedi_range *range_ptr,*best_ptr;
+       int i;
+       
+       if(!valid_chan(it,subd,chan))return -1;
+
+       range_type=comedi_get_rangetype(it,subd,chan);
+       best=-1;
+       best_ptr=NULL;
+       for(i=0;i<RANGE_LENGTH(range_type);i++){
+               range_ptr=comedi_get_range(it,subd,chan,i);
+               if(range_ptr->min<=min && range_ptr->max>=max){
+                       if(best<0 || (range_ptr->max-range_ptr->min) < 
+                          (best_ptr->max-best_ptr->min)){
+                               best=i;
+                               best_ptr=range_ptr;
+                       }
+               }
+       }
+       return best;
+}
+
+int comedi_get_n_ranges(comedi_t *it,unsigned int subd,unsigned int chan)
+{
+       unsigned int range_type;
+
+       if(!valid_chan(it,subd,chan))return -1;
+
+       range_type=comedi_get_rangetype(it,subd,chan);
+       return RANGE_LENGTH(range_type);
+}
+
diff --git a/lib/sv.c b/lib/sv.c
new file mode 100644 (file)
index 0000000..2145690
--- /dev/null
+++ b/lib/sv.c
@@ -0,0 +1,188 @@
+/*
+    lib/sv.c
+    comedi library routines - sv section
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+    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.
+
+*/
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+
+int sv_measure_l(comedi_sv_t *it,double *data);
+int sv_measure_s(comedi_sv_t *it,double *data);
+
+
+int comedi_sv_init(comedi_sv_t *it,comedi_t *dev,unsigned int subd,unsigned int chan)
+{
+       if(!valid_chan(dev,subd,chan))return -1;
+       if(!it)return -1;
+
+       memset(it,0,sizeof(*it));
+
+       it->dev=dev;
+       it->subdevice=subd;
+       it->chan=chan;
+       it->n=100;
+
+       return comedi_sv_update(it);
+}
+
+int comedi_sv_update(comedi_sv_t *it)
+{
+       if(!it)return -1;
+       if(!valid_chan(it->dev,it->subdevice,it->chan))return -1;
+
+       it->maxdata=comedi_get_maxdata(it->dev,it->subdevice,it->chan);
+
+       /* check current range policy */
+       /* check n */
+
+       return 0;
+}
+
+int comedi_sv_measure(comedi_sv_t *it,double *data)
+{
+       if(it->dev->subdevices[it->subdevice].subd_flags & SDF_LSAMPL){
+               return sv_measure_l(it,data);
+       }else{
+               return sv_measure_s(it,data);
+       }
+}
+
+int sv_measure_l(comedi_sv_t *it,double *data)
+{
+       comedi_trig t;
+       int ret=0;
+       lsampl_t *val;
+       unsigned int chan;
+       comedi_range *rng;
+       double sum;
+       int i;
+       int n;
+
+       val=malloc(sizeof(*val)*it->n);
+
+       chan=CR_PACK(it->chan,it->range,it->aref);
+       
+       t.subdev=it->subdevice;
+       t.mode=0;
+       t.flags=TRIG_DITHER;
+       t.n_chan=1;
+       t.chanlist=&chan;
+       t.trigsrc=0;
+       t.trigvar=0;
+       t.trigvar1=0;
+
+       rng=comedi_get_range(it->dev,it->subdevice,it->chan,it->range);
+
+       for(n=0;n<it->n;){
+               t.data=(void *)(val+n);
+               t.n=it->n-n;
+               i=ioctl_trigger(it->dev->fd,&t);
+               if(i<=0){
+                       ret=i;
+                       goto out;
+               }
+               n+=i;
+       }
+
+       sum=0;
+       for(i=0;i<it->n;i++){
+               sum+=comedi_to_phys(val[i],rng,it->maxdata);
+       }
+       *data=sum/it->n;
+
+out:
+       free(val);
+
+       return ret;
+}
+
+/* yes, these functions are _almost_ exactly the same... */
+
+int sv_measure_s(comedi_sv_t *it,double *data)
+{
+       comedi_trig t;
+       int ret=0;
+       sampl_t *val;
+       unsigned int chan;
+       comedi_range *rng;
+       double sum;
+       int i;
+       int n;
+
+       val=malloc(sizeof(*val)*it->n);
+
+       chan=CR_PACK(it->chan,it->range,it->aref);
+       
+       t.subdev=it->subdevice;
+       t.mode=0;
+       t.flags=TRIG_DITHER;
+       t.n_chan=1;
+       t.chanlist=&chan;
+       t.data=val;
+       t.n=it->n;
+       t.trigsrc=0;
+       t.trigvar=0;
+       t.trigvar1=0;
+
+       rng=comedi_get_range(it->dev,it->subdevice,it->chan,it->range);
+
+       for(n=0;n<it->n;){
+               t.data=val+n;
+               t.n=it->n-n;
+               i=ioctl_trigger(it->dev->fd,&t);
+               if(i<=0){
+                       ret=i;
+                       goto out;
+               }
+               n+=i;
+       }
+
+       sum=0;
+       for(i=0;i<it->n;i++){
+               sum+=comedi_to_phys(val[i],rng,it->maxdata);
+       }
+       *data=sum/it->n;
+
+out:
+       free(val);
+
+       return ret;
+}
+
+
+
+
+
+
+
diff --git a/lib/timed.c b/lib/timed.c
new file mode 100644 (file)
index 0000000..432cb2e
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+    lib/sv.c
+    comedi library routines - sv section
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+    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.
+
+*/
+
+#include <libinternal.h>
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+
+#define BUFSZ 100
+
+int comedi_timed_1chan(comedi_t *dev,unsigned int subd,unsigned int chan,unsigned int range,
+       unsigned int aref,double freq,unsigned int n_samples,double *data)
+{
+       comedi_trig t;
+       double act_freq;
+       sampl_t *buffer;
+       comedi_range *the_range;
+       unsigned int maxdata;
+       int i,n,m;
+       
+       if(!valid_chan(dev,subd,chan))return -1;
+       if(!data)return -1;
+       
+       memset(&t,0,sizeof(t));
+       
+       /* check range */
+
+       chan=CR_PACK(chan,range,aref);
+
+       t.subdev=subd;
+       t.mode=2;
+       t.n_chan=1;
+       t.chanlist=&chan;
+       t.n=n_samples;
+       comedi_get_timer(dev,subd,freq,&t.trigvar,&act_freq);
+       t.trigvar1=1;
+       
+       the_range=comedi_get_range(dev,subd,chan,range);
+       maxdata=comedi_get_maxdata(dev,subd,chan);
+       
+       buffer=malloc(sizeof(sampl_t)*BUFSZ);
+       if(!buffer)return -1;
+
+       comedi_trigger(dev,&t);
+       n=0;
+       while(n<n_samples){
+               m=n_samples-n;
+               if(m>BUFSZ)m=BUFSZ;
+               if((m=read(dev->fd,buffer,m*sizeof(sampl_t)))<0){
+                       /* ack! */
+                       return -1;
+               }
+               m/=sizeof(sampl_t);
+               for(i=0;i<m;i++){
+                       data[n+i]=comedi_to_phys(buffer[i],the_range,maxdata);
+               }
+               n+=m;
+       }
+
+       free(buffer);
+
+       return 0;
+}
+
diff --git a/lib/timer.c b/lib/timer.c
new file mode 100644 (file)
index 0000000..4129e13
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+    lib/comedi.c
+    comedi library routines
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
+
+    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.
+
+*/
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <comedi.h>
+#include <string.h>
+
+#include <libinternal.h>
+
+
+
+/* dt282x timer */
+
+static int dt282x_timer(double freq,unsigned int *trigvar,double *actual_freq)
+{
+       int divider,prescaler;
+       double basefreq=4e6;
+       
+       divider=floor(4e6/(freq));
+       prescaler=0;
+       while(divider>255){
+               prescaler++;
+               divider>>=1;
+               basefreq/=2;
+       }
+       if(prescaler==1){
+               prescaler++;
+               divider>>=1;
+               basefreq/=2;
+       }
+       if(prescaler>=16)return -1;
+       *trigvar=(prescaler<<8)|(255-divider);
+       *actual_freq=basefreq/divider;
+       
+       return 0;
+}
+
+
+/* dt2814 timer */
+static int dt2814_timer(double freq,unsigned int *trigvar,double *actual_freq)
+{
+       double f;
+       int  i;
+       
+       f=1e5;
+       for(i=0;i<8;i++){
+               if(f-freq<freq-f/10){
+                       *trigvar=i;
+                       *actual_freq=f;
+                       return 0;
+               }
+               f/=10;
+       }
+       *trigvar=i;
+       *actual_freq=f;
+       
+       return 0;
+}
+
+/* atmio/pcimio timer */
+static int atmio_timer(double freq,unsigned int *trigvar,double *actual_freq)
+{
+       unsigned int divider;
+       
+       divider=floor(20e6/(freq));
+       *actual_freq=20e6/divider;
+       *trigvar=divider-1;
+       
+       return 0;
+}
+
+/* acl8112 timer */
+static int acl8112_timer(double freq,unsigned int *trigvar,double *actual_freq)
+{
+       int divider,prescaler;
+       double basefreq=2e6;
+
+       /* XXX my notes say that the prescaler and divider cannot
+          be 1.  This needs to be checked.  --ds */
+       
+       /* Force at least one division to get something in CTR2. */
+       prescaler=1;
+       divider = basefreq/(freq);
+
+       while(divider>32767){
+               prescaler*=2;
+               divider>>=1;
+       }
+
+       *trigvar = (prescaler<<16) | divider;
+       *actual_freq=basefreq/(divider*prescaler);
+       
+       return 0;
+}
+
+/* nanosec timer */
+static int nanosec_timer(double freq,unsigned int *trigvar,double *actual_freq)
+{
+       *trigvar=(1e9/freq);
+       *actual_freq=1e9/(*trigvar);
+
+       return 0;
+}
+
+typedef int (*timerfunc)(double freq,unsigned int *trigvar,double *actual_freq);
+
+static timerfunc timer_functions[]={
+       NULL,
+       dt282x_timer,
+       dt2814_timer,
+       atmio_timer,
+       acl8112_timer,
+       nanosec_timer,
+};
+#define N_TIMERTYPES 6
+
+int comedi_get_timer(comedi_t *it,unsigned int subdev,double freq,
+       unsigned int *trigvar,double *actual_freq)
+{
+       int timer_type;
+       
+       if(!it || !trigvar || !actual_freq)
+               return -1;
+
+       timer_type=it->subdevices[subdev].timer_type;
+       
+       if(timer_type==0 || timer_type>=N_TIMERTYPES)
+               return -1;
+       
+       return (timer_functions[timer_type])(freq,trigvar,actual_freq);
+}
+
diff --git a/lib/version_script b/lib/version_script
new file mode 100644 (file)
index 0000000..9b2c0a7
--- /dev/null
@@ -0,0 +1,11 @@
+
+VERSION{
+
+v0.7.0{
+       global:
+               comedi_*;
+       local:
+               *;
+};
+
+}
diff --git a/perl/Changes b/perl/Changes
new file mode 100644 (file)
index 0000000..0b2cf7f
--- /dev/null
@@ -0,0 +1,7 @@
+Revision history for Perl extension Comedi.
+
+0.01  Tue Nov  9 01:17:02 1999
+       - original version; created by h2xs 1.18
+
+0.02  Tue Nov 16 11:49:11 EST 1999
+       - (Comedi::Lib only) track changes to comedilib 0.7.6 (jes)
diff --git a/perl/Comedi.pm b/perl/Comedi.pm
new file mode 100644 (file)
index 0000000..87e64b5
--- /dev/null
@@ -0,0 +1,289 @@
+# Copyright (c) 1999 Joseph E. Smith <jes@presto.med.upenn.edu>
+#
+# See the 'COPYRIGHT' section below for complete copyright information.
+
+package Comedi;
+
+use strict;
+use Carp;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD);
+
+require Exporter;
+require DynaLoader;
+require AutoLoader;
+
+@ISA = qw(Exporter DynaLoader);
+
+%EXPORT_TAGS = (
+
+ Constants => [ qw(
+       AREF_COMMON
+       AREF_DIFF
+       AREF_GROUND
+       AREF_OTHER
+       COMEDI_INPUT
+       COMEDI_MAJOR
+       COMEDI_NAMELEN
+       COMEDI_NDEVCONFOPTS
+       COMEDI_NDEVICES
+       COMEDI_OUTPUT
+ ) ],
+
+ Ioctls => [ qw(
+       COMEDI_CANCEL
+       COMEDI_CHANINFO
+       COMEDI_DEVCONFIG
+       COMEDI_DEVINFO
+       COMEDI_LOCK
+       COMEDI_RANGEINFO
+       COMEDI_SUBDINFO
+       COMEDI_TRIG
+       COMEDI_UNLOCK
+ ) ],
+
+ SubdeviceTypes => [ qw(
+       COMEDI_SUBD_AI
+       COMEDI_SUBD_AO
+       COMEDI_SUBD_CALIB
+       COMEDI_SUBD_COUNTER
+       COMEDI_SUBD_DI
+       COMEDI_SUBD_DIO
+       COMEDI_SUBD_DO
+       COMEDI_SUBD_MEMORY
+       COMEDI_SUBD_PROC
+       COMEDI_SUBD_TIMER
+       COMEDI_SUBD_UNUSED
+ ) ],
+
+ SubdeviceFlags => [ qw(
+       SDF_BUSY
+       SDF_BUSY_OWNER
+       SDF_COMMON
+       SDF_DEGLITCH
+       SDF_DIFF
+       SDF_DITHER
+       SDF_FLAGS
+       SDF_GROUND
+       SDF_INTERNAL
+       SDF_LOCKED
+       SDF_LOCK_OWNER
+       SDF_LSAMPL
+       SDF_MAXDATA
+       SDF_MMAP
+       SDF_MODE0
+       SDF_MODE1
+       SDF_MODE2
+       SDF_MODE3
+       SDF_MODE4
+       SDF_OTHER
+       SDF_RANGETYPE
+       SDF_READABLE
+       SDF_RT
+       SDF_RUNNING
+       SDF_WRITEABLE
+ ) ],
+
+ TriggerFlags => [ qw (
+       TRIG_BOGUS
+       TRIG_CONFIG
+       TRIG_DEGLITCH
+       TRIG_DITHER
+       TRIG_RT
+       TRIG_WAKE_EOS
+       TRIG_WRITE
+ ) ],
+
+ Units => [ qw (
+       UNIT_mA
+       UNIT_none
+       UNIT_volt
+ ) ],
+
+ Functions => [ qw(
+        CR_PACK
+ ) ],
+);
+
+
+
+# Items to export into callers namespace by default. Note: do not export
+# names by default without a very good reason. Use EXPORT_OK instead.
+# Do not simply export all your public functions/methods/constants.
+@EXPORT = ();
+
+@EXPORT_OK = (
+    @{$EXPORT_TAGS{'Functions'}},
+    @{$EXPORT_TAGS{'Constants'}},
+    @{$EXPORT_TAGS{'Ioctls'}},
+    @{$EXPORT_TAGS{'SubdeviceTypes'}},
+    @{$EXPORT_TAGS{'SubdeviceFlags'}},
+    @{$EXPORT_TAGS{'TriggerFlags'}},
+    @{$EXPORT_TAGS{'Units'}},
+);
+
+$VERSION = '0.02';
+
+sub AUTOLOAD {
+    # This AUTOLOAD is used to 'autoload' constants from the constant()
+    # XS function.  If a constant is not found then control is passed
+    # to the AUTOLOAD in AutoLoader.
+
+    my $constname;
+    ($constname = $AUTOLOAD) =~ s/.*:://;
+    my $val = constant($constname, @_ ? $_[0] : 0);
+    if ($! != 0) {
+       if ($! =~ /Invalid/) {
+           $AutoLoader::AUTOLOAD = $AUTOLOAD;
+           goto &AutoLoader::AUTOLOAD;
+       }
+       else {
+               croak "Your vendor has not defined Comedi macro $constname";
+       }
+    }
+    eval "sub $AUTOLOAD { $val }";
+    goto &$AUTOLOAD;
+}
+
+bootstrap Comedi $VERSION;
+
+# Preloaded methods go here.
+
+sub CR_PACK {
+    my ($chan, $rng, $aref) = @_;
+
+    return ( ((($aref)&0x3)<<24) | ((($rng)&0xff)<<16) | (($chan)&0xffff) );
+}
+
+
+# Autoload methods go after =cut, and are processed by the autosplit program.
+
+1;
+__END__
+
+=head1 NAME
+
+Comedi - Perl extension for data acquisition using comedi
+
+=head1 SYNOPSIS
+
+use Comedi qw( :Functions :Constants :Ioctls :SubdeviceTypes :SubdeviceFlags :TriggerFlags :Units );
+
+=head1 DESCRIPTION
+
+The B<Comedi> module provides constants and data types for using the comedi data acquisition module.
+
+By itself, this module will allow you to access the B<comedi> module using standard I/O functions and ioctls.
+For more robust access to B<comedi>, see the B<Comedi::Lib> module.
+
+=head1 Exported constants
+
+The large number of constants defined in C<comedi.h> are divided into
+several functional categories for export.  No constants are exported
+by default.
+
+=item Constants
+
+AREF_COMMON
+AREF_DIFF
+AREF_GROUND
+AREF_OTHER
+COMEDI_INPUT
+COMEDI_MAJOR
+COMEDI_NAMELEN
+COMEDI_NDEVCONFOPTS
+COMEDI_NDEVICES
+COMEDI_OUTPUT
+
+=item Ioctls
+
+COMEDI_CANCEL
+COMEDI_CHANINFO
+COMEDI_DEVCONFIG
+COMEDI_DEVINFO
+COMEDI_LOCK
+COMEDI_RANGEINFO
+COMEDI_SUBDINFO
+COMEDI_TRIG
+COMEDI_UNLOCK
+
+=item SubdeviceTypes
+
+COMEDI_SUBD_AI
+COMEDI_SUBD_AO
+COMEDI_SUBD_CALIB
+COMEDI_SUBD_COUNTER
+COMEDI_SUBD_DI
+COMEDI_SUBD_DIO
+COMEDI_SUBD_DO
+COMEDI_SUBD_MEMORY
+COMEDI_SUBD_PROC
+COMEDI_SUBD_TIMER
+COMEDI_SUBD_UNUSED
+
+=item  SubdeviceFlags
+
+SDF_BUSY
+SDF_BUSY_OWNER
+SDF_COMMON
+SDF_DEGLITCH
+SDF_DIFF
+SDF_DITHER
+SDF_FLAGS
+SDF_GROUND
+SDF_INTERNAL
+SDF_LOCKED
+SDF_LOCK_OWNER
+SDF_LSAMPL
+SDF_MAXDATA
+SDF_MMAP
+SDF_MODE0
+SDF_MODE1
+SDF_MODE2
+SDF_MODE3
+SDF_MODE4
+SDF_OTHER
+SDF_RANGETYPE
+SDF_READABLE
+SDF_RT
+SDF_RUNNING
+SDF_WRITEABLE
+
+=item TriggerFlags
+
+TRIG_BOGUS
+TRIG_CONFIG
+TRIG_DEGLITCH
+TRIG_DITHER
+TRIG_RT
+TRIG_WAKE_EOS
+TRIG_WRITE
+
+=item Units
+
+UNIT_mA
+UNIT_none
+UNIT_volt
+
+=item Functions
+
+CR_PACK
+
+=head1 VERSION
+
+Version 0.01 09-Nov-1999
+
+=head1 AUTHOR
+
+Joe Smith <F<jes@presto.med.upenn.edu>>.
+
+=head1 COPYRIGHT
+
+Copyright (c) 1999 Joseph E. Smith.  All rights reserved.  This
+program is free software.  You may redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+Comedi::Trigger(1), Comedi::Lib(1).
+
+=cut
diff --git a/perl/Comedi.xs b/perl/Comedi.xs
new file mode 100644 (file)
index 0000000..6b3862b
--- /dev/null
@@ -0,0 +1,621 @@
+/*
+ * Copyright (c) 1999 Joseph E. Smith <jes@presto.med.upenn.edu>
+ *
+ * All rights reserved.  This program is free software.  You may
+ * redistribute it and/or modify it under the same terms as Perl itself.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#ifdef __cplusplus
+}
+#endif
+
+#include <comedi.h>
+
+static int
+not_here(s)
+char *s;
+{
+    croak("%s not implemented on this architecture", s);
+    return -1;
+}
+
+static double
+constant(name, arg)
+char *name;
+int arg;
+{
+    errno = 0;
+    switch (*name) {
+    case 'A':
+       if (strEQ(name, "AREF_COMMON"))
+#ifdef AREF_COMMON
+           return AREF_COMMON;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "AREF_DIFF"))
+#ifdef AREF_DIFF
+           return AREF_DIFF;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "AREF_GROUND"))
+#ifdef AREF_GROUND
+           return AREF_GROUND;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "AREF_OTHER"))
+#ifdef AREF_OTHER
+           return AREF_OTHER;
+#else
+           goto not_there;
+#endif
+       break;
+    case 'B':
+       break;
+    case 'C':
+       if (strEQ(name, "CIO"))
+#ifdef CIO
+           return CIO;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_CANCEL"))
+#ifdef COMEDI_CANCEL
+           return COMEDI_CANCEL;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_CB_BLOCK"))
+#ifdef COMEDI_CB_BLOCK
+           return COMEDI_CB_BLOCK;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_CB_EOA"))
+#ifdef COMEDI_CB_EOA
+           return COMEDI_CB_EOA;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_CB_EOBUF"))
+#ifdef COMEDI_CB_EOBUF
+           return COMEDI_CB_EOBUF;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_CB_EOS"))
+#ifdef COMEDI_CB_EOS
+           return COMEDI_CB_EOS;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_CHANINFO"))
+#ifdef COMEDI_CHANINFO
+           return COMEDI_CHANINFO;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_CMD"))
+#ifdef COMEDI_CMD
+           return COMEDI_CMD;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_DEVCONFIG"))
+#ifdef COMEDI_DEVCONFIG
+           return COMEDI_DEVCONFIG;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_DEVINFO"))
+#ifdef COMEDI_DEVINFO
+           return COMEDI_DEVINFO;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_INPUT"))
+#ifdef COMEDI_INPUT
+           return COMEDI_INPUT;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_LOCK"))
+#ifdef COMEDI_LOCK
+           return COMEDI_LOCK;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_MAJOR"))
+#ifdef COMEDI_MAJOR
+           return COMEDI_MAJOR;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_NAMELEN"))
+#ifdef COMEDI_NAMELEN
+           return COMEDI_NAMELEN;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_NDEVCONFOPTS"))
+#ifdef COMEDI_NDEVCONFOPTS
+           return COMEDI_NDEVCONFOPTS;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_NDEVICES"))
+#ifdef COMEDI_NDEVICES
+           return COMEDI_NDEVICES;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_OUTPUT"))
+#ifdef COMEDI_OUTPUT
+           return COMEDI_OUTPUT;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_RANGEINFO"))
+#ifdef COMEDI_RANGEINFO
+           return COMEDI_RANGEINFO;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_SUBDINFO"))
+#ifdef COMEDI_SUBDINFO
+           return COMEDI_SUBDINFO;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_SUBD_AI"))
+#ifdef COMEDI_SUBD_AI
+           return COMEDI_SUBD_AI;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_SUBD_AO"))
+#ifdef COMEDI_SUBD_AO
+           return COMEDI_SUBD_AO;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_SUBD_CALIB"))
+#ifdef COMEDI_SUBD_CALIB
+           return COMEDI_SUBD_CALIB;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_SUBD_COUNTER"))
+#ifdef COMEDI_SUBD_COUNTER
+           return COMEDI_SUBD_COUNTER;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_SUBD_DI"))
+#ifdef COMEDI_SUBD_DI
+           return COMEDI_SUBD_DI;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_SUBD_DIO"))
+#ifdef COMEDI_SUBD_DIO
+           return COMEDI_SUBD_DIO;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_SUBD_DO"))
+#ifdef COMEDI_SUBD_DO
+           return COMEDI_SUBD_DO;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_SUBD_MEMORY"))
+#ifdef COMEDI_SUBD_MEMORY
+           return COMEDI_SUBD_MEMORY;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_SUBD_PROC"))
+#ifdef COMEDI_SUBD_PROC
+           return COMEDI_SUBD_PROC;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_SUBD_TIMER"))
+#ifdef COMEDI_SUBD_TIMER
+           return COMEDI_SUBD_TIMER;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_SUBD_UNUSED"))
+#ifdef COMEDI_SUBD_UNUSED
+           return COMEDI_SUBD_UNUSED;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_TRIG"))
+#ifdef COMEDI_TRIG
+           return COMEDI_TRIG;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "COMEDI_UNLOCK"))
+#ifdef COMEDI_UNLOCK
+           return COMEDI_UNLOCK;
+#else
+           goto not_there;
+#endif
+       break;
+    case 'D':
+       break;
+    case 'E':
+       break;
+    case 'F':
+       break;
+    case 'G':
+       break;
+    case 'H':
+       break;
+    case 'I':
+       break;
+    case 'J':
+       break;
+    case 'K':
+       break;
+    case 'L':
+       break;
+    case 'M':
+       break;
+    case 'N':
+       break;
+    case 'O':
+       break;
+    case 'P':
+       break;
+    case 'Q':
+       break;
+    case 'R':
+       if (strEQ(name, "RF_EXTERNAL"))
+#ifdef RF_EXTERNAL
+           return RF_EXTERNAL;
+#else
+           goto not_there;
+#endif
+       break;
+    case 'S':
+       if (strEQ(name, "SDF_BUSY"))
+#ifdef SDF_BUSY
+           return SDF_BUSY;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_BUSY_OWNER"))
+#ifdef SDF_BUSY_OWNER
+           return SDF_BUSY_OWNER;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_COMMON"))
+#ifdef SDF_COMMON
+           return SDF_COMMON;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_DEGLITCH"))
+#ifdef SDF_DEGLITCH
+           return SDF_DEGLITCH;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_DIFF"))
+#ifdef SDF_DIFF
+           return SDF_DIFF;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_DITHER"))
+#ifdef SDF_DITHER
+           return SDF_DITHER;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_FLAGS"))
+#ifdef SDF_FLAGS
+           return SDF_FLAGS;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_GROUND"))
+#ifdef SDF_GROUND
+           return SDF_GROUND;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_INTERNAL"))
+#ifdef SDF_INTERNAL
+           return SDF_INTERNAL;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_LOCKED"))
+#ifdef SDF_LOCKED
+           return SDF_LOCKED;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_LOCK_OWNER"))
+#ifdef SDF_LOCK_OWNER
+           return SDF_LOCK_OWNER;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_LSAMPL"))
+#ifdef SDF_LSAMPL
+           return SDF_LSAMPL;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_MAXDATA"))
+#ifdef SDF_MAXDATA
+           return SDF_MAXDATA;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_MMAP"))
+#ifdef SDF_MMAP
+           return SDF_MMAP;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_MODE0"))
+#ifdef SDF_MODE0
+           return SDF_MODE0;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_MODE1"))
+#ifdef SDF_MODE1
+           return SDF_MODE1;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_MODE2"))
+#ifdef SDF_MODE2
+           return SDF_MODE2;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_MODE3"))
+#ifdef SDF_MODE3
+           return SDF_MODE3;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_MODE4"))
+#ifdef SDF_MODE4
+           return SDF_MODE4;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_OTHER"))
+#ifdef SDF_OTHER
+           return SDF_OTHER;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_RANGETYPE"))
+#ifdef SDF_RANGETYPE
+           return SDF_RANGETYPE;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_READABLE"))
+#ifdef SDF_READABLE
+           return SDF_READABLE;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_RT"))
+#ifdef SDF_RT
+           return SDF_RT;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_RUNNING"))
+#ifdef SDF_RUNNING
+           return SDF_RUNNING;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "SDF_WRITEABLE"))
+#ifdef SDF_WRITEABLE
+           return SDF_WRITEABLE;
+#else
+           goto not_there;
+#endif
+       break;
+    case 'T':
+       if (strEQ(name, "TRIG_ANY"))
+#ifdef TRIG_ANY
+           return TRIG_ANY;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_BOGUS"))
+#ifdef TRIG_BOGUS
+           return TRIG_BOGUS;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_CONFIG"))
+#ifdef TRIG_CONFIG
+           return TRIG_CONFIG;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_COUNT"))
+#ifdef TRIG_COUNT
+           return TRIG_COUNT;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_DEGLITCH"))
+#ifdef TRIG_DEGLITCH
+           return TRIG_DEGLITCH;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_DITHER"))
+#ifdef TRIG_DITHER
+           return TRIG_DITHER;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_EXT"))
+#ifdef TRIG_EXT
+           return TRIG_EXT;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_FOLLOW"))
+#ifdef TRIG_FOLLOW
+           return TRIG_FOLLOW;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_INT"))
+#ifdef TRIG_INT
+           return TRIG_INT;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_NONE"))
+#ifdef TRIG_NONE
+           return TRIG_NONE;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_NOW"))
+#ifdef TRIG_NOW
+           return TRIG_NOW;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_RT"))
+#ifdef TRIG_RT
+           return TRIG_RT;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_TIME"))
+#ifdef TRIG_TIME
+           return TRIG_TIME;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_TIMER"))
+#ifdef TRIG_TIMER
+           return TRIG_TIMER;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_WAKE_EOS"))
+#ifdef TRIG_WAKE_EOS
+           return TRIG_WAKE_EOS;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "TRIG_WRITE"))
+#ifdef TRIG_WRITE
+           return TRIG_WRITE;
+#else
+           goto not_there;
+#endif
+       break;
+    case 'U':
+       if (strEQ(name, "UNIT_mA"))
+#ifdef UNIT_mA
+           return UNIT_mA;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "UNIT_none"))
+#ifdef UNIT_none
+           return UNIT_none;
+#else
+           goto not_there;
+#endif
+       if (strEQ(name, "UNIT_volt"))
+#ifdef UNIT_volt
+           return UNIT_volt;
+#else
+           goto not_there;
+#endif
+       break;
+    case 'V':
+       break;
+    case 'W':
+       break;
+    case 'X':
+       break;
+    case 'Y':
+       break;
+    case 'Z':
+       break;
+    }
+    errno = EINVAL;
+    return 0;
+
+not_there:
+    errno = ENOENT;
+    return 0;
+}
+
+
+typedef struct comedi_trig_struct Trigger;
+
+
+MODULE = Comedi                PACKAGE = Comedi                
+
+
+double
+constant(name,arg)
+       char *          name
+       int             arg
+
+
+
+
+MODULE = Comedi   PACKAGE = TriggerPtr  PREFIX = tptr_
+
+# create a new trigger
+#
+Trigger *
+tptr_new(CLASS, subd, n)
+       char *CLASS
+       int subd
+       int n
+       CODE:
+         RETVAL = (Trigger *) malloc(sizeof(Trigger));
+         RETVAL->subdev = subd;
+         RETVAL->n = n;
+         fprintf(stderr, "new Trigger at 0x%08x: sub=%d, n=%d\n", RETVAL, RETVAL->subdev, RETVAL->n);
+       OUTPUT:
+       RETVAL
+
+# destruct a trigger
+#
+void
+tptr_DESTROY(self)
+       Trigger *self
+       CODE:
+       fprintf(stderr, "destroy Trigger at 0x%08x\n", self);
+       free((void *) self);
diff --git a/perl/INSTALL b/perl/INSTALL
new file mode 100644 (file)
index 0000000..fc86237
--- /dev/null
@@ -0,0 +1,11 @@
+
+  perl Makefile.PL
+  make
+  make install
+
+Yow!  Who'da guessed?
+
+You can add '-DLIB_DEBUG' to Makefile.PL for a little extra verbosity.
+
+You can run the examples before 'make install' using the 'lperl' script.
+
diff --git a/perl/Lib/Changes b/perl/Lib/Changes
new file mode 100644 (file)
index 0000000..f2c3788
--- /dev/null
@@ -0,0 +1,7 @@
+Revision history for Perl extension Comedi::Lib.
+
+0.01  Tue Nov  9 01:17:02 1999
+       - original version; created by h2xs 1.18
+
+0.02  Tue Nov 16 11:49:11 EST 1999
+       - (Lib.xs) track changes to comedilib 0.7.6 (jes)
diff --git a/perl/Lib/Lib.pm b/perl/Lib/Lib.pm
new file mode 100644 (file)
index 0000000..0beac74
--- /dev/null
@@ -0,0 +1,350 @@
+# Copyright (c) 1999 Joseph E. Smith <jes@presto.med.upenn.edu>
+#
+# See the 'COPYRIGHT' section below for complete copyright information.
+
+package Comedi::Lib;
+
+use strict;
+use Carp;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD);
+
+require Exporter;
+require DynaLoader;
+require AutoLoader;
+
+@ISA = qw(Exporter DynaLoader);
+
+%EXPORT_TAGS = (
+
+ Functions => [ qw(
+        comedi_open
+       comedi_close
+       comedi_loglevel
+       comedi_error
+       comedi_perror
+       comedi_strerror
+       comedi_errno
+       comedi_fileno
+       comedi_get_n_subdevices
+       comedi_get_version
+       comedi_get_version_code
+       comedi_get_driver_name
+       comedi_get_board_name
+       comedi_get_subdevice_type
+       comedi_find_subdevice_by_type
+       comedi_get_n_channels
+       comedi_get_maxdata
+       comedi_get_rangetype
+       comedi_get_range
+       comedi_find_range
+       comedi_trigger
+       comedi_to_phys
+       comedi_from_phys
+       comedi_data_read
+       comedi_data_write
+       comedi_sv_init
+       comedi_sv_update
+       comedi_sv_measure
+       comedi_get_timer
+       comedi_timed_1chan
+ ) ],
+);
+
+# Items to export into callers namespace by default. Note: do not export
+# names by default without a very good reason. Use EXPORT_OK instead.
+# Do not simply export all your public functions/methods/constants.
+
+@EXPORT = ( @{$EXPORT_TAGS{'Functions'}});
+
+$VERSION = '0.02';
+
+sub AUTOLOAD {
+    # This AUTOLOAD is used to 'autoload' constants from the constant()
+    # XS function.  If a constant is not found then control is passed
+    # to the AUTOLOAD in AutoLoader.
+
+    my $constname;
+    ($constname = $AUTOLOAD) =~ s/.*:://;
+    my $val = constant($constname, @_ ? $_[0] : 0);
+    if ($! != 0) {
+       if ($! =~ /Invalid/) {
+           $AutoLoader::AUTOLOAD = $AUTOLOAD;
+           goto &AutoLoader::AUTOLOAD;
+       }
+       else {
+               croak "Your vendor has not defined Comedilib macro $constname";
+       }
+    }
+    eval "sub $AUTOLOAD { $val }";
+    goto &$AUTOLOAD;
+}
+
+bootstrap Comedi::Lib $VERSION;
+
+# Preloaded methods go here.
+
+sub comedi_get_version {
+    my $it = shift || return ();
+    my $code = comedi_get_version_code($it);
+
+    return (($code & 0xff0000) >> 16, 
+           ($code & 0x00ff00) >> 8,
+           ($code & 0x0000ff));
+}
+
+sub comedi_error {
+    my $err = shift || comedi_errno();
+
+    return comedi_strerror($err);
+}
+
+sub comedi_trigger {
+    my ($d, $t) = @_;
+
+    if (ref($t) eq 'Comedi::Trigger') {
+       _comedi_trigger($d, $t->{struct});
+    }
+    else {
+       _comedi_trigger($d, $t);
+    }
+}
+
+# Autoload methods go after =cut, and are processed by the autosplit program.
+
+1;
+__END__
+# Below is the stub of documentation for your module. You better edit it!
+
+=head1 NAME
+
+Comedi::Lib - Perl API for B<comedilib>
+
+=head1 SYNOPSIS
+
+  use Comedi::Lib;
+
+=head1 DESCRIPTION
+
+
+
+=head2 Core functions
+
+=item comedi_open
+
+  $dev = comedi_open('/dev/comedi0');
+
+=item comedi_close
+
+  comedi_close($dev);
+
+=item comedi_loglevel
+
+  $l = comedi_loglevel(4);
+
+=item comedi_perror
+
+  comedi_perror('Oops!');
+
+=item comedi_strerror
+
+  $s = comedi_strerror($errno);
+
+=item comedi_errno
+
+  $errno = comedi_errno();
+
+=item comedi_error
+
+  $s = comedi_error();          # get current error string
+  $s = comedi_error($errno);    # same as comedi_strerror
+
+=item comedi_fileno
+
+  $fd = comedi_fileno($dev);
+
+
+
+
+=head2 Query functions
+
+=item comedi_get_n_subdevices
+
+  $n = comedi_get_n_subdevices($dev);
+
+=item comedi_get_version_code
+
+  $n = comedi_get_version_code($dev);
+
+=item comedi_get_version
+
+  ($major, $minor, $rev) = comedi_get_version();
+
+=item comedi_get_driver_name
+
+  $s = comedi_get_driver_name($dev);
+
+=item comedi_get_board_name
+
+  $s = comedi_get_board_name($dev);
+
+=item comedi_get_subdevice_type
+
+  $n = comedi_get_subdevice_type($dev, $subd);
+
+=item comedi_find_subdevice_by_type
+
+  $n = comedi_find_subdevice_by_type($dev, $type, $subd)
+
+=item comedi_get_n_channels
+
+  $n = comedi_get_n_channels($dev, $subd);
+
+=item comedi_get_maxdata
+
+  $n = comedi_get_maxdata($dev, $subd, $channel);
+
+=item comedi_get_rangetype
+
+  $n = comedi_get_rangetype($dev, $subd, $channel);
+
+=item comedi_find_range
+
+  $n = comedi_find_range($dev, $subd, $unit, $min, $max);
+
+
+
+
+
+=head2 Trigger and command functions
+
+=item comedi_cancel
+
+  $n = comedi_cancel($dev, $subd)
+
+=item comedi_trigger
+
+=item _comedi_trigger
+
+  $t_obj = new Comedi::Trigger(mode => 2, n => 100, ...);
+  $ret = comedi_trigger($dev, $t_obj);
+
+  # or equivalently...
+  $ret = _comedi_trigger($dev, $t_obj->struct);
+
+=item comedi_command
+
+  $n = comedi_command($dev, $cmd)
+
+This function is untested.
+B<$cmd> is a scalar holding a packed C<comedi_cmd> structure.
+
+
+
+
+=head2 Functions dealing with physical units
+
+=item comedi_to_phys
+
+  $v = comedi_to_phys($sample, $range, $maxdata);
+
+=item comedi_from_phys
+
+  $n = comedi_from_phys($v, $range, $maxdata);
+
+
+
+=head2 Synchronous i/o functions
+
+=item comedi_data_read
+
+  $n = comedi_data_read($dev, $subd, $range, $aref, $data);
+
+=item comedi_data_write
+
+  $n = comedi_data_write($dev, $subd, $chan, $range, $aref, $data)
+
+
+
+
+=head2 Slowly-varying i/o functions
+
+=item comedi_sv_init
+
+  $n = comedi_sv_init($svdev, $dev, $subd, $chan);
+
+=item comedi_sv_update
+
+  $n = comedi_sv_update($svdev);
+
+=item comedi_sv_measure
+
+  $n = comedi_sv_measure($svdev, $v);
+
+
+
+
+=head2 Digital i/o functions
+
+=item comedi_dio_config
+
+  $n = comedi_dio_config($dev, $subd, $chan, $dir)
+
+=item comedi_dio_read
+
+  $n = comedi_dio_read($dev, $subd, $chan, $bit)
+
+=item comedi_dio_write
+
+  $n = comedi_dio_write($dev, $subd, $chan, $bit)
+
+=item comedi_dio_bitfield
+
+  $n = comedi_dio_bitfield($dev, $subd, $mask, $bits)
+
+
+
+
+
+=head2 Timer functions
+
+=item comedi_get_timer
+
+  $n = comedi_get_timer($dev, $subd, $freq, $ticks, $actual_freq);
+
+=item comedi_timed_1chan
+
+  $n = comedi_timed_1chan($dev, $subd, $chan, $range, $aref, $freq, $n, $vdata);
+
+
+
+
+
+=head2 Range functions
+
+=item comedi_get_range
+
+  $range = comedi_get_range($dev, $subd, $channel, $range_num);
+
+
+
+
+
+=head1 VERSION
+
+Version 0.01 09-Nov-1999
+
+=head1 AUTHOR
+
+Joe Smith <F<jes@presto.med.upenn.edu>>.
+
+=head1 COPYRIGHT
+
+Copyright (c) 1999 Joseph E. Smith.  All rights reserved.  This
+program is free software.  You may redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+Comedi(1), Comedi::Trigger(1).
+
+=cut
diff --git a/perl/Lib/Lib.xs b/perl/Lib/Lib.xs
new file mode 100644 (file)
index 0000000..8c3908d
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 1999 Joseph E. Smith <jes@presto.med.upenn.edu>
+ *
+ * All rights reserved.  This program is free software.  You may
+ * redistribute it and/or modify it under the same terms as Perl itself.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#ifdef __cplusplus
+}
+#endif
+
+#include <comedi.h>
+#include <comedilib.h>
+
+static int
+not_here(s)
+char *s;
+{
+    croak("%s not implemented on this architecture", s);
+    return -1;
+}
+
+static double
+constant(name, arg)
+char *name;
+int arg;
+{
+    errno = 0;
+    switch (*name) {
+    }
+    errno = EINVAL;
+    return 0;
+
+not_there:
+    errno = ENOENT;
+    return 0;
+}
+
+typedef struct comedi_trig_struct Trigger;
+
+MODULE = Comedi::Lib           PACKAGE = Comedi::Lib           
+
+
+double
+constant(name,arg)
+       char *          name
+       int             arg
+
+comedi_t *
+comedi_open(fn)
+       const char *    fn
+
+void
+comedi_close(it)
+       comedi_t *      it
+
+int
+comedi_loglevel(level)
+       int     level
+
+void
+comedi_perror(s)
+       const char *    s
+
+char *
+comedi_strerror(n)
+       int     n
+
+int
+comedi_errno()
+
+int
+comedi_fileno(it)
+       comedi_t *      it
+
+#/* queries */
+
+int
+comedi_get_n_subdevices(it)
+       comedi_t *      it
+
+int
+comedi_get_version_code(it)
+       comedi_t *      it
+
+char *
+comedi_get_driver_name(it)
+       comedi_t *      it
+
+char *
+comedi_get_board_name(it)
+       comedi_t *      it
+
+int
+comedi_get_subdevice_type(it, subdevice)
+       comedi_t *      it
+       unsigned int    subdevice
+
+int
+comedi_find_subdevice_by_type(it, type, subd)
+       comedi_t *      it
+       int     type
+       unsigned int    subd
+
+int
+comedi_get_n_channels(it, subdevice)
+       comedi_t *      it
+       unsigned int    subdevice
+
+lsampl_t
+comedi_get_maxdata(it, subdevice, chan)
+       comedi_t *      it
+       unsigned int    subdevice
+       unsigned int    chan
+
+int
+comedi_get_rangetype(it, subdevice, chan)
+       comedi_t *      it
+       unsigned int    subdevice
+       unsigned int    chan
+
+int
+comedi_find_range(it, subd, chan, unit, min, max)
+       comedi_t *      it
+       unsigned int    subd
+       unsigned int    chan
+       unsigned int    unit
+       double  min
+       double  max
+
+#/* triggers and commands */
+
+int
+comedi_cancel(it, subd)
+       comedi_t *      it
+       unsigned int    subd
+
+#/*
+# * This function takes a scalar holding a packed comedi trigger structure.
+# * The 'comedi_trigger' function (found in Lib.pm) takes a Perl trigger
+# * object and passes a packed structure to this function.
+# */
+
+int
+_comedi_trigger(it, trig)
+       comedi_t *      it
+       comedi_trig *   trig
+       PREINIT:
+         int i;
+       CODE:
+#ifdef LIB_DEBUG
+         fprintf(stderr, "trigger: it=%08x, trig=%08x\n", it, trig);
+         fprintf(stderr, "trigger: mode=%d, n=%d, tv=(%d,%d), nch=%d\n",
+           trig->mode, trig->n, trig->trigvar, trig->trigvar1, trig->n_chan);
+         fprintf(stderr, "chan list @%08x:", trig->chanlist);
+         for(i = 0; i < trig->n_chan; ++i) {
+           fprintf(stderr, " %08x", trig->chanlist[i]);
+         }
+         fprintf(stderr, "\n");
+         fprintf(stderr, "data buffer @%08x:", trig->data);
+         for(i = 0; i < 6; ++i) {
+           fprintf(stderr, " %04x", trig->data[i]);
+         }
+         fprintf(stderr, "\n");
+#endif
+         RETVAL = comedi_trigger(it, trig);
+       OUTPUT:
+         RETVAL
+
+int
+comedi_command(it, cmd)
+       comedi_t *      it
+       comedi_cmd *    cmd
+
+#/* physical units */
+
+double
+comedi_to_phys(data, rng, maxdata)
+       lsampl_t        data
+       comedi_range *  rng
+       lsampl_t        maxdata
+
+lsampl_t
+comedi_from_phys(data, rng, maxdata)
+       double  data
+       comedi_range *  rng
+       lsampl_t        maxdata
+
+#/* synchronous stuff */
+
+int
+comedi_data_read(it, subd, chan, range, aref, data)
+       comedi_t *      it
+       unsigned int    subd
+       unsigned int    chan
+       unsigned int    range
+       unsigned int    aref
+       lsampl_t *      data
+
+
+int
+comedi_data_write(it, subd, chan, range, aref, data)
+       comedi_t *      it
+       unsigned int    subd
+       unsigned int    chan
+       unsigned int    range
+       unsigned int    aref
+       lsampl_t        data
+
+#/* slowly varying stuff */
+
+int
+comedi_sv_init(it, dev, subd, chan)
+       comedi_sv_t *   it
+       comedi_t *      dev
+       unsigned int    subd
+       unsigned int    chan
+       OUTPUT:
+       it
+
+int
+comedi_sv_update(it)
+       comedi_sv_t *   it
+
+int
+comedi_sv_measure(it, data)
+       comedi_sv_t *   it
+       double *        data
+
+#/* dio config */
+
+int
+comedi_dio_config(it, subd, chan, dir)
+       comedi_t *      it
+       unsigned int    subd
+       unsigned int    chan
+       unsigned int    dir
+
+int
+comedi_dio_read(it, subd, chan, bit)
+       comedi_t *      it
+       unsigned int    subd
+       unsigned int    chan
+       unsigned int    &bit
+       OUTPUT:
+       bit
+
+int
+comedi_dio_write(it, subd, chan, bit)
+       comedi_t *      it
+       unsigned int    subd
+       unsigned int    chan
+       unsigned int    bit
+
+int
+comedi_dio_bitfield(it,        subd, write_mask, bits)
+       comedi_t *      it
+       unsigned int    subd
+       unsigned int    write_mask
+       unsigned int    &bits
+       OUTPUT:
+       bits
+
+#/* timer stuff */
+
+int
+comedi_get_timer(it, subdev, freq, trigvar, actual_freq)
+       comedi_t *      it
+       unsigned int    subdev
+       double  freq
+       unsigned int    &trigvar
+       double  &actual_freq
+       OUTPUT:
+       trigvar
+       actual_freq
+
+int
+comedi_timed_1chan(it, subdev, chan, range, aref, freq, n_samples, data)
+       comedi_t *      it
+       unsigned int    subdev
+       unsigned int    chan
+       unsigned int    range
+       unsigned int    aref
+       double  freq
+       unsigned int    n_samples
+       double *        data
+       CODE:
+#ifdef LIB_DEBUG
+         fprintf(stderr, "timed_1chan: CR=(%d,%d,%d), f=%g, n=%d, data->0x%08x\n",
+           chan, range, aref, freq, n_samples, data);
+#endif
+         RETVAL = comedi_timed_1chan(it, subdev, chan, range, aref, freq, n_samples, data);
+       OUTPUT:
+         RETVAL
+
+
+
+#/* range stuff */
+
+comedi_range *
+comedi_get_range(it, subdevice, chan, range)
+       comedi_t *      it
+       unsigned int    subdevice
+       unsigned int    chan
+       unsigned int    range
+
diff --git a/perl/Lib/Makefile.PL b/perl/Lib/Makefile.PL
new file mode 100644 (file)
index 0000000..9cc83f5
--- /dev/null
@@ -0,0 +1,13 @@
+use ExtUtils::MakeMaker;
+
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+
+WriteMakefile(
+    'NAME'        => 'Comedi::Lib',
+    'VERSION_FROM' => 'Lib.pm',
+    'LIBS'        => ['-lcomedi'],
+    'DEFINE'      => '',
+    'INC'         => '',
+    'TYPEMAPS'     => ['../typemap'],
+);
diff --git a/perl/MANIFEST b/perl/MANIFEST
new file mode 100644 (file)
index 0000000..cfdd7a7
--- /dev/null
@@ -0,0 +1,24 @@
+README
+INSTALL
+MANIFEST
+Changes
+Makefile.PL
+Comedi.pm
+Comedi.xs
+test.pl
+typemap
+lperl
+info.perl
+example.perl
+mode1.perl
+mode2.perl
+mode2lib.perl
+Trigger/Makefile.PL
+Trigger/Trigger.pm
+Trigger/test.pl
+Trigger/Changes
+Lib/Makefile.PL
+Lib/Lib.pm
+Lib/Lib.xs
+Lib/Changes
+
diff --git a/perl/Makefile.PL b/perl/Makefile.PL
new file mode 100644 (file)
index 0000000..6c81b2e
--- /dev/null
@@ -0,0 +1,12 @@
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+    'NAME'        => 'Comedi',
+    'VERSION_FROM' => 'Comedi.pm',
+    'LIBS'        => [],
+    'DEFINE'      => '',
+    'INC'         => '',
+
+    'dist'      => { COMPRESS=>"gzip", SUFFIX=>"gz" },
+);
diff --git a/perl/README b/perl/README
new file mode 100644 (file)
index 0000000..8da8e5a
--- /dev/null
@@ -0,0 +1,30 @@
+
+Comedi and Comedi::Lib: A Perl API for comedi and comedilib
+
+Version 0.02 16-Nov-1999
+
+This is a first pass at a Perl extension for using comedi and comedilib
+(http://stm.lbl.gov/comedi).
+
+This is also my first attempt at a Perl extension, so be warned!  I
+started with h2xs and hacked away at it from there.  Please pass the
+clues if you're an XS wizard.
+
+There are some example scripts but no tests (yet).  You can run the
+examples before actually installing the modules (make install) using
+the little script 'lperl', e.g.,
+
+  [jes@rtc Comedi]$ ./lperl example.perl
+
+The modules and examples were tested with comedi 0.7.{19,26} and
+comedilib 0.7.{3,5,6}, using an NI-MIO-16e.  There should be no
+board-specific stuff here (except possibly the subdevice codes);
+please let me know if it (does|doesn't) work with other boards.
+
+There is very little object oriented code here, and what is here (a
+Trigger object) need not be used.  Right now, I'm not anxious to add
+another layer over comedi, which is still alpha.  Any ideas or
+suggestions are welcome, however.
+
+
+Joe Smith <jes@presto.med.upenn.edu>
diff --git a/perl/Trigger/Changes b/perl/Trigger/Changes
new file mode 100644 (file)
index 0000000..ad7f9b4
--- /dev/null
@@ -0,0 +1,5 @@
+Revision history for Perl extension Comedi::Trigger.
+
+0.01  Tue Nov  9 10:18:34 1999
+       - original version; created by h2xs 1.18
+
diff --git a/perl/Trigger/Makefile.PL b/perl/Trigger/Makefile.PL
new file mode 100644 (file)
index 0000000..fb3497b
--- /dev/null
@@ -0,0 +1,9 @@
+use ExtUtils::MakeMaker;
+
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+
+WriteMakefile(
+    'NAME'        => 'Comedi::Trigger',
+    'VERSION_FROM' => 'Trigger.pm',
+);
diff --git a/perl/Trigger/Trigger.pm b/perl/Trigger/Trigger.pm
new file mode 100644 (file)
index 0000000..aa8d3e1
--- /dev/null
@@ -0,0 +1,234 @@
+# Copyright (c) 1999 Joseph E. Smith <jes@presto.med.upenn.edu>
+#
+# See the 'COPYRIGHT' section below for complete copyright information.
+
+package Comedi::Trigger;
+
+use strict;
+use vars qw($VERSION $AUTOLOAD @ISA @EXPORT @EXPORT_OK);
+
+use Carp;
+
+require Exporter;
+require AutoLoader;
+
+@ISA = qw(Exporter AutoLoader);
+
+# Nothing exported by default; all access through Trigger object
+
+@EXPORT;
+
+$VERSION = '0.01';
+
+
+# Preloaded methods go here.
+
+sub new {
+    my $class = shift;
+    my $self = bless {};
+
+    while (@_) {
+       my ($member, $value) = (shift, shift);
+
+       $self->$member($value);
+    }
+
+    return $self->pack;
+}
+
+sub pack {
+    my $self = shift;
+    my $struct = $self->_pack;
+
+    $self->{'struct'} = $struct;
+
+    return $struct ? $self : undef;
+}
+
+sub _pack {
+    my $self = shift;
+
+    my $chl = $self->{'chanlist'} || return undef;
+
+    my $n_chan = (exists($self->{'n_chan'}) ?
+       $self->{'n_chan'} : @$chl) || return undef;
+
+    # keep these buffers in the object so they don't go away
+    #
+    $self->{'_chanlist'} = pack('L*', @{$self->{'chanlist'}});
+
+    my ($n, $buf);
+
+    # n can be zero
+    #
+    if (exists($self->{'n'}) && exists($self->{'data'})) {
+       $n = $self->{'n'};
+       $buf = $self->{'data'};
+    }
+    elsif (exists($self->{'n'})) {
+       $n = $self->{'n'};
+       $self->{'_data'} = "\0" x ($n * $n_chan * 2);
+       $buf = $self->{'data'} = \$self->{'_data'};
+    }
+    elsif (exists($self->{'data'})) {
+       $buf = $self->{'data'};
+       $n = length($$buf)/(2*$n_chan);
+    }
+    else {
+       return undef;
+    }
+
+    # TODO: more error-checking (return undef)
+    #
+    # minimal sanity checks
+    #
+    croak("Data buffer is not a scalar reference")
+       unless ref($buf) eq 'SCALAR';
+    croak("Sample count (2*$n) too large for buffer (" .
+         length($$buf) . ")")
+       unless ((2*$n) <= length $$buf);
+
+    return pack('L4 p2 L5 L3',
+                $self->{'subdev'} || 0,
+                $self->{'mode'} || 0,
+                $self->{'flags'} || 0,
+                $n_chan,
+                $self->{'_chanlist'},
+                $$buf,
+                $n,
+                $self->{'trigsrc'} || $self->{'source'},
+                $self->{'trigvar'} || $self->{'major'},
+                $self->{'trigvar1'} || $self->{'minor'},
+                length $$buf,
+                0, 0, 0);
+}
+
+
+sub AUTOLOAD {
+    my $self = shift;
+    ref($self) || croak "$self is not an object";
+
+    (my $name = $AUTOLOAD) =~ s/.*://;  # strip leading class name(s)
+
+    return @_ ? $self->{$name} = shift : $self->{$name};
+}
+
+# Autoload methods go after =cut, and are processed by the autosplit program.
+
+1;
+__END__
+# Below is the stub of documentation for your module. You better edit it!
+
+=head1 NAME
+
+Comedi::Trigger - Object oriented interface to comedi trigger struct
+
+=head1 SYNOPSIS
+
+  use Comedi::Trigger;
+
+  $t = new Comedi::Trigger;
+
+  $t->mode(2);
+  $t->chanlist([ CR_PACK(0,0,AREF_GROUND), CR_PACK(1,0,AREF_GROUND) ]);
+  $t->n(100);
+  $t->major(1999);
+  $t->minor(20);
+
+  $ret = comedi_trigger($dev, $t);
+
+=head1 DESCRIPTION
+
+This module provides an object oriented interface to B<comedi> trigger
+structures.  It provides some convenience over C<pack>ing one by hand:
+you need only specify a minimum of information, the rest will be
+calculated if possible.
+
+=head1 METHODS
+
+Each element of the B<comedi> trigger structure is available as a
+method which either retrieves or sets the value of that element.  In
+addition, some of the elements have aliases which may be used to make
+the programmer's intention clear.
+
+The following methods function analogously to the C struct elements:
+
+=over 4
+
+=item subdev
+
+=item mode
+
+=item flags
+
+=item n_chan
+
+=item n
+
+=back
+
+These elements function analogously, but have aliases:
+
+=over 4
+
+=item trigsrc or source
+
+=item trigvar or major
+
+=item trigvar1 or minor
+
+=back
+
+These elements function somewhat differently than the C implementation:
+
+=over 4
+
+=item chanlist
+
+This should be a reference to a Perl list of channel descriptors
+packed using B<Comedi::CR_PACK> (see the example above).
+
+If the number of channels (B<n_chan>)is not specified, it will be set
+to the number of elements in this list.
+
+=item data
+
+This should be a scalar, pre-allocated to a length large enough to
+hold the requested data.  If no value is set, a scalar will be created
+of the appropriate size.
+
+=item data_len
+
+This element should be set to the size of the data buffer (in bytes).
+If not specified, it will be set automatically.
+
+=back
+
+This method is specific to the Perl module.
+
+=item pack
+
+This method packs the current state of the trigger elements into a
+binary form compatible with the C C<comedi_trig_struct>.  This method
+is normally called automatically by B<Comedi::Lib> functions that take
+trigger arguments.
+
+=head1 VERSION
+
+Version 0.01 09-Nov-1999
+
+=head1 AUTHOR
+
+Joe Smith <F<jes@presto.med.upenn.edu>>.
+
+=head1 COPYRIGHT
+
+Copyright (c) 1999 Joseph E. Smith.  All rights reserved.  This
+program is free software.  You may redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+Comedi(1), Comedi::Lib(1).
+
+=cut
diff --git a/perl/Trigger/test.pl b/perl/Trigger/test.pl
new file mode 100644 (file)
index 0000000..52a701f
--- /dev/null
@@ -0,0 +1,20 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl test.pl'
+
+######################### We start with some black magic to print on failure.
+
+# Change 1..1 below to 1..last_test_to_print .
+# (It may become useful if the test is moved to ./t subdirectory.)
+
+BEGIN { $| = 1; print "1..1\n"; }
+END {print "not ok 1\n" unless $loaded;}
+use Comedi::Trigger;
+$loaded = 1;
+print "ok 1\n";
+
+######################### End of black magic.
+
+# Insert your test code below (better if it prints "ok 13"
+# (correspondingly "not ok 13") depending on the success of chunk 13
+# of the test code):
+
diff --git a/perl/example.perl b/perl/example.perl
new file mode 100644 (file)
index 0000000..dea28ed
--- /dev/null
@@ -0,0 +1,41 @@
+
+#   A little input demo
+
+use constant N_SAMPLES => 64;
+
+use Comedi qw( :Functions :Constants :Ioctls ) ;
+
+# allocate a data buffer
+
+# create a channel list
+$ch = pack('L*', CR_PACK(0, 0, AREF_GROUND));
+
+# create a data buffer
+$buf = pack('S' . N_SAMPLES);
+
+# create a trigger object
+#
+$it = pack('L4 p2 L5 L3',
+          0,
+          0,
+          0,
+          1,
+          $ch,
+          $buf,
+          N_SAMPLES,
+          0,
+          0,
+          0
+          );
+
+$fn = '/dev/comedi0';
+
+sysopen(FILE, $fn, O_RDWR) || die "Can't open '$fn': $!";
+
+ioctl(FILE, COMEDI_TRIG, $it) || die "ioctl failed: $!";
+
+foreach (unpack('S*', $buf)) {
+    printf("%d\n", $_);
+}
+
+
diff --git a/perl/info.perl b/perl/info.perl
new file mode 100644 (file)
index 0000000..1e4dc92
--- /dev/null
@@ -0,0 +1,44 @@
+
+use Comedi qw( :SubdeviceTypes );
+use Comedi::Lib;
+
+sub help {
+    print STDERR "info </dev/comediN>\n";
+    exit(0);
+}
+
+%subdevice_types = (
+    COMEDI_SUBD_UNUSED,  'unused',
+    COMEDI_SUBD_AI,      'analog input',
+    COMEDI_SUBD_AO,      'analog output',
+    COMEDI_SUBD_DI,      'digital input',
+    COMEDI_SUBD_DO,      'digital output',
+    COMEDI_SUBD_DIO,     'digital I/O',
+    COMEDI_SUBD_COUNTER, 'counter',
+    COMEDI_SUBD_TIMER,   'timer',
+    COMEDI_SUBD_MEMORY,  'memory',
+    COMEDI_SUBD_CALIB,   'calibration',
+    COMEDI_SUBD_PROC,    'processor',
+);
+
+#
+# Main Program
+#
+
+help() if (@ARGV != 1);
+
+$it = comedi_open($ARGV[0]) || die "cannot open $ARGV[0]: $!";
+
+printf("overall info:\n");
+printf("  version code: %d.%d.%d (0x%06x)\n", comedi_get_version($it), comedi_get_version_code($it));
+printf("  driver name: %s\n", comedi_get_driver_name($it));
+printf("  board name: %s\n", comedi_get_board_name($it));
+printf("  number of subdevices: %d\n", $n_subdevices = comedi_get_n_subdevices($it));
+
+for ($i = 0; $i < $n_subdevices; $i++) {
+    printf("subdevice %d:\n", $i);
+    $type = comedi_get_subdevice_type($it, $i);
+    printf("  type: %d (%s)\n", $type, $subdevice_types{$type});
+    printf("  number of channels: %d\n", comedi_get_n_channels($it, $i));
+    printf("  max data value: %d\n", comedi_get_maxdata($it, $i, 0));
+}
diff --git a/perl/lperl b/perl/lperl
new file mode 100755 (executable)
index 0000000..612dfc3
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+PERL_DL_NONLAZY=1 /usr/local/bin/perl -I./blib/arch -I./blib/lib -I/usr/lib/perl5/i386-linux/5.00404 -I/usr/lib/perl5 "$@"
diff --git a/perl/mode1.perl b/perl/mode1.perl
new file mode 100644 (file)
index 0000000..67016ec
--- /dev/null
@@ -0,0 +1,54 @@
+
+#   A little input demo
+
+use Comedi::Trigger;
+
+use Comedilib qw( :DEFAULT :Constants :Ioctls ) ;
+
+use constant (
+  AI_DEV => 0,
+  AI_SUB => 0,
+  AO_DEV => 0,
+  AO_SUB => 1,
+);
+
+$fn = '/dev/comedi0';
+
+# create a channel list
+#
+@ch = ( CR_PACK(0, 0, AREF_GROUND),
+       CR_PACK(1, 0, AREF_GROUND) );
+
+$d = comedi_open($fn) || die "Can't open '$fn': " . comedi_error();
+
+$freq = $opt_f || 1000;
+
+comedi_loglevel(4);
+
+# convert the requested frequency into a timer value
+#
+($ret = comedi_get_timer($d, AI_SUB, $freq, $ticks, $actual_freq)) == 0 ||
+    die "Can't get timer: " . comedi_strerror($ret);
+
+$buf = pack('S*', 100..119);
+
+# create a trigger object
+#
+$it = new Comedi::Trigger(
+   mode => 1,
+   chanlist => \@ch,
+   n => 20,
+   data => \$buf,
+   major => $ticks);
+
+
+die "Mode 1 doesn't work yet...";
+
+($ret = comedi_trigger($d, $it)) > 0 ||
+    die "Analog input error: " . comedi_strerror(comedi_errno()) ;
+
+foreach (unpack('S*', ${$it->data})) {
+    printf("%d\n", $_);
+}
+
+
diff --git a/perl/mode2.perl b/perl/mode2.perl
new file mode 100644 (file)
index 0000000..8cb6bb4
--- /dev/null
@@ -0,0 +1,57 @@
+
+#   A little input demo
+
+use Comedi qw( :Functions :Constants :Ioctls ) ;
+use Comedi::Trigger;
+use Comedi::Lib;
+
+use constant AI_DEV => 0;
+use constant AI_SUB => 0;
+use constant AO_DEV => 0;
+use constant AO_SUB => 1;
+
+use constant N_SAMPLES => 100;
+
+$fn = '/dev/comedi0';
+
+# create a channel list
+#
+@ch = ( CR_PACK(0, 0, AREF_GROUND),
+       CR_PACK(1, 0, AREF_GROUND) );
+
+$d = comedi_open($fn) || die "Can't open '$fn': " . comedi_error();
+
+$fd = comedi_fileno($d);
+
+open(COMDEV, "<&$fd") || die "Can't get filehandle from fd $fd: $!";
+
+$freq = $opt_f || 1000;
+
+comedi_loglevel(4);
+
+# convert the requested frequency into a timer value
+#
+($ret = comedi_get_timer($d, AI_SUB, $freq, $ticks, $actual_freq)) == 0 ||
+    die "Can't get timer: " . comedi_strerror($ret);
+
+$buf = pack('S*', 100..(100+N_SAMPLES-1));
+
+# create a trigger object
+#
+$it = new Comedi::Trigger(
+   mode     => 2,
+   chanlist => \@ch,
+   n        => 20,
+   data     => \$buf,
+   major    => $ticks,
+   minor    => 20);
+
+
+die "Analog input trigger error ($ret): " . comedi_strerror(comedi_errno())
+    if (($ret = comedi_trigger($d, $it)) < 0);
+
+($ret = sysread(COMDEV, $buf, 2*N_SAMPLES)) || die "Read error ($ret): $!";
+    
+foreach (unpack('s*', $buf)) {
+    printf("%d\n", $_);
+}
diff --git a/perl/mode2lib.perl b/perl/mode2lib.perl
new file mode 100644 (file)
index 0000000..e32be8a
--- /dev/null
@@ -0,0 +1,38 @@
+
+#   A little input demo
+
+use Comedi qw( :Functions :Constants );
+use Comedi::Lib;
+
+use constant AI_DEV => 0;
+use constant AI_SUB => 0;
+use constant AO_DEV => 0;
+use constant AO_SUB => 1;
+use constant N_SAMPLES => 100;
+
+$fn = '/dev/comedi0';
+
+# create a channel list
+#
+@ch = ( CR_PACK(0, 0, AREF_GROUND));
+
+$d = comedi_open($fn) || die "Can't open '$fn': " . comedi_error();
+
+$freq = $opt_f || 1000;
+
+# convert the requested frequency into a timer value
+#
+($ret = comedi_get_timer($d, AI_SUB, $freq, $ticks, $actual_freq)) == 0 ||
+    die "Can't get timer: " . comedi_strerror($ret);
+
+$buf = pack('d*', 100..(100+N_SAMPLES-1));
+
+($ret =
+ comedi_timed_1chan($d, AI_SUB, 0, 0, AREF_GROUND, 1000, N_SAMPLES, $buf)) == 0 ||
+    die "Analog input error ($ret): " . comedi_strerror(comedi_errno()) ;
+
+foreach (unpack('d*', $buf)) {
+    printf("%g\n", $_);
+}
+
+
diff --git a/perl/test.pl b/perl/test.pl
new file mode 100644 (file)
index 0000000..c5f1e2c
--- /dev/null
@@ -0,0 +1,20 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl test.pl'
+
+######################### We start with some black magic to print on failure.
+
+# Change 1..1 below to 1..last_test_to_print .
+# (It may become useful if the test is moved to ./t subdirectory.)
+
+BEGIN { $| = 1; print "1..1\n"; }
+END {print "not ok 1\n" unless $loaded;}
+use Comedi;
+$loaded = 1;
+print "ok 1\n";
+
+######################### End of black magic.
+
+# Insert your test code below (better if it prints "ok 13"
+# (correspondingly "not ok 13") depending on the success of chunk 13
+# of the test code):
+
diff --git a/perl/typemap b/perl/typemap
new file mode 100644 (file)
index 0000000..2f4d7b6
--- /dev/null
@@ -0,0 +1,21 @@
+TYPEMAP
+const char *   T_PV
+
+lsampl_t       T_U_INT
+sampl_t        T_U_SHORT
+
+comedi_t *     T_PTRREF
+comedi_sv_t *  T_PTRREF
+comedi_range * T_PTRREF
+
+# these are buffers: perl scalar holding binary data
+#
+comedi_trig *  T_PV
+comedi_cmd *   T_PV
+sampl_t *      T_PV
+lsampl_t *     T_PV
+double *       T_PV
+buffer_t *     T_PV
+
+Trigger *      T_PTROBJ
+
diff --git a/testing/Makefile b/testing/Makefile
new file mode 100644 (file)
index 0000000..2c04b65
--- /dev/null
@@ -0,0 +1,19 @@
+
+
+
+CFLAGS +=-I ../include -I . -O2 -Wall
+LDFLAGS=-L../lib/ -lcomedi
+
+
+TARG=comedi_test
+OBJS=main.o mode1.o
+
+all: $(TARG)
+
+$(TARG):       $(OBJS)
+       $(CC) -o $(TARG) $(OBJS) $(LDFLAGS)
+
+clean:
+       -rm $(TARG) $(OBJS)
+
+distclean: clean
diff --git a/testing/inpn.c b/testing/inpn.c
new file mode 100644 (file)
index 0000000..4268121
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+   This demo opens /dev/comedi0 and looks for an analog input
+   subdevice.  If it finds one, it measures one sample on each
+   channel for each input range.  The value NaN indicates that
+   the measurement was out of range.
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+extern int verbose_flag;
+extern int subdevice;
+extern int range;
+extern int channel;
+extern int aref;
+extern char *filename;
+
+comedi_t *device;
+
+
+int main(int argc, char *argv[])
+{
+       int n_subdevs;
+       int n_chans,chan;
+       int n_ranges;
+       int range;
+       int rangetype;
+       int maxdata;
+       lsampl_t data;
+       double voltage;
+
+       parse_options(argc,argv);
+
+       device=comedi_open(filename);
+       if(!device){
+               comedi_perror(filename);
+               exit(0);
+       }
+
+       subdevice=comedi_find_subdevice_by_type(device,COMEDI_SUBD_AI,0);
+       if(subdevice<0){
+               printf("no analog input subdevice found\n");
+               exit(0);
+       }
+
+       n_chans=comedi_get_n_channels(device,subdevice);
+       for(chan=0;chan<n_chans;chan++){
+               printf("%d: ",chan);
+
+               //n_ranges=comedi_get_n_ranges(device,subdevice,chan);
+               rangetype=comedi_get_rangetype(device,subdevice,chan);
+               n_ranges=RANGE_LENGTH(rangetype);
+
+               maxdata=comedi_get_maxdata(device,subdevice,chan);
+               for(range=0;range<n_ranges;range++){
+                       comedi_data_read(device,subdevice,chan,range,aref,&data);
+                       voltage=comedi_to_phys(data,comedi_get_range(device,subdevice,chan,range),maxdata);
+                       printf("%g ",voltage);
+               }
+               printf("\n");
+       }
+       
+       exit(0);
+}
+
diff --git a/testing/main.c b/testing/main.c
new file mode 100644 (file)
index 0000000..e591eb2
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <malloc.h>
+
+
+char *filename="/dev/comedi0";
+int verbose_flag;
+comedi_t *device;
+
+int subdevice;
+int channel;
+int aref;
+int range;
+
+
+int parse_options(int argc, char *argv[])
+{
+       int c;
+
+
+       while (1) {
+               c = getopt(argc, argv, "acsrfvdgom");
+               if (c == -1)
+                       break;
+               switch (c) {
+               case 'f':
+                       filename = argv[optind];
+                       break;
+               case 's':
+                       sscanf(argv[optind],"%d",&subdevice);
+                       break;
+               case 'c':
+                       sscanf(argv[optind],"%d",&channel);
+                       break;
+               case 'a':
+                       sscanf(argv[optind],"%d",&aref);
+                       break;
+               case 'r':
+                       sscanf(argv[optind],"%d",&range);
+                       break;
+               case 'v':
+                       verbose_flag = 1;
+                       break;
+               case 'd':
+                       aref=AREF_DIFF;
+                       break;
+               case 'g':
+                       aref=AREF_GROUND;
+                       break;
+               case 'o':
+                       aref=AREF_OTHER;
+                       break;
+               case 'm':
+                       aref=AREF_COMMON;
+                       break;
+               default:
+                       printf("bad option\n");
+                       exit(1);
+               }
+       }
+
+       return argc;
+}
+
+
+
diff --git a/testing/mode1.c b/testing/mode1.c
new file mode 100644 (file)
index 0000000..6c7752c
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+       A little input demo for mode 1
+
+       Mode 1 uses a timer to acquire samples at regular intervals.
+       It scans through the channel list, and then repeats.
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <math.h>
+#include <sys/time.h>
+#include <string.h>
+
+#define BUFSZ          1000
+#define N_CHANS                1000
+
+extern int verbose_flag;
+extern int subdevice;
+extern int range;
+extern int channel;
+extern int aref;
+extern char *filename;
+
+comedi_t *device;
+
+void parse_options(int argc,char *argv[]);
+
+sampl_t data[BUFSZ];
+
+unsigned int chanlist[N_CHANS];
+
+int ai_mode1_test(double freq,int n_chans,int n_scans);
+
+int main(int argc, char *argv[])
+{
+       parse_options(argc,argv);
+
+       device=comedi_open(filename);
+       if(!device){
+               comedi_perror(filename);
+               exit(0);
+       }
+
+       subdevice=comedi_find_subdevice_by_type(device,COMEDI_SUBD_AI,0);
+       if(subdevice<0){
+               printf("no analog input subdevice found\n");
+               exit(0);
+       }
+
+while(1){
+       printf("Testing mode 1:\n");
+
+       printf("10 hz, 1 chan\n");
+       ai_mode1_test(10.0,1,10);
+
+       printf("100 hz, 1 chan\n");
+       ai_mode1_test(100.0,1,100);
+
+       printf("1000 hz, 1 chan\n");
+       ai_mode1_test(1000.0,1,1000);
+
+       printf("10000 hz, 1 chan\n");
+       ai_mode1_test(10000.0,1,10000);
+
+       printf("100000 hz, 1 chan\n");
+       ai_mode1_test(100000.0,1,100000);
+
+       printf("100000 hz, 2 chan\n");
+       ai_mode1_test(100000.0,2,50000);
+
+       printf("100000 hz, 16 chans\n");
+       ai_mode1_test(100000.0,16,100000/16);
+}
+
+
+       return 0;
+}
+
+int ai_mode1_test(double freq,int n_chans,int n_scans)
+{
+       comedi_trig it;
+       int err;
+       int n,i;
+       double actual_freq;
+       void *data_ptr;
+       int n_left;
+       int maxchan;
+       struct timeval start,stop;
+       int m;
+
+       memset(&it,0,sizeof(it));
+       it.subdev = subdevice;
+       it.mode = 1;
+       it.n_chan = n_chans;
+       it.chanlist = chanlist;
+       it.data = data;
+       it.n = n_scans;
+
+       maxchan = comedi_get_n_channels(device,subdevice);
+
+       /* pack the channel list */
+       for(i=0;i<n_chans;i++){
+               chanlist[i] = CR_PACK(i%maxchan, range, aref);
+       }
+
+       comedi_get_timer(device,subdevice,freq,&it.trigvar,&actual_freq);
+       printf("primary actual frequency=%g timer value=%d\n",actual_freq,it.trigvar);
+
+       gettimeofday(&start,NULL);
+
+       if ((err = comedi_trigger(device, &it)) < 0) {
+               perror("ioctl");
+       }
+
+       data_ptr=data;
+       n_left=n_scans*n_chans*sizeof(sampl_t);
+       while(n_left>0){
+               m=n_left;
+               if(m>=BUFSZ)m=BUFSZ;
+               if((n=read(comedi_fileno(device),data_ptr,m))<0){
+                       perror("read");
+                       exit(1);
+               }
+               //printf("read %d\n",n);
+               n_left-=n;
+               //data_ptr+=n;
+       }
+
+       gettimeofday(&stop,NULL);
+
+       if(stop.tv_usec<=start.tv_usec){
+               stop.tv_usec+=1000000;
+               stop.tv_sec--;
+       }
+       stop.tv_sec-=start.tv_sec;
+       stop.tv_usec-=start.tv_usec;
+
+       printf("actual time elapsed: %d.%06d s.\n",(int)stop.tv_sec,(int)stop.tv_usec);
+       printf("expected time: %g\n",n_scans*n_chans/actual_freq);
+
+       return 0;
+}
+
diff --git a/testing/mode2.c b/testing/mode2.c
new file mode 100644 (file)
index 0000000..45013e3
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+   A little input demo for mode 2
+
+   Mode 2 uses two different timers to convert samples.
+   The primary timer determines the time between scans,
+   and the secondary timer determines the time between
+   samples in a scan.
+
+   The time between scans is in trigval; the time
+   between samples is selected by trigval1.  Conversion
+   from seconds or Hz is done using the standard timer
+   routines.
+
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#define N_SCANS                10
+#define N_CHANS                16
+
+int subdevice = 0;
+int chan=0;
+int range = 0;
+int aref = AREF_GROUND;
+double freq = 1000;
+
+#define N_SAMPLES      1000
+
+double data[N_SAMPLES];
+
+
+int main(int argc, char *argv[])
+{
+       char *fn = NULL;
+       int i;
+       comedi_t *dev;
+
+       fn = "/dev/comedi0";
+
+       dev = comedi_open(fn);
+
+#if 0
+       for(i=0;i<10;i++){
+               range=comedi_find_range(dev,subdevice,chan,0,-i,i);
+               printf("%d\n",range);
+       }
+#endif
+       comedi_timed_1chan(dev,subdevice,chan,range,aref,freq,N_SAMPLES,data);
+
+       for(i=0;i<N_SAMPLES;i++){
+               printf("%g\n",data[i]);
+       }
+
+       return 0;
+}
+