Initial revision
authorDavid Schleef <ds@schleef.org>
Wed, 2 Feb 2000 03:21:59 +0000 (03:21 +0000)
committerDavid Schleef <ds@schleef.org>
Wed, 2 Feb 2000 03:21:59 +0000 (03:21 +0000)
90 files changed:
Changelog [new file with mode: 0644]
Documentation/comedi/FAQ [new file with mode: 0644]
Documentation/comedi/Hardware_Driver.HOWTO [new file with mode: 0644]
Documentation/comedi/command [new file with mode: 0644]
Documentation/comedi/drivers.txt [new file with mode: 0644]
Documentation/comedi/mode-info [new file with mode: 0644]
Documentation/comedi/notes/README [new file with mode: 0644]
Documentation/comedi/notes/atmio_notes [new file with mode: 0644]
Documentation/comedi/notes/das [new file with mode: 0644]
Documentation/comedi/notes/testing [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile [new file with mode: 0644]
NOTES [new file with mode: 0644]
README [new file with mode: 0644]
Rules.make [new file with mode: 0644]
TODO [new file with mode: 0644]
comedi/Config.in [new file with mode: 0644]
comedi/Makefile [new file with mode: 0644]
comedi/am9513.h [new file with mode: 0644]
comedi/comedi_ksyms.c [new file with mode: 0644]
comedi/comedi_module.h [new file with mode: 0644]
comedi/drivers.c [new file with mode: 0644]
comedi/drivers/8255.c [new file with mode: 0644]
comedi/drivers/8255.h [new file with mode: 0644]
comedi/drivers/Makefile [new file with mode: 0644]
comedi/drivers/das08.c [new file with mode: 0644]
comedi/drivers/das08jr.c [new file with mode: 0644]
comedi/drivers/das16.c [new file with mode: 0644]
comedi/drivers/das1600.c [new file with mode: 0644]
comedi/drivers/das6402.c [new file with mode: 0644]
comedi/drivers/dt2801.c [new file with mode: 0644]
comedi/drivers/dt2811.c [new file with mode: 0644]
comedi/drivers/dt2814.c [new file with mode: 0644]
comedi/drivers/dt2815.c [new file with mode: 0644]
comedi/drivers/dt2817.c [new file with mode: 0644]
comedi/drivers/dt282x.c [new file with mode: 0644]
comedi/drivers/dt3000.c [new file with mode: 0644]
comedi/drivers/mite.c [new file with mode: 0644]
comedi/drivers/mite.h [new file with mode: 0644]
comedi/drivers/multiq3.c [new file with mode: 0644]
comedi/drivers/ni_atmio.c [new file with mode: 0644]
comedi/drivers/ni_mio_common.c [new file with mode: 0644]
comedi/drivers/ni_pcidio.c [new file with mode: 0644]
comedi/drivers/ni_pcimio.c [new file with mode: 0644]
comedi/drivers/ni_stc.h [new file with mode: 0644]
comedi/drivers/pcl711.c [new file with mode: 0644]
comedi/drivers/pcl725.c [new file with mode: 0644]
comedi/drivers/pcl726.c [new file with mode: 0644]
comedi/drivers/rti800.c [new file with mode: 0644]
comedi/drivers/rti802.c [new file with mode: 0644]
comedi/dummy.c [new file with mode: 0644]
comedi/kcomedilib/Makefile [new file with mode: 0644]
comedi/kcomedilib/kcomedilib.c [new file with mode: 0644]
comedi/kern_compat.h [new file with mode: 0644]
comedi/kvmem.c [new file with mode: 0644]
comedi/kvmem.h [new file with mode: 0644]
comedi/mk_range.c [new file with mode: 0644]
comedi/module.c [new file with mode: 0644]
comedi/old/atmio-16d.c [new file with mode: 0644]
comedi/old/atmio-16f.c [new file with mode: 0644]
comedi/old/atmio-16x.c [new file with mode: 0644]
comedi/old/rti860.c [new file with mode: 0644]
comedi/parport.c [new file with mode: 0644]
comedi/proc.c [new file with mode: 0644]
comedi/range.c [new file with mode: 0644]
comedi/realtime/vd_dds.c [new file with mode: 0644]
comedi/realtime/vd_timer.c [new file with mode: 0644]
comedi/rtai.c [new file with mode: 0644]
comedi/rtai.h [new file with mode: 0644]
comedi/rtl.c [new file with mode: 0644]
comedi/rtl.h [new file with mode: 0644]
comedi/rtl_v1.c [new file with mode: 0644]
comedi/rtl_v1.h [new file with mode: 0644]
comedi_config/Makefile [new file with mode: 0644]
comedi_config/comedi_config.c [new file with mode: 0644]
etc/comedi.conf [new file with mode: 0755]
etc/conf.modules [new file with mode: 0644]
etc/das1600.conf [new file with mode: 0755]
etc/dt282x.conf [new file with mode: 0755]
etc/isapnp.conf [new file with mode: 0644]
include/comedi.h [new file with mode: 0644]
man/comedi.7 [new file with mode: 0644]
man/comedi_config.8 [new file with mode: 0644]
scripts/Configure [new file with mode: 0755]
scripts/Configure.help [new file with mode: 0644]
scripts/check_kernel [new file with mode: 0755]
scripts/config.dist [new file with mode: 0644]
scripts/config.h.dist [new file with mode: 0644]
scripts/config.in [new file with mode: 0644]
scripts/pathdown.sh [new file with mode: 0644]

diff --git a/Changelog b/Changelog
new file mode 100644 (file)
index 0000000..dd313fc
--- /dev/null
+++ b/Changelog
@@ -0,0 +1,20 @@
+
+0.7.36
+
+ - Rearranged most files
+ - changed Makefiles to work the same as Linux-2.2.13
+ - moved all drivers to attach/detach methods
+ - made all drivers modular
+ - changed exp_ioctl to kcomedilib
+ - fixed NI pcidio 96 probing problems
+
+0.7.31
+
+ - Added etc/das1600.conf, etc/dt282x.conf
+ - Start of new attach/detach methods
+ - dt282x: uses new attach/detach methods
+ - das1600: changed comments to reflect actual code
+ - dt282x: DMA code rewritten
+ - dt282x: ao mode2 support added, with DMA
+ - ni_E: removed non-functional code
+
diff --git a/Documentation/comedi/FAQ b/Documentation/comedi/FAQ
new file mode 100644 (file)
index 0000000..611d686
--- /dev/null
@@ -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/Documentation/comedi/Hardware_Driver.HOWTO b/Documentation/comedi/Hardware_Driver.HOWTO
new file mode 100644 (file)
index 0000000..8472e83
--- /dev/null
@@ -0,0 +1,206 @@
+
+Hardware driver interface
+
+
+[ this is a little outdated -ds ]
+
+
+1. Introduction
+
+This comedi hardware driver writing HOWTO is written to help you
+write a driver for your particular choice of hardware.  You should
+be familiar with how comedi works from the user's point of view,
+i.e., from a process that is utilizing the comedi driver.
+
+This guide does not explain the details of things like reading
+and writing I/O ports, Linux kernel internals, or interrupt
+processing.  These issues are covered in other documents,
+specifically, the IO-Port-Programming mini-HOWTO, the Kernel
+Hacker's Guide, and the Linux source.
+
+
+2. The source tree
+
+As of comedi-0.5.0, hardware drivers need to be part of the
+source tree and be compiled with the rest of the comedi driver
+into the module comedi.o.  Later versions will hopefully support
+separate compiling/separate modules, etc.
+
+The source for the comedi module is located in module/, including
+the device independent part and each hardware driver.  A hardware
+driver, such as the "dt282x" driver, is typically located in the
+C source file of the same name.
+
+The hardware driver "dummy" is a stripped-down example that may be
+a good starting point for new hardware drivers.
+
+
+3. comedi_config and *_init()
+
+The file "module/comeditypes.c" has a list of the initialization
+functions for all the drivers.  You should add your init function
+to this list.  The top level Makefile and scripts/configure
+take care of all the defines.
+
+The purpose of comeditypes.c is to keep a list of available
+hardware drivers by keeping a list of available initialization
+functions.  Thus, when comedi_config is run, it issues the
+COMEDI_CONFIG ioctl() with the name of the driver and
+configuration parameters (I/O port address, irq, dma, etc.).
+The comedi driver then calls the initialization function of
+each hardware driver on the list.
+
+Inside the initialization function, you should perform the
+following tasks:
+
+   o  Check to see if you are the correct hardware driver
+      for the name specified in the comedi_devconfig structure.
+      Your hardware driver may consider several names as
+      "correct", and even behave differently depending on
+      the name given.  The idea here is to support different
+      cards in a series using different names, but using the
+      same hardware driver.  If you are not the correct
+      hardware driver, return a 0.
+
+   o  Announce that the hardware driver has begun initialization
+      by a printk("comedi%d: driver: ",minor);
+
+   o  Check and request the I/O port region, IRQ, DMA, and other
+      hardware resources.  It is convenient here if you verify the
+      existence of the hardware and the correctness of the other
+      information given.  Sometimes, unfortunately, this cannot
+      be done.
+
+   o  Fill in the comedi_device structure.
+
+   o  Allocate your private data structure and space for channel
+      configuration.
+
+   o  Configure each channel.  Each channel has three function
+      pointers:  one for triggering a single conversion (itrig),
+      one for triggering general conversions (trig), and one
+      for setting channel parameters (sp).  Each channel also has
+      a parameter linked list.
+      Parameters are added to this list via addparam().  All the
+      parameter routines are in param.c.  They are currently not
+      very efficient, so if you know of a better algorithm...
+      Look at the header files for parameter types that you may
+      wish to include.
+
+   o  Initialize the read buffer via comedi_initbuf().  The buffering
+      routines are in buffer.c, and support multiple processes
+      reading the same device.  The buffers are also dynamic, so
+      you don't have to worry about the hardware driver collecting
+      1M of data before the controlling process reads it.  I've been
+      using these routines for a while--they appear to be pretty
+      stable.  Most of the error messages you get from the buffer
+      routines indicate memory leaks.
+
+   o  Set mtrig in the comedi_device structure to the function that
+      is called for a channel scan trigger.
+
+   o  Tell the comedi driver the function to call when your driver
+      needs to be removed.
+
+   o  Return a 1.  If there were any errors along the way, you
+      should return the appropriate error number, but don't forget
+      to release any resources that you allocated.  (It will only
+      take one time to realize that if you don't release an I/O region,
+      you have to reboot to get it back.  (Or write a hack, as I did.))
+
+
+4. Set Parameter routines
+
+When the COMEDI_SETPARAM ioctl() is called, the comedi driver
+calls the setparameter routine of the channel involved.  Common
+settable parameters are input gain (input range) and setting bits
+on digital I/O lines as input or output.  The setparameter
+function should check the validity of the parameter type and
+value, and then call changeparam() to update the parameter
+linked lists.  If necessary, calls to update the hardware
+should be made.
+
+
+5. Triggering routines
+
+Like the setparameter routines, each channel has a trigger
+function that is called for a COMEDI_TRIG ioctl().  The trigger
+function is called with the comedi_trig structure, which
+includes the trigger type, number of samples, and one sample value
+(which may be read or written, depending on context).
+
+As of 0.5.0, there are two triggering routines per channel, one
+for TRIGNOW, and one for all the rest.  The reason this was done
+is because the TRIGNOW functions are exported to the rest of the
+kernel (i.e., for RTLinux).
+
+Trigger types are accompanied by the trigger variable, which
+is interpreted based on the trigger type.  The types of triggers
+(currently) available are:
+
+   o  COMEDI_TRIGNOW - causes one sample conversion to be
+      completed immediately.  The trigger variable and number of
+      samples are ignored.
+
+   o  COMEDI_TRIGCLK - causes conversions timed by a clock on
+      the hardware.  The trigger variable determines the clock
+      speed.
+
+   o  COMEDI_TRIGEXT - causes conversions to be triggered by
+      an external trigger provided by the hardware.  The trigger
+      variable determines the particular choice, if there is
+      more than one choice.
+Other trigger sources that may be defined in the future are
+analog triggers and comedi triggers.  Comedi triggers could include
+the 100 Hz interrupt, the rtc interrupt, general timing triggers,
+signals generated by other boards, etc.
+
+Except for COMEDI_TRIGNOW, your triggering routine should return
+after telling the hardware to perform the requested task.  For
+COMEDI_TRIGNOW on input, you should wait for the conversion to
+finish and return it in the ioctl structure, but do *NOT* report
+the sample via report_sample().
+
+Typically, you will poll the hardware or the hardware will
+generate interrupts to tell you when samples are ready.  The
+samples should be reported via report_sample().
+
+
+6. The remove function
+
+A driver is removed from service via a call to dev->rem().  The driver
+is expected to halt all conversions, put the hardware in a sane,
+off-line state, delete all channel parameters, free all allocated memory,
+and release any irq's and port addresses held.  If these things are
+not all done, there will be memory leaks, and more importantly, the
+driver will not configure properly if reused.
+
+
+A. Goals
+
+A few things to strive for:
+
+   o  Your hardware driver should be functional appropriate to
+      the resources allocated.  I.e., if the driver is fully
+      functional when configured with an IRQ and DMA, it should
+      still function moderately well with just an IRQ, or still
+      do minor tasks without IRQ or DMA.  Does your driver really
+      require an IRQ to do digital I/O?  Maybe someone will want
+      to use your driver *just* to do digital I/O and has no
+      interrupts available.
+
+   o  Drivers are to have absolutely *NO* global variables, mainly
+      because the existence of global variables immediately negates
+      any possibility of using the driver for two devices.  The
+      pointer dev->private should be used to point to a structure
+      containing any additional variables needed by a driver/device
+      combination.
+
+   o  Drivers should report errors and warnings via a printk line
+      that starts with "comedi%d: driver_name:" where %d is the
+      minor number of the device.
+
+
+
+
diff --git a/Documentation/comedi/command b/Documentation/comedi/command
new file mode 100644 (file)
index 0000000..43d7ac5
--- /dev/null
@@ -0,0 +1,150 @@
+
+Version 0.8.x will feature a replacement for the comedi_trig
+structure that allows much more flexible acquisition with
+applicable hardware.  Gone will be the days of "mode2" and
+friends.
+
+The replacement for mode0, i.e., immediate acquisition, is a
+new ioctl that is very similar to the old trigger ioctl, but
+just implements mode0 features.
+
+The other modes are replaced by the comedi "command" ioctl.
+The command structure allows you to specify what causes each
+action that is taken during acquisition.  The actions currently
+understood by comedi are:
+
+       - Start acquisition
+       - Stop acquisition
+       - Begin a scan
+       - End a scan
+       - Convert a sample
+
+A "scan" is a repeated measurement of N different channels.
+
+The different types of "triggers" or "trigger signals" that can
+cause events to occur are:
+
+       - TRIG_NONE - don't ever trigger
+       - TRIG_NOW - cause trigger to occur right now (or really soon)
+       - TRIG_FOLLOW - (see below)
+       - TRIG_TIME - trigger at specific time
+       - TRIG_TIMER - trigger at specific rate
+       - TRIG_COUNT - trigger when count reaches specific value
+       - TRIG_EXT - trigger on external signal
+       - TRIG_INT - trigger on an internal comedi signal
+
+Not all triggers are applicable to all events.  Supported triggers
+for specific events depends significantly on your particular
+device.  In addition, for every trigger type, there is a cooresponding
+argument that specifies the rate, the count, which external signal,
+etc.
+
+In particular, scan_end events will almost always be triggered on
+TRIG_COUNT, with the argument being the number of channels in the
+scan.  (Actually, samples in the scan, since on most boards you can
+measure a single channel multiple times in a scan.)  Also, until
+otherwise supported, start events can only be TRIG_NOW.
+
+TRIG_FOLLOW is a special type of trigger for scan_begin events that
+triggers on the next lower level trigger, in this case, the trigger
+for convert events.  It may or may not be supported.  Later, it may
+also be used for start events if you want to chain multiple commands.
+I might also consider using it for convert events, to indicate the
+board maximum rate.
+
+The command strucure is as follows:
+
+struct comedi_cmd_struct{
+       unsigned int subdev;
+       unsigned int flags;
+
+       unsigned int start_src;
+       unsigned int start_arg;
+
+       unsigned int scan_begin_src;
+       unsigned int scan_begin_arg;
+
+       unsigned int convert_src;
+       unsigned int convert_arg;
+
+       unsigned int scan_end_src;
+       unsigned int scan_end_arg;
+
+       unsigned int stop_src;
+       unsigned int stop_arg;
+
+       unsigned int *chanlist;         /* channel/range list */
+       unsigned int chanlist_len;
+
+       sampl_t *data;                  /* data list, size depends on subd flags */
+       unsigned int data_len;
+};
+
+subdev is the target subdevice.  flags are flags, similar to the
+old trigger structure.  chanlist, chanlist_len, data, and data_len
+are similar to the old trigger structure, although chanlist_len has
+been changed from n_chan.  data and data_len are used only by users
+who write kernel-space code, i.e., virtual comedi devices or RTLinux.
+
+
+Some examples for commands:
+
+Suppose you want to measure channels 1,2,3,4 at a rate of 10 khz, with
+the inter-sample time at 10 us (100 khz), and that you want to measure
+10000 scans.  This is analogous to the old mode2 acquisition.
+Initialization of the command structure to do this would include:
+
+       start_src       = TRIG_NOW;
+       start_arg       = 0;            // doesn't matter
+       scan_begin_src  = TRIG_TIMER;
+       scan_begin_arg  = 100000;       // == 100e3 nanoseconds
+       convert_src     = TRIG_TIMER;
+       convert_arg     = 10000;        // == 10e3 nanoseconds
+       scan_end_src    = TRIG_COUNTER;
+       scan_end_arg    = 4;            // == number of channels
+       stop_src        = TRIG_COUNTER;
+       stop_arg        = 10000;        // == 10000 scans
+
+If, instead, you wish to start continuous acquisition, and stop it with
+a comedi_cancel(), you would change the stop event trigger to:
+
+       stop_src        = TRIG_NONE;
+       stop_arg        = 0;
+
+Suppose you want to use an external signal to trigger scan_begin events.
+Then you would change the scan_begin event trigger to:
+
+       scan_begin_src  = TRIG_EXT;
+       scan_begin_arg  = 2;            // external trigger #2
+
+The number of possible external trigger lines is board dependent.  Your
+board may only have one non-configurable digital external trigger, or
+it may support complicated selection of external triggering, including
+analog triggers at multiple levels, edge or level digital triggers,
+synchonization signals with other boards, etc.  Eventually, boards
+that support complicated triggering will have a triggering subdevice
+with which you can configure all the different features.
+
+Here's some pseudo-code that describes how the device should interpret
+your command:
+
+do_command()
+{
+       while(!start_trigger)
+               do_nothing();
+       
+       while(!stop_trigger){
+               if(!scan_begin_trigger)
+                       continue;
+               
+               while(!scan_end_trigger){
+                       while(!convert_trigger)
+                               do_nothing();
+                       do_conversion();
+               }
+       }
+}
+
+The fencepost issues, i.e., what happens when you have conflicting
+triggers, is device-dependent.
+
diff --git a/Documentation/comedi/drivers.txt b/Documentation/comedi/drivers.txt
new file mode 100644 (file)
index 0000000..808a62a
--- /dev/null
@@ -0,0 +1,405 @@
+
+This file contains information about specific drivers, their
+features, and limitation.
+
+8255.o:
+
+Author: ds
+Status: only mode 0 supported
+
+The classic in digital I/O.  Three channels of 8 bit digital I/O,
+each channel is I/O configurable, channels 0 and 1 in 8 bit units,
+channel 2 in 4 bit units.  The driver does not support modes 1 or 2
+yet, since I don't really understand how they would potentially be used.
+(Send me email if you want to use these modes.)  If and when
+modes 1 and 2 are supported, there is a strong possibility that the
+3rd channel will be split into two 4-bit channels.  (Refer to the
+8255 spec for clues as to why.)
+
+You should configure this driver if you plan to use a board that
+has an 8255 chip.  For multifunction boards, the main driver will
+configure the 8255 subdevice automatically.
+
+
+
+ni-atmio.o: National Instruments AT-MIO-E series  (all boards)
+
+Author: ds
+Status: mainly limited by Comedi infrastructure
+
+The isapnptools package is required to use this board.  Use isapnp to
+configure the I/O base for the board, and then pass the same value as
+a parameter in comedi_config.  A sample isapnp.conf file is included
+in the etc/ directory.
+
+Assuming that the NI spec is correct, the driver should correctly
+identify every board in the series.  Each channel should have the
+appropriate parameters, i.e., input/output ranges, number of bits,
+etc.  If the driver fails to recognize your card or does not have
+the correct parameters, please contact me.
+
+Comedilib includes a utility to autocalibrate these boards.  The
+boards seem to boot into a state where the all calibration DACs
+are at one extreme of their range, thus the default calibration
+is terrible.  Calibration at boot is strongly encouraged.
+
+
+
+das08.o: ComputerBoards, Keithley Metrabyte DAS-08
+
+Authors: Warren Jasper, ds
+Status: unknown
+
+
+
+das08jr.o: ComputerBoards DAS-08jr-AO
+
+Authors: Jochen Küpper <jochen@pc1.uni-duesseldorf.de>, ds
+Status: unknown
+
+
+
+
+das16.o: CIO-DAS16 (& compatibles)
+
+Authors: Sam Moore, Warren Jasper, ds
+Status: unknown
+
+
+
+das1600.o: Keithley Metrabyte DAS1600 (& compatibles)
+
+Author: Anders Blomdell <anders.blomdell@control.lth.se>
+
+The driver recognizes the following board names:
+       das1601/12
+       das1602/12
+       das1602/16
+
+The file etc/das1600.conf is useful for configuring this board.
+
+Options (probably wrong, verify with source):
+  1   Board base address
+  2   IRQ
+  3   DMA level select configuration
+       0 == DMA level 1
+       1 == DMA level 1
+        3 == DMA level 3
+  4   Crystal select configuration
+        0 == 1 MHz
+        1 == 1 MHz
+        10 == 10 MHz
+  5   Input configuration
+        0 == differential
+        1 == single-ended
+  6   Analog input range configuration
+        0 == bipolar 10V  (-10V -- +10V)
+        1 == unipolar 10V  (0V -- +10V)
+  7   Analog output 0 range configuration
+        0 == bipolar 10V  (-10V -- +10V)
+        1 == bipolar 5V  (-5V -- +5V)
+        2 == bipolar user supplied  (-xV -- +xV)
+        3 == unipolar 10V  (0V -- +10V)
+        4 == unipolar 5V  (0V -- +5V)
+        5 == unipolar user supplied  (0V -- +xV)
+  8   Analog output 1 range configuration
+        0 == bipolar 10V  (-10V -- +10V)
+        1 == bipolar 5V  (-5V -- +5V)
+        2 == bipolar user supplied  (-xV -- +xV)
+        3 == unipolar 10V  (0V -- +10V)
+        4 == unipolar 5V  (0V -- +5V)
+        5 == unipolar user supplied  (0V -- +xV)
+
+
+
+das6402.o: Keithley Metrabyte DAS6402 (& compatibles)
+
+Author: Oystein Svendsen <svendsen@pvv.org>
+Status: unknown
+
+
+
+
+
+
+dt2801.o: Data Translation DT2801 series
+         (2801, 2805, 2808, 2809, 2818, and DT01-EZ)
+
+Author: ds
+Status: might work
+
+
+
+
+
+dt2811.o: Data Translation DT2811
+
+Author: ds
+Status: might work
+
+
+
+dt2814.o: Data Translation DT2814
+
+Author: ds
+Status: complete
+
+This card has 16 analog inputs multiplexed onto a 12 bit ADC.  There
+is a minimally useful onboard clock.  The base frequency for the
+clock is selected by jumpers, and the clock divider can be selected
+via programmed I/O.  Unfortunately, the clock divider can only be
+a power of 10, from 1 to 10^7, of which only 3 or 4 are useful.  In
+addition, the clock does not seem to be very accurate.
+
+Also, the board does not have a fifo or do DMA, so timed mode
+is not supported.
+
+
+
+dt2815.o: Data Translation DT2815
+
+Author: ds
+Status: mostly complete, untested
+
+I'm not sure anyone has ever tested this board.  If you have information
+contrary, please update.
+
+Options
+  1   Board base address
+  2   IRQ (not applicable)
+  3   Voltage unipolar/bipolar configuration
+        0 == unipolar 5V  (0V -- +5V)
+       1 == bipolar 5V  (-5V -- +5V)
+  4   Current offset configuration
+        0 == disabled  (0mA -- +32mAV)
+        1 == enabled  (+4mA -- +20mAV)
+  5   Firmware program configuration
+        0 == program 1 (see manual table 5-4)
+        1 == program 2 (see manual table 5-4)
+        2 == program 3 (see manual table 5-4)
+        3 == program 4 (see manual table 5-4)
+  6   Analog output 0 range configuration
+        0 == voltage
+        1 == current
+  7   Analog output 1 range configuration
+        0 == voltage
+        1 == current
+  8   Analog output 2 range configuration
+        0 == voltage
+        1 == current
+  9   Analog output 3 range configuration
+        0 == voltage
+        1 == current
+  10  Analog output 4 range configuration
+        0 == voltage
+        1 == current
+  11  Analog output 5 range configuration
+        0 == voltage
+        1 == current
+  12  Analog output 6 range configuration
+        0 == voltage
+        1 == current
+  13  Analog output 7 range configuration
+        0 == voltage
+        1 == current
+
+
+
+dt2817.o: Data Translation DT2817
+
+Author: ds
+Status: complete
+
+A very simple digital I/O card.  Four banks of 8 lines, each bank
+is configurable for input or output.  One wonders why it takes a
+50 page manual to describe this thing.
+
+The driver (which, btw, is much less than 50 pages) has 1 subdevice
+with 32 channels, configurable in groups of 8.
+
+
+
+dt282x.o: Data Translation DT2821 series
+       (including DT-EZ)
+
+Author: ds
+Status: complete
+
+This driver recognizes the following board names:
+       dt2821
+       dt2823
+       dt2824-pgh
+       dt2824-pgl
+       dt2825
+       dt2827
+       dt2828
+       dt21-ez
+       dt23-ez
+       dt24-ez
+       dt24-ez-pgl
+
+The driver supports numerous options, which are documented
+in the source, as well as the file etc/dt282x.conf.
+
+
+
+dt3000.o: Data Translation DT3000 series
+
+Author: ds
+Status: untested
+
+
+
+
+multiq3.o: Quanser Consulting MultiQ-3
+
+Author: Anders Blomdell <anders.blomdell@control.lth.se>
+Status: works
+
+
+
+
+ni_pcidio.o: National Instruments PCI-DIO32HS, PCI-DIO96, PCI-6533
+
+Author: ds
+Status: works in immediate mode
+
+The DIO-96 appears as four 8255 subdevices.  See the 8255
+driver notes for details.
+
+The DIO32HS board appears as one subdevice, with 32 channels.
+Each channel is individually I/O configurable.  The channel order,
+as one might guess, is 0=A0, 1=A1, 2=A2, ... 8=B0, 16=C0, 24=D0.
+
+DMA is halfway completed, but not operational, for the PCI-DIO32HS.
+This driver could be easily modified to support AT-MIO32HS and
+AT-MIO96.
+
+
+
+
+parport.o: Standard PC parallel port
+
+Author: ds
+Status: works in immediate mode
+
+A cheap and easy way to get a few more digital I/O lines.  Steal
+additional parallel ports from old computers or your neighbors'
+computers.
+
+
+Parallel Output Port Lines:
+
+pin    subdev  chan    aka
+---    ------  ----    ---
+1      2       0       strobe
+2      0       0       data 0
+3      0       1       data 1
+4      0       2       data 2
+5      0       3       data 3
+6      0       4       data 4
+7      0       5       data 5
+8      0       6       data 6
+9      0       7       data 7
+10     1       3       acknowledge
+11     1       4       busy
+12     1       2       output
+13     1       1       printer selected
+14     2       1       auto LF
+15     1       0       error
+16     2       2       init
+17     2       3       select printer
+18-25  ground
+
+
+Notes:
+
+Channel 0 and 2 are output, channel 1 is input.  I know that it
+is possible to change this with ECP/EPP parallel ports, but this
+driver is a cheap hack.
+
+Pins 13 and 14 are inverted once by comedi and once by the
+hardware, thus cancelling the effect.
+
+Pin 1 is a strobe, thus acts like one.  There's no way in software
+to change this, at least on a standard parallel port.
+
+Please report any sucesses/failures.
+
+
+
+ni_pcimio.o: National Instruments PCI-MIO-E series (all boards)
+
+Author: ds
+Status: mainly limited by Comedi infrastructure
+
+These boards are almost identical to the AT-MIO E series, except that
+they use the PCI bus instead of ISA (i.e., AT).  See the notes above for
+ni_atmio.o for additional information about these boards.
+
+Comedi knows the PCI ID codes for many of the boards in this series,
+but the NI documentation is incomplete in this matter.  If you have
+a PCI-MIO board that Comedi doesn't recognize, send me the PCI device
+ID, as can be found in /proc/pci or the output of lspci.  The vendor
+code for National Instruments is 0x1093.  I will include the ID in
+the next version.
+
+DMA is halfway completed, but not yet operational.
+
+
+
+
+pcl711.o: PC-LabCard PCL-711 and 711b, AdSys ACL-8112
+
+Authors: ds, Janne Jalkanen <jalkanen@cs.hut.fi>, Eric Bunn <ebu@cs.hut.fi>
+Status: mostly complete
+
+This driver recognizes the following board names:
+
+       pcl711          PCL-711
+       pcl711b         PCL-711B
+       acl8112dg       ACL-8112DG
+       acl8112hg       ACL-8112HG
+
+Since these boards do not have DMA or FIFOs, only immediate mode is
+supported.
+
+
+
+pcl725.o: PC-LabCard PCL-725 (& compatibles)
+
+Author: ds
+Status: unknown
+
+
+
+
+pcl726.o: PC-LabCard PCL-726 (& compatibles)
+       Advantech PCL-726
+       ADLink Technology ACL-6126
+
+Author: ds
+Status: untested
+
+Currently, the driver doesn't recognize bipolar operation of DAC's.
+Interrupts are not supported.
+
+
+
+rti800.o: Analog Devices RTI-800/815
+
+Author: ds
+Status: unknown
+
+See the source for configuration details.
+
+
+
+rti802.o: Analog Devices RTI-802
+
+Author: Anders Blomdell <anders.blomdell@control.lth.se>
+Status: works
+
+
+
+
diff --git a/Documentation/comedi/mode-info b/Documentation/comedi/mode-info
new file mode 100644 (file)
index 0000000..e9109ab
--- /dev/null
@@ -0,0 +1,168 @@
+From owner-comedi@stm.lbl.gov  Tue Jun 29 00:56:15 1999
+Received: (from majordom@localhost)
+       by stm.lbl.gov (8.8.7/8.8.7) id AAA29747
+       for comedi-list; Tue, 29 Jun 1999 00:56:15 -0700
+Received: (from ds@localhost)
+       by stm.lbl.gov (8.8.7/8.8.7) id AAA29742;
+       Tue, 29 Jun 1999 00:56:14 -0700
+Message-ID: <19990629005613.A29725@stm.lbl.gov>
+Date: Tue, 29 Jun 1999 00:56:13 -0700
+From: David Schleef <ds@stm.lbl.gov>
+To: Tomasz Motylewski <motyl@stan.chemie.unibas.ch>
+Cc: comedi@stm.lbl.gov
+Subject: Re: pcl-711 driver ISR.
+References: <Pine.LNX.3.96.990629020900.18849h-100000@crds.chemie.unibas.ch>
+Mime-Version: 1.0
+Content-Type: text/plain; charset=us-ascii
+X-Mailer: Mutt 0.91.1
+In-Reply-To: <Pine.LNX.3.96.990629020900.18849h-100000@crds.chemie.unibas.ch>; from Tomasz Motylewski on Tue, Jun 29, 1999 at 02:22:26AM +0200
+Sender: owner-comedi@stm.lbl.gov
+Precedence: bulk
+Status: RO
+Content-Length: 5035
+Lines: 143
+
+On Tue, Jun 29, 1999 at 02:22:26AM +0200, Tomasz Motylewski wrote:
+> 
+> Is there any documentation for _ai_mode0, 1 2 3 4 etc? Do they mean
+> the same for different cards ?
+
+Found it.  It has a little extra info, but that shouldn't hurt.
+Here it is:
+
+-----
+> 
+> 1. What are the different modes?  2, 3, and 4 seem pretty well
+> explained, but I guess mode 0 is a one-shot sample, and mode is 1...
+> what?
+
+0 is one-shot.  Actually, some of the drivers will return as many
+samples as you ask (i.e., trig.n), for the channels you specify.  Others
+will limit it to a reasonable number that will finish in 1-10 ms, and
+the rest only return 1, for the first channel specified.  The idea here
+is "at the convenience of the driver, preferably fast."
+
+mode 1 is timed acquisition, with a single timer.  For example, if
+you request channels 2, 4, and 6, in mode 1, with the number of samples
+(trig.n) = 2, and a timer value that corresponds to 100 us, the driver
+programs the device to measure inputs at these times:
+       time            chan
+       +0 us           2
+       +100 us         4
+       +200            6
+       +300            2
+       +400            4
+       +500            6
+If you board can support mode 2, mode 1 is not necessary.  Comedilib
+will eventually emulate it.
+
+mode 2 is similar, but has 2 timers, a major timer (trig.timeval)
+and a minor timer (trig.timeval1).  Taking the previous example, but
+with a major timer of 1000 us and a minor timer of 100 us, inputs
+are measured at these times:
+       time            chan
+       +0 us           2
+       +100 us         4
+       +200            6
+       +1000           2
+       +1100           4
+       +1200           6
+
+mode 3 is like mode 1, except that an external trigger is used.  If
+t(n) represents the time of the nth trigger, inputs are measured at
+the following times:
+       time            chan
+       t(1)            2
+       t(2)            4
+       t(3)            6
+       t(4)            2
+       t(5)            4
+       t(6)            6
+
+mode 4 uses an external trigger and a minor timer.  With a minor
+timer of 100 us, we get:
+       time            chan
+       t(1)            2
+       t(1)+100 us     4
+       t(1)+200 us     6
+       t(2)            2
+       t(2)+100 us     4
+       t(2)+200 us     6
+
+As a bit of background, mode 2 & 4 are the most useful.  Mode 1
+is the only supported by many boards.  Mode 3 is the only supported
+by a few boards.  Mode 3 could be useful to synchronize multiple
+boards, if the master board is capable of exporting a trigger signal.
+
+If you have an application that doesn't fit into the existing
+modes, and your board supports it, I'm content with adding more
+modes.
+
+> 
+> 2. How is the channel mask to be interpreted?  If multiple channels are
+> specified in the mask, should successive bytes in the data array match
+> to different channels?
+
+CR_MASK() packs range, analog reference, and channel information into
+a 32-bit integer.  An array of these integers are passed from the program
+to comedi, to be used as mentioned above.  Samples are put into the
+data array in time order.
+
+> 
+> 3. If ntrig is zero, does that mean sample nothing, or keep sampling
+> forever?
+
+I haven't decided.  Since comedi doesn't currently support continuous
+acquisition, I have time to decide.  It did previously, but I never
+wrote buffer streaming code for the 0.6 series.  It's on my list.
+The alternative to specifying continuous is ((unsigned int)(-1)).
+
+> 
+> 4. How should a card like mine be handled, where there's a single A/D
+> converter servicing multiple channels?  Should the driver attempt to
+> multiplex everything together, and then finally return an error if a
+> request is made that just can't be squeezed in?
+
+If I understand what you are saying, yes.  I.e., if in mode 2, you ask
+for 10 channels, major timer 50 us, minor timer of 10 us, then yes,
+you should return an error.  In actuality, most drivers aren't that
+mature yet.
+
+> 
+> 5. Anything else you can send me that's useful?  I've got comedi and
+> comedilib 0.7.0.
+
+Not really.  I'm trying to pause comedi/comedilib development for
+a little while so I can write documentation, so maybe soon there'll
+be more info.
+
+Timer values are currently board dependent.  Most boards use dividers
+off a frequency like 20 Mhz, but some have multiple clocks and/or
+prescalers.  I'd like to just specify timers as (say) nanoseconds,
+and let the boards do the division, but that limits timers to ~4.29
+seconds.
+
+About ranges, range info, range types, etc.:  Typical boards allow
+you to specify an input gain on the A/D converter, and possibly
+also unipolar/bipolar.  Collectively, these are "input ranges".  A
+board that allows 4 different gain settings, and unipolar/bipolar
+setting, would have 8 available ranges, numbered 0-7.  This range
+specification is what you give to CR_PACK().  Not all boards are
+identical, so each subdevice (or channel, sometimes) has a
+range_type, to determine what voltages your range specification
+cooresponds to.  The low bits of range_type give the number
+of ranges available.  The high bits are used by the comedi module
+when it is asked to return an array of comedi_krange structures;
+these structures contain the max and min voltages and a unit
+specification for each range.
+
+
+
+
+dave...
+
+
+****
+Sent by the Comedi mailing list - <comedi@stm.lbl.gov>  To unsubscribe,
+send mail to <comedi-request@stm.lbl.gov> with "unsubscribe" in the body.
+
diff --git a/Documentation/comedi/notes/README b/Documentation/comedi/notes/README
new file mode 100644 (file)
index 0000000..2320936
--- /dev/null
@@ -0,0 +1,3 @@
+
+These are some notes probably only decipherable by me
+
diff --git a/Documentation/comedi/notes/atmio_notes b/Documentation/comedi/notes/atmio_notes
new file mode 100644 (file)
index 0000000..62c5823
--- /dev/null
@@ -0,0 +1,60 @@
+
+
+at-mio-64f-5   320487.pdf      has info
+at-mio-16x     320640b.pdf     has info
+at-mio-16d     320489.pdf      has info
+at-mio-16f-5   320266.pdf      has info
+at-mio-16      320476.pdf      no info
+
+
+E series:
+
+see ni_E.c
+
+
+
+
+E series:
+
+issues:
+
+- extra interrupt when changing from NE to HF FIFO mode
+
+ai reset registers:
+
+ AI_Command_1_Register
+ AI_Command_2_Register
+
+ AI_Mode_1_Register
+ AI_Mode_2_Register
+ AI_Mode_3_Register
+ AI_START_STOP_Select_Register
+
+ AI_Output_Control_Register
+ AI_Personal_Register
+ AI_Trigger_Select_Register
+
+
+ai mode registers:
+
+ 2:
+ AI_START_STOP_Select_Register
+ AI_Mode_2_Register
+ AI_Mode_3_Register
+ AI_Command_1_Register
+ AI_Command_2_Register
+
+ 4:
+ AI_START_STOP_Select_Register
+ AI_Mode_2_Register
+ AI_Command_1_Register
+
+ai_reset():
+
+ AI_Personal_Register
+ AI_Output_Control_Register
+ AI_Trigger_Select_Register
diff --git a/Documentation/comedi/notes/das b/Documentation/comedi/notes/das
new file mode 100644 (file)
index 0000000..e3334e6
--- /dev/null
@@ -0,0 +1,146 @@
+
+
+/* cio-dac08 */
+/* cio-dac16 */
+
+/*
+       08 has 8 channels, 16 has 16
+       can do simultaneous update
+ */
+
+#define DAC08_LSB(a)           (2*(a)+0)
+#define DAC08_MSB(a)           (2*(a)+1)
+
+
+
+
+/* cio-das16jr */
+
+/*
+       has a 8254
+*/
+
+read
+
+0      a/d low
+1      a/d hi
+2      mux settings
+3      digital 4 in
+4-7    unused
+8      a/d status
+9      control settings
+10     unused
+11     gain and range
+12     counter 0
+13     counter 1
+14     counter 2
+15     unused
+
+write
+
+0      start a/d
+1      unused
+2      mux scan ctrl
+3      digital 4 out
+4-7    unused
+8      unused
+9      control
+10     counter source control
+11     gain and range
+12     ctr 0 load
+13     ctr 1 load
+14     ctr 2 load
+15     8254 counter control
+
+
+
+
+
+computer boards current list of products:
+
+ISA:
+
+(dio)
+CIO-DI48
+CIO-DI96
+CIO-DI192
+CIO-DIO24
+CIO-DIO24H
+CIO-DIO/CTR3
+CIO-DIO48
+CIO-DIO48H
+CIO-DIO96
+CIO-DIO192
+CIO-DO24DD
+CIO-DO48DD
+CIO-DO48H
+CIO-DO96H
+CIO-DO192H
+CIO-PDMA16
+
+(ao)
+CIO-DAC02
+CIO-DAC02/16
+CIO-DAC08
+CIO-DAC16
+CIO-DDA06/16
+CIO-DDA06/JR
+CIO-DDA06/JR/16
+
+(ai)
+CIO-DAS08              *
+CIO-DAS08-AO
+CIO-DAS08/JR/16-AO
+CIO-DAS08PGH           *
+CIO-DAS08PGL           *
+CIO-DAS802/16
+CIO-DAS1402/16
+CIO-DAS16
+CIO-DAS16F
+CIO-DAS16/330
+CIO-DAS16/JR
+CIO-DAS16/M1
+CIO-DAS6402/12
+CIO-DAS6402/16
+
+
+
+PCI:
+
+(dio)
+PCI-DIO24
+PCI-DIO24H
+PCI-DIO24H/CTR3
+PCI-DIO48H/CTR15
+PCI-DIO48H
+PCI-DIO96H
+PCI-PDISO8
+PCI-PDISO16
+
+(ao)
+PCI-DDA08/12
+PCI-DDA04/12
+PCI-DDA02/12
+
+(ai)
+PCI-DAS1200
+PCI-DAS1001
+PCI-DAS1002
+PCI-DAS1200/JR
+PCI-DAS1602/12
+PCI-DAS1602/16
+PCI-DAS1602/16/JR
+PCI-DDA02/12
+PCI-DDA04/12
+PCI-DDA08/12
+PCI-DAS64/M3/16
+PCI-DAS64/M3/16/JR
+PCI-DAS64/M2/16
+PCI-DAS64/M2/16/JR
+PCI-DAS64/M1/16
+PCI-DAS64/M1/16/JR
+PCI-DAS6402/16
+PCI-DAS6402/16/JR
+PCI-DAS08
+
+
diff --git a/Documentation/comedi/notes/testing b/Documentation/comedi/notes/testing
new file mode 100644 (file)
index 0000000..a6c51d5
--- /dev/null
@@ -0,0 +1,27 @@
+
+*_init():
+
+each subdev should init fields:
+       type
+       n_chan
+       subdev_flags
+       maxdata || maxdata_list
+       flags || flag_list
+       range_type || range_type_list
+       trig
+
+    (optional)
+       timer_type
+       len_chanlist
+       maxbufsz
+       cancel
+
+
+check subdev_flags, RT, etc.
+
+check for mem leaks
+
+check that each synchronous function returns 1
+
+check that comedi_done is eventually called.
+
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..8a3ff64
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,90 @@
+
+Prerequisites:
+       - a machine capable of compiling Linux modules
+       - a correctly installed kernel source tree.  Specifically,
+         you need to have the file /usr/src/linux/.config and
+         /usr/src/linux/include/linux/autoconf.h.  If these are
+         not present, you can create them by compiling a new
+         kernel.  Some distributions don't provide these by
+         default.
+       - a 2.0, 2.2, or 2.3 series kernel.  Some development
+         kernels may not work, since I don't keep very good
+         track of them.
+
+Configure using 'make'.  The first time you run make, it will take
+you through configuration options similar to compiling a linux
+kernel.  If you need to run the configuration again, use 'make config'.
+
+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:
+       /lib/modules/<<kernel version>>/misc/comedi.o
+       /lib/modules/<<kernel version>>/misc/<<driver files>>.o
+       /usr/sbin/comedi_config
+       /usr/include/comedi.h
+If you chose to install in /usr/local instead, it will, of course,
+install in /usr/local instead of /usr.  The modules are still installed
+in /lib/modules.
+
+You need to create device files to access the hardware from a
+user process.  These can be created using 'make dev'.  The following
+special files will be created:
+       /dev/comedi0
+       /dev/comedi1
+       /dev/comedi2
+       /dev/comedi3
+
+To use comedi, the driver module must be loaded into the kernel.
+In general, this is done by a command similar to
+
+       /sbin/modprobe <<driver>>.o
+
+If your module dependencies are set up correctly, this will load
+both comedi.o and your driver.
+
+In this version, the default behavior when the module is loaded is
+to _not_ configure it automatically.  This may change soon.  For
+many boards, you need to supply additional information, such as
+I/O address, IRQ, and possibly DMA channels.  The following commands
+are examples:
+
+       /usr/sbin/comedi_config /dev/comedi0 dt282x 0x240,3
+       /usr/sbin/comedi_config /dev/comedi1 atmio-E 0x260,4
+       /usr/sbin/comedi_config /dev/comedi2 dt2817 0x228
+       /usr/sbin/comedi_config /dev/comedi0 pcimio-E
+
+Try a 'man comedi_config' for information on how to use
+this utility.  IRQs can be automatically detected for some hardware,
+although this is not recommended, for the same reasons as other
+hardware.
+
+If you like to autoload your modules, put the lines
+
+       alias char-major-98 comedi
+       post-install comedi /etc/comedi.conf
+
+and create a script /etc/comedi.conf, which can be a list of commands
+to configure the board.  If you have a National Instruments AT-MIO or
+PCI-MIO board, you probably will want to run the autocalibration tool
+that is part of comedilib in this script.
+
+To write programs that use comedi, there are demo programs included
+with comedilib.
+
+
+Upgrading:
+
+From versions prior to 0.6.0, you will need to edit and recompile
+all programs that use comedi or comedilib, since the names of
+functions and ioctls have changed.
+
+From versions prior to 0.5.0, you will need to recompile all programs
+that use comedi or comedilib, since the interface to both of these has
+changed.  No changes should need to be made to the source of the
+programs.  The format for parameters of comedi_config has changed.
+
+From versions prior to 0.4.0, you will need to run 'make dev' again
+to recreate /dev/comedi*, since the major number has changed.
+
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..627e3c4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,100 @@
+
+# Makefile for comedi
+
+VERS1 = 0
+VERS2 = 7
+VERS3 = 36
+
+INSTALLDIR=/usr
+
+LINUXDIR = /usr/src/linux
+
+TOPDIR := $(shell if [ "$$PWD" != "" ]; then echo $$PWD; else pwd; fi)
+
+.EXPORT_ALL_VARIABLES:
+
+CFLAGS = -Wall -O2 -Wstrict-prototypes
+CFLAGS += -D__KERNEL__ -I $(LINUXDIR)/include -I $(TOPDIR)/include -I .
+
+ifeq ($(CONFIG_SMP),y)
+CFLAGS += -D__SMP__
+endif
+
+CONFIG_SHELL := sh
+
+ifeq (.config,$(wildcard .config))
+include .config
+include .uts_version
+include $(LINUXDIR)/.config
+all2:  modules comedi_config
+else
+all2:  config
+endif
+
+SUBDIRS := comedi
+
+DOCFILES= README INSTALL drivers `find doc -type f`
+
+comedi_config: dummy
+       $(MAKE) -C comedi_config
+
+config:        dummy
+       scripts/Configure
+
+install:       dummy
+ifeq (/lib/modules/${UTS_VERSION},$(wildcard /lib/modules/${UTS_VERSION}))
+       install -d /lib/modules/${UTS_VERSION}/misc
+       install modules/*.o /lib/modules/${UTS_VERSION}/misc
+       depmod -a ${UTS_VERSION}
+else
+       # ***
+       # *** Could not install comedi.o into /lib/modules/${UTS_VERSION}/misc
+       # *** Please install by hand.
+       # ***
+endif
+       install -m 755 comedi_config/comedi_config ${INSTALLDIR}/sbin
+       install -d ${INSTALLDIR}/include
+       (cd include;install -m 644 comedi.h ${INSTALLDIR}/include)
+       install man/comedi.7 ${INSTALLDIR}/man/man7
+       install man/comedi_config.8 ${INSTALLDIR}/man/man8
+#      install -d ${INSTALLDIR}/doc/comedi
+#      install ${DOCFILES} ${INSTALLDIR}/doc/comedi
+
+lpr:   dummy
+       find . -name '*.[chs]'|xargs enscript -2r -pit.ps
+
+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*
+
+
+MODFLAGS += -DMODULE
+
+modules:       $(patsubst %, _mod_%, $(SUBDIRS))
+
+$(patsubst %, _mod_%, $(SUBDIRS)) : dummy
+       $(MAKE) -C $(patsubst _mod_%, %, $@) CFLAGS="$(CFLAGS) $(MODFLAGS)" MAKING_MODULES=1 modules
+
+clean:
+       rm -f core `find . -name '*.[oas]'`
+       rm -f core `find . -name '.*.flags' -print`
+       rm -f comedi/range.h comedi/mk_range comedi/range.def
+       rm -f comedi_config/comedi_config
+
+distclean:     clean
+       rm -f .depend `find . -name .depend -print`
+       rm -f core `find . \( -name '*.orig' -o -name '*.rej' -o -name '*~' \
+               -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \
+               -o -name '.*.rej' -o -name '.SUMS' -o -size 0 \) -print` TAGS
+       rm -f .config .uts_version include/config.h
+       rm -f modules/*
+
+include $(TOPDIR)/Rules.make
+
+
+dummy:
diff --git a/NOTES b/NOTES
new file mode 100644 (file)
index 0000000..2b21d06
--- /dev/null
+++ b/NOTES
@@ -0,0 +1,18 @@
+
+0.7.36
+
+I *really* changed everything around.  Basically, I changed all the
+Makefiles to fit in with the Linux kernel Makefiles.  Ideally, you
+could now move the comedi directory to /usr/src/linux/drivers/char,
+change a few files, and compile comedi into your kernel.
+
+One of the side effects of changing the makefiles is that all
+the drivers are compiled as separate modules.  Another side effect
+is that I'd bet comedi doesn't compile correctly for 2.0.x kernels
+and possibly RTL and/or RTAI.
+
+I almost certainly broke a bunch of drivers.  Let me know which
+ones.  
+
+
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..a0d768c
--- /dev/null
+++ b/README
@@ -0,0 +1,23 @@
+
+For installation instructions, look at the file INSTALL.  For notes
+of what is happening/changing in recent comedi releases, see the
+file NOTES.
+
+This distribution contains just the Comedi kernel module.  You will
+almost certainly also want to download Comedilib, which is a user
+space library, a few utilities, and some example programs.
+
+Comedi has a mailing list.  Send "subscribe comedi" to
+majordomo@stm.lbl.gov to subscribe.  Traffic is light, and mainly
+questions/answers about comedi installation, bugs, and programming.
+General questions about data acquisition are also welcome.
+
+Comedi also has a web page, at http://stm.lbl.gov/comedi.  New versions
+of comedi can be found on the ftp site ftp://stm.lbl.gov/pub/comedi.
+
+Comedi may be freely distibuted and modified in accordance with the
+GNU General Public License.
+
+The person behind all this misspelled humor is David Schleef
+<ds@stm.lbl.gov>, or http://stm.lbl.gov/~ds.
+
diff --git a/Rules.make b/Rules.make
new file mode 100644 (file)
index 0000000..f55ea43
--- /dev/null
@@ -0,0 +1,301 @@
+#
+# This file contains rules which are shared between multiple Makefiles.
+#
+
+#
+# False targets.
+#
+.PHONY: dummy
+
+#
+# Special variables which should not be exported
+#
+unexport EXTRA_ASFLAGS
+unexport EXTRA_CFLAGS
+unexport EXTRA_LDFLAGS
+unexport EXTRA_ARFLAGS
+unexport SUBDIRS
+unexport SUB_DIRS
+unexport ALL_SUB_DIRS
+unexport MOD_SUB_DIRS
+unexport O_TARGET
+unexport O_OBJS
+unexport L_OBJS
+unexport M_OBJS
+# intermediate objects that form part of a module
+unexport MI_OBJS
+unexport ALL_MOBJS
+# objects that export symbol tables
+unexport OX_OBJS
+unexport LX_OBJS
+unexport MX_OBJS
+unexport MIX_OBJS
+unexport SYMTAB_OBJS
+
+unexport MOD_LIST_NAME
+
+#
+# Get things started.
+#
+first_rule: sub_dirs
+       $(MAKE) all_targets
+
+#
+# Common rules
+#
+
+%.s: %.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -S $< -o $@
+
+%.i: %.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -E $< > $@
+
+%.o: %.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -c -o $@ $<
+       @ ( \
+           echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@))),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@))))' ; \
+           echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
+           echo 'endif' \
+       ) > $(dir $@)/.$(notdir $@).flags
+
+%.o: %.s
+       $(AS) $(ASFLAGS) $(EXTRA_CFLAGS) -o $@ $<
+
+#
+#
+#
+all_targets: $(O_TARGET) $(L_TARGET)
+
+#
+# Rule to compile a set of .o files into one .o file
+#
+ifdef O_TARGET
+ALL_O = $(OX_OBJS) $(O_OBJS)
+$(O_TARGET): $(ALL_O)
+       rm -f $@
+ifneq "$(strip $(ALL_O))" ""
+       $(LD) $(EXTRA_LDFLAGS) -r -o $@ $(ALL_O)
+else
+       $(AR) rcs $@
+endif
+       @ ( \
+           echo 'ifeq ($(strip $(subst $(comma),:,$(EXTRA_LDFLAGS) $(ALL_O))),$$(strip $$(subst $$(comma),:,$$(EXTRA_LDFLAGS) $$(ALL_O))))' ; \
+           echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
+           echo 'endif' \
+       ) > $(dir $@)/.$(notdir $@).flags
+endif # O_TARGET
+
+#
+# Rule to compile a set of .o files into one .a file
+#
+ifdef L_TARGET
+$(L_TARGET): $(LX_OBJS) $(L_OBJS)
+       rm -f $@
+       $(AR) $(EXTRA_ARFLAGS) rcs $@ $(LX_OBJS) $(L_OBJS)
+       @ ( \
+           echo 'ifeq ($(strip $(subst $(comma),:,$(EXTRA_ARFLAGS) $(LX_OBJS) $(L_OBJS))),$$(strip $$(subst $$(comma),:,$$(EXTRA_ARFLAGS) $$(LX_OBJS) $$(L_OBJS))))' ; \
+           echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
+           echo 'endif' \
+       ) > $(dir $@)/.$(notdir $@).flags
+endif
+
+#
+# This make dependencies quickly
+#
+fastdep: dummy
+       $(TOPDIR)/scripts/mkdep $(wildcard *.[chS] local.h.master) > .depend
+ifdef ALL_SUB_DIRS
+       $(MAKE) $(patsubst %,_sfdep_%,$(ALL_SUB_DIRS)) _FASTDEP_ALL_SUB_DIRS="$(ALL_SUB_DIRS)"
+endif
+
+ifdef _FASTDEP_ALL_SUB_DIRS
+$(patsubst %,_sfdep_%,$(_FASTDEP_ALL_SUB_DIRS)):
+       $(MAKE) -C $(patsubst _sfdep_%,%,$@) fastdep
+endif
+
+       
+#
+# A rule to make subdirectories
+#
+sub_dirs: dummy $(patsubst %,_subdir_%,$(SUB_DIRS))
+
+ifdef SUB_DIRS
+$(patsubst %,_subdir_%,$(SUB_DIRS)) : dummy
+       $(MAKE) -C $(patsubst _subdir_%,%,$@)
+endif
+
+#
+# A rule to make modules
+#
+ALL_MOBJS = $(MX_OBJS) $(M_OBJS)
+ifneq "$(strip $(ALL_MOBJS))" ""
+PDWN=$(shell $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh)
+endif
+
+ifdef MOD_SUB_DIRS
+$(patsubst %,_modsubdir_%,$(MOD_SUB_DIRS)) : dummy
+       $(MAKE) -C $(patsubst _modsubdir_%,%,$@) modules
+endif
+
+ifdef MOD_IN_SUB_DIRS
+$(patsubst %,_modinsubdir_%,$(MOD_IN_SUB_DIRS)) : dummy
+       $(MAKE) -C $(patsubst _modinsubdir_%,%,$@) modules
+endif
+
+modules: $(ALL_MOBJS) $(MIX_OBJS) $(MI_OBJS) dummy \
+        $(patsubst %,_modsubdir_%,$(MOD_SUB_DIRS)) \
+        $(patsubst %,_modinsubdir_%,$(MOD_IN_SUB_DIRS))
+ifneq "$(strip $(MOD_LIST_NAME))" ""
+       rm -f $$TOPDIR/modules/$(MOD_LIST_NAME)
+ifdef MOD_SUB_DIRS
+       for i in $(MOD_SUB_DIRS); do \
+           echo `basename $$i`.o >> $$TOPDIR/modules/$(MOD_LIST_NAME); done
+endif
+ifneq "$(strip $(ALL_MOBJS))" ""
+       echo $(ALL_MOBJS) >> $$TOPDIR/modules/$(MOD_LIST_NAME)
+endif
+ifneq "$(strip $(MOD_TO_LIST))" ""
+       echo $(MOD_TO_LIST) >> $$TOPDIR/modules/$(MOD_LIST_NAME)
+endif
+endif
+ifneq "$(strip $(ALL_MOBJS))" ""
+       echo $(PDWN)
+       cd $$TOPDIR/modules; for i in $(ALL_MOBJS); do \
+           ln -sf ../$(PDWN)/$$i $$i; done
+endif
+
+#
+# A rule to do nothing
+#
+dummy:
+
+#
+# This is useful for testing
+#
+script:
+       $(SCRIPT)
+
+#
+# This sets version suffixes on exported symbols
+# Uses SYMTAB_OBJS
+# Separate the object into "normal" objects and "exporting" objects
+# Exporting objects are: all objects that define symbol tables
+#
+ifdef CONFIG_MODULES
+
+SYMTAB_OBJS = $(LX_OBJS) $(OX_OBJS) $(MX_OBJS) $(MIX_OBJS)
+
+ifdef CONFIG_MODVERSIONS
+ifneq "$(strip $(SYMTAB_OBJS))" ""
+
+MODINCL = $(LINUXDIR)/include/linux/modules
+
+# The -w option (enable warnings) for genksyms will return here in 2.1
+# So where has it gone?
+#
+# Added the SMP separator to stop module accidents between uniprocessor
+# and SMP Intel boxes - AC - from bits by Michael Chastain
+#
+
+ifdef CONFIG_SMP
+       genksyms_smp_prefix := -p smp_
+else
+       genksyms_smp_prefix := 
+endif
+
+$(MODINCL)/%.ver: %.c
+       @if [ ! -r $(MODINCL)/$*.stamp -o $(MODINCL)/$*.stamp -ot $< ]; then \
+               echo '$(CC) $(CFLAGS) -E -D__GENKSYMS__ $<'; \
+               echo '| $(GENKSYMS) $(genksyms_smp_prefix) -k $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) > $@.tmp'; \
+               $(CC) $(CFLAGS) -E -D__GENKSYMS__ $< \
+               | $(GENKSYMS) $(genksyms_smp_prefix) -k $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) > $@.tmp; \
+               if [ -r $@ ] && cmp -s $@ $@.tmp; then echo $@ is unchanged; rm -f $@.tmp; \
+               else echo mv $@.tmp $@; mv -f $@.tmp $@; fi; \
+       fi; touch $(MODINCL)/$*.stamp
+       
+$(addprefix $(MODINCL)/,$(SYMTAB_OBJS:.o=.ver)): $(LINUXDIR)/include/linux/autoconf.h
+
+$(LINUXDIR)/include/linux/modversions.h: $(addprefix $(MODINCL)/,$(SYMTAB_OBJS:.o=.ver))
+       @echo updating $(LINUXDIR)/include/linux/modversions.h
+       @(echo "#ifndef _LINUX_MODVERSIONS_H";\
+         echo "#define _LINUX_MODVERSIONS_H"; \
+         echo "#include <linux/modsetver.h>"; \
+         cd $(LINUXDIR)/include/linux/modules; \
+         for f in *.ver; do \
+           if [ -f $$f ]; then echo "#include <linux/modules/$${f}>"; fi; \
+         done; \
+         echo "#endif"; \
+       ) > $@
+
+dep fastdep: $(LINUXDIR)/include/linux/modversions.h
+
+endif # SYMTAB_OBJS 
+
+$(M_OBJS): $(LINUXDIR)/include/linux/modversions.h
+ifdef MAKING_MODULES
+$(O_OBJS) $(L_OBJS): $(LINUXDIR)/include/linux/modversions.h
+endif
+
+else
+
+$(LINUXDIR)/include/linux/modversions.h:
+       @echo "#include <linux/modsetver.h>" > $@
+
+endif # CONFIG_MODVERSIONS
+
+ifneq "$(strip $(SYMTAB_OBJS))" ""
+$(SYMTAB_OBJS): $(LINUXDIR)/include/linux/modversions.h $(SYMTAB_OBJS:.o=.c)
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c)
+       @ ( \
+           echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB)),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@) -DEXPORT_SYMTAB)))' ; \
+           echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
+           echo 'endif' \
+       ) > $(dir $@)/.$(notdir $@).flags
+endif
+
+endif # CONFIG_MODULES
+
+
+#
+# include dependency files if they exist
+#
+ifneq ($(wildcard .depend),)
+include .depend
+endif
+
+ifneq ($(wildcard $(TOPDIR)/.hdepend),)
+include $(TOPDIR)/.hdepend
+endif
+
+#
+# Find files whose flags have changed and force recompilation.
+# For safety, this works in the converse direction:
+#   every file is forced, except those whose flags are positively up-to-date.
+#
+FILES_FLAGS_UP_TO_DATE :=
+
+# For use in expunging commas from flags, which mung our checking.
+comma = ,
+
+FILES_FLAGS_EXIST := $(wildcard .*.flags)
+ifneq ($(FILES_FLAGS_EXIST),)
+include $(FILES_FLAGS_EXIST)
+endif
+
+FILES_FLAGS_CHANGED := $(strip \
+    $(filter-out $(FILES_FLAGS_UP_TO_DATE), \
+       $(O_TARGET) $(O_OBJS) $(OX_OBJS) \
+       $(L_TARGET) $(L_OBJS) $(LX_OBJS) \
+       $(M_OBJS) $(MX_OBJS) \
+       $(MI_OBJS) $(MIX_OBJS) \
+       ))
+
+# A kludge: .S files don't get flag dependencies (yet),
+#   because that will involve changing a lot of Makefiles.
+FILES_FLAGS_CHANGED := $(strip \
+    $(filter-out $(patsubst %.S, %.o, $(wildcard *.S)), \
+    $(FILES_FLAGS_CHANGED)))
+
+ifneq ($(FILES_FLAGS_CHANGED),)
+$(FILES_FLAGS_CHANGED): dummy
+endif
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..bb5b2cc
--- /dev/null
+++ b/TODO
@@ -0,0 +1,14 @@
+
+das6402 needs cleanup
+
+check each driver init for stuff
+
+grep XXX
+
+register_symtab
+
+allocate subdevice
+allocate private
+
+fix PCI code, use pci_find_device()
+
diff --git a/comedi/Config.in b/comedi/Config.in
new file mode 100644 (file)
index 0000000..88e8b2f
--- /dev/null
@@ -0,0 +1,64 @@
+
+define_bool CONFIG_COMEDI m
+
+comment 'Comedi Features'
+
+#bool 'Backwards compatibility' CONFIG_BACKWARDS
+
+tristate 'Kernel Comedilib' CONFIG_COMEDI_KLIB
+if [ "$CONFIG_RTL" = "y" ];then
+       define_bool CONFIG_COMEDI_RT y
+       define_bool CONFIG_COMEDI_RTL y
+fi
+if [ "$CONFIG_RTAI" = "y" ];then
+       bool 'Real-time support' CONFIG_COMEDI_RT
+       if [ "$CONFIG_RTAI" = "y" ];then
+               define_bool CONFIG_COMEDI_RTAI y
+       fi
+fi
+
+bool 'Verbose Debugging' CONFIG_COMEDI_DEBUG
+
+comment 'Hardware device drivers'
+
+
+bool 'Data Translation boards' CONFIG_COMEDI_DT
+if [ "$CONFIG_DT" = "y" ];then
+       tristate 'DT 2801' CONFIG_COMEDI_DT2801
+       tristate 'DT 2811' CONFIG_COMEDI_DT2811
+       tristate 'DT 2814' CONFIG_COMEDI_DT2814
+       tristate 'DT 2815' CONFIG_COMEDI_DT2815
+       tristate 'DT 2817' CONFIG_COMEDI_DT2817
+       tristate 'DT 2821 series' CONFIG_COMEDI_DT282x
+       if [ "$CONFIG_EXPERIMENTAL" = "y" ];then
+               tristate 'DT3000' CONFIG_COMEDI_DT3000
+       fi
+fi
+
+bool 'National Instruments boards' CONFIG_COMEDI_NI
+if [ "$CONFIG_NI" = "y" ];then
+       tristate 'AT-MIO E series' CONFIG_COMEDI_NI_ATMIO
+       if [ "$CONFIG_PCI" = "y" ];then
+               tristate 'PCI-MIO E series' CONFIG_COMEDI_NI_PCIMIO
+               tristate 'NI PCI-DIO series' CONFIG_COMEDI_NI_PCIDIO
+               if [ "$CONFIG_COMEDI_NI_PCIMIO" = "y" -o "$CONFIG_COMEDI_NI_PCIDIO" = "y" ];then
+                       define_bool CONFIG_COMEDI_MITE y
+               fi
+       fi
+fi
+
+tristate 'Computer Boards DAS-08 series' CONFIG_COMEDI_DAS08
+tristate 'Computer Boards DAS-08jr' CONFIG_COMEDI_DAS08JR
+tristate 'Keithley Metrabyte DAS-1600 and compatibles' CONFIG_COMEDI_DAS1600
+tristate 'DAS16' CONFIG_COMEDI_DAS16
+tristate 'DAS-6402 and compatibles' CONFIG_COMEDI_DAS6402
+tristate 'Generic 8255 support' CONFIG_COMEDI_8255
+tristate 'Quanser Consulting MultiQ-3' CONFIG_COMEDI_MULTIQ3
+tristate 'Generic parallel port support' CONFIG_COMEDI_PARPORT
+tristate 'PCL-711, PCL-711b, ACL-8112, and compatibles' CONFIG_COMEDI_PCL711
+tristate 'PCL-725' CONFIG_COMEDI_PCL725
+tristate 'PCL-726' CONFIG_COMEDI_PCL726
+tristate 'Analog Devices RTI-800/815' CONFIG_COMEDI_RTI800
+tristate 'Analog Devices RTI-802' CONFIG_COMEDI_RTI802
+
+
diff --git a/comedi/Makefile b/comedi/Makefile
new file mode 100644 (file)
index 0000000..ad92c15
--- /dev/null
@@ -0,0 +1,73 @@
+
+
+SUB_DIRS     := drivers
+ALL_SUB_DIRS := kcomedilib drivers
+MOD_SUB_DIRS := drivers
+MOD_IN_SUB_DIRS :=
+
+ifneq ($(CONFIG_COMEDI_KLIB),n)
+       MOD_SUB_DIRS += kcomedilib
+       SUB_DIRS += kcomedilib
+endif
+
+MOD_LIST_NAME := MISC_MODULES
+
+M_OBJS := comedi.o
+MI_OBJS := module.o dummy.o proc.o range.o drivers.o kvmem.o
+MIX_OBJS := comedi_ksyms.o
+
+
+
+ifeq ($(CONFIG_COMEDI_RTL),y)
+MI_OBJS += rtl.o
+CFLAGS += -D__RTL__
+endif
+
+ifeq ($(CONFIG_COMEDI_RTAI),y)
+MI_OBJS += rtai.o
+endif
+
+ifeq ($(CONFIG_COMEDI_RTL_V1),y)
+MI_OBJS += rtl_v1.o
+CFLAGS += -D__RTL__
+endif
+
+range-y := range.c
+range-$(CONFIG_COMEDI_DT2811)          += drivers/dt2811.c
+range-$(CONFIG_COMEDI_DT2815)          += drivers/dt2815.c
+range-$(CONFIG_COMEDI_DT282x)          += drivers/dt282x.c
+range-$(CONFIG_COMEDI_DT3000)          += drivers/dt3000.c
+range-$(CONFIG_COMEDI_NI_ATMIO)                += drivers/ni_mio_common.c
+ifeq ($(CONFIG_COMEDI_NI_ATMIO),n)
+range-$(CONFIG_COMEDI_NI_PCIMIO)       += drivers/ni_mio_common.c
+endif
+
+range-$(CONFIG_COMEDI_PCL711)          += drivers/pcl711.c
+range-$(CONFIG_COMEDI_RTI800)          += drivers/rti800.c
+range-$(CONFIG_COMEDI_DAS1600)         += drivers/das1600.c
+
+
+
+
+
+include $(TOPDIR)/Rules.make
+
+
+comedi.o: $(MI_OBJS)
+       $(LD) -r -o $@ $(MI_OBJS)
+
+range.h: mk_range $(TOPDIR)/.config
+       -rm -f range.h
+       set -e ; for i in $(range-y) $(range-m) ; do \
+       grep -A 99999 -e '^--BEGIN-RANGE-DEFS--' $$i | \
+       grep -B 99999 -e '^---END-RANGE-DEFS---' | \
+       grep -v -e 'RANGE-DEFS' ; done >range.def
+       ./mk_range source <range.def >range.h
+       ./mk_range include <range.def >>range.h
+
+mk_range: mk_range.c
+       $(CC) -Wall mk_range.c -o mk_range
+
+$(MIX_OBJS) $(MI_OBJS):        range.h
+
+
diff --git a/comedi/am9513.h b/comedi/am9513.h
new file mode 100644 (file)
index 0000000..284fb2d
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+    module/am9513.h
+    value added preprocessor definitions for Am9513 timer chip
+
+    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 _AM9513_H_
+#define _AM9513_H_
+
+#if 0
+
+/*
+ *     Before including this file, the following need to be defined:
+ */
+#define Am9513_8BITBUS xxx
+/* or */
+#define Am9513_16BITBUS xxx
+
+#define Am9513_output_control(a)       xxx
+#define Am9513_input_status()          xxx
+#define Am9513_output_data(a)          xxx
+#define Am9513_input_data()            xxx
+
+#endif
+
+/*
+ *
+ */
+
+#ifdef Am9513_8BITBUS
+
+#define Am9513_write_register(reg,val)                         \
+       do{                                                     \
+               Am9513_output_control(reg);                     \
+               Am9513_output_data(val>>8);                     \
+               Am9513_output_data(val&0xff);                   \
+       }while(0)
+
+#define Am9513_read_register(reg,val)                          \
+       do{                                                     \
+               Am9513_output_control(reg);                     \
+               val=Am9513_input_data()<<8;                     \
+               val|=Am9513_input_data();                       \
+       }while(0)
+
+#else /* Am9513_16BITBUS */
+
+#define Am9513_write_register(reg,val)                         \
+       do{                                                     \
+               Am9513_output_control(reg);                     \
+               Am9513_output_data(val);                        \
+       }while(0)
+
+#define Am9513_read_register(reg,val)                          \
+       do{                                                     \
+               Am9513_output_control(reg);                     \
+               val=Am9513_input_data();                        \
+       }while(0)
+
+#endif
+
+#endif
+
diff --git a/comedi/comedi_ksyms.c b/comedi/comedi_ksyms.c
new file mode 100644 (file)
index 0000000..f5d4596
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+    module/exp_ioctl.c
+    exported comedi functions
+
+    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 <comedi_module.h>
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+
+
+#ifdef LINUX_V20
+
+struct symbol_table comedi_syms = {
+#include <linux/symtab_begin.h>
+       X(comedi_trig_ioctl),
+       X(__comedi_trig_ioctl),
+       X(comedi_lock_ioctl),
+       X(comedi_unlock_ioctl),
+       X(comedi_cancel_ioctl),
+       X(comedi_register_callback),
+#include <linux/symtab_end.h>
+};
+
+#endif
+
+#ifdef LINUX_V22
+
+#if 0
+EXPORT_SYMBOL(comedi_trig_ioctl);
+EXPORT_SYMBOL(__comedi_trig_ioctl);
+EXPORT_SYMBOL(comedi_lock_ioctl);
+EXPORT_SYMBOL(comedi_unlock_ioctl);
+EXPORT_SYMBOL(comedi_cancel_ioctl);
+EXPORT_SYMBOL(comedi_register_callback);
+#endif
+EXPORT_SYMBOL(comedi_driver_register);
+EXPORT_SYMBOL(comedi_driver_unregister);
+EXPORT_SYMBOL(comedi_bufcheck);
+EXPORT_SYMBOL(comedi_done);
+EXPORT_SYMBOL(comedi_error);
+
+#endif
+
+
+
diff --git a/comedi/comedi_module.h b/comedi/comedi_module.h
new file mode 100644 (file)
index 0000000..b65dfb5
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+    module/comedi_module.h
+    header file for kernel-only structures, variables, and constants
+
+    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.
+
+*/
+
+#ifndef _COMEDI_MODULE_H
+#define _COMEDI_MODULE_H
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/kdev_t.h>
+#include <config.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <comedi.h>
+
+
+#include <kern_compat.h>
+
+#ifdef CONFIG_COMEDI_RT
+
+struct comedi_irq_struct{
+       struct comedi_irq_struct *next;
+
+       int irq;
+       void *dev_id;
+       unsigned long flags;
+       void (*handler)(int,void *,struct pt_regs *);
+       char *device;
+};
+
+int get_priority_irq(struct comedi_irq_struct *);
+int free_priority_irq(struct comedi_irq_struct *);
+struct comedi_irq_struct * get_irq_struct(unsigned int);
+
+#ifndef SA_PRIORITY
+#define SA_PRIORITY 0x08000000
+#endif
+
+#ifdef CONFIG_COMEDI_RTL
+#include <rtl.h>
+#endif
+
+#ifdef CONFIG_COMEDI_RTAI
+#include <rtai.h>
+#define rt_printk(format,args...)      printk(format,##args)
+#define rt_printk_init()               
+#define rt_printk_cleanup()            
+
+#endif
+
+#ifdef CONFIG_COMEDI_RTL_V1
+#include <rtl_v1.h>
+#define rt_printk(format,args...)      printk(format,##args)
+#define rt_printk_init()               
+#define rt_printk_cleanup()            
+
+#endif
+
+#else /* !CONFIG_COMEDI_RT */
+
+#define rt_printk(format,args...)      printk(format,##args)
+#define rt_printk_init()               
+#define rt_printk_cleanup()            
+
+#endif
+
+#ifdef CONFIG_DEBUG
+#define DPRINTK(format, args...)       printk("comedi: " format , ## args )
+#else
+#define DPRINTK(format, args...)       /* */
+#endif
+
+
+typedef struct comedi_device_struct comedi_device;
+typedef struct comedi_subdevice_struct comedi_subdevice;
+typedef struct comedi_driver_struct comedi_driver;
+
+
+struct comedi_subdevice_struct{
+       int type;
+       int n_chan;
+       int subdev_flags;
+       int timer_type;
+       int len_chanlist;               /* length of channel/gain list, if available */
+
+       void            *prealloc_buf;          /* pre-allocated buffer */
+       unsigned int    prealloc_bufsz;         /* buffer size, in bytes */
+
+       void *lock;
+       void *busy;
+       
+       int io_bits;
+       
+       lsampl_t maxdata;               /* if maxdata==0, use list */
+       lsampl_t *maxdata_list;         /* list is channel specific */
+       
+       unsigned int flags;
+       unsigned int *flaglist;
+
+       unsigned int range_type;
+       unsigned int *range_type_list;
+       
+       unsigned int *chanlist;         /* driver-owned chanlist (not used) */
+       
+       comedi_trig     cur_trig;       /* current trig structure */
+       comedi_cmd      cmd;
+       
+       volatile unsigned int buf_int_ptr;      /* buffer marker for interrupt */
+       unsigned int buf_user_ptr;              /* buffer marker for read() and write() */
+       volatile unsigned int buf_int_count;    /* byte count for interrupt */
+       unsigned int buf_user_count;            /* byte count for read() and write() */
+       unsigned int cur_chan;          /* useless channel marker for interrupt */
+       
+#if 0
+       unsigned int *range_list;       /* is this necessary? */
+#endif
+       
+       int (*trig[5])(comedi_device *,comedi_subdevice *,comedi_trig *);
+
+       int (*do_cmd)(comedi_device *,comedi_subdevice *);
+       int (*poll)(comedi_device *,comedi_subdevice *);
+       int (*cancel)(comedi_device *,comedi_subdevice *);
+       int (*do_lock)(comedi_device *,comedi_subdevice *);
+       int (*do_unlock)(comedi_device *,comedi_subdevice *);
+
+       unsigned int cb_mask;
+       int (*cb_func)(unsigned int flags,void *);
+       void *cb_arg;
+
+       unsigned int state;
+};
+
+struct comedi_driver_struct{
+       struct comedi_driver_struct *next;
+
+       char *driver_name;
+       struct module *module;
+       int (*attach)(comedi_device *,comedi_devconfig *);
+       int (*detach)(comedi_device *);
+       int (*recognize)(char *name);
+};
+
+struct comedi_device_struct{
+       int use_count;
+       comedi_driver *driver;
+       void *private;
+       kdev_t minor;
+       //char *driver_name;
+       char *board_name;
+       int board;
+       void *board_ptr;
+       int attached;
+
+       int n_subdevices;
+       comedi_subdevice *subdevices;
+       int options[COMEDI_NDEVCONFOPTS];
+
+       /* dumb */
+       int iobase;
+       int iosize;
+       int irq;
+
+       wait_queue_head_t wait;
+};
+
+
+
+extern comedi_device *comedi_devices;
+
+/*
+ * function prototypes
+ */
+
+void comedi_error(comedi_device *dev,const char *s);
+void comedi_done(comedi_device *dev,comedi_subdevice *s);
+void comedi_eos(comedi_device *dev,comedi_subdevice *s);
+void comedi_eobuf(comedi_device *dev,comedi_subdevice *s);
+void comedi_bufcheck(comedi_device *dev,comedi_subdevice *s);
+
+int comedi_device_detach(comedi_device *dev);
+int comedi_device_attach(comedi_device *dev,comedi_devconfig *it);
+int comedi_driver_register(comedi_driver *);
+int comedi_driver_unregister(comedi_driver *);
+
+void init_polling(void);
+void cleanup_polling(void);
+void start_polling(comedi_device *);
+void stop_polling(comedi_device *);
+
+void comedi_proc_init(void);
+void comedi_proc_cleanup(void);
+
+int di_unpack(unsigned int bits,comedi_trig *it);
+int do_pack(unsigned int *bits,comedi_trig *it);
+
+#ifdef CONFIG_COMEDI_RT
+int comedi_request_irq(unsigned int irq,void (*handler)(int,void *,struct pt_regs *),
+               unsigned long flags,const char *device,void *dev_id);
+int comedi_change_irq_flags(unsigned int irq,void *dev_id,unsigned long new_flags);
+void comedi_free_irq(unsigned int irq,void *dev_id);
+#else
+#define comedi_request_irq             request_irq
+#define comedi_change_irq_flags(a,b,c) /* */
+#define comedi_free_irq                        free_irq
+#endif
+
+
+
+/*
+   various internal comedi functions
+ */
+
+int do_rangeinfo_ioctl(comedi_device *dev,comedi_rangeinfo *arg);
+int check_chanlist(comedi_subdevice *s,int n,unsigned int *chanlist);
+
+/* range stuff */
+
+#include <range.h>
+
+#define RANGE_digital                  RANGE_unipolar5
+
+extern unsigned int comedi_max_range;
+extern comedi_krange comedi_kranges[];
+
+
+/* timer types */
+/* these *must* agree with lib/timer.c */
+
+#define TIMER_dt282x                   1
+#define TIMER_dt2814                   2
+#define TIMER_atmio                    3
+#define TIMER_acl8112                  4
+#define TIMER_nanosec                  5
+
+
+/* some silly little inline functions */
+
+static inline int alloc_subdevices(comedi_device *dev)
+{
+       int size=sizeof(comedi_subdevice)*dev->n_subdevices;
+
+       dev->subdevices=kmalloc(size,GFP_KERNEL);
+       if(!dev->subdevices)
+               return -ENOMEM;
+       memset(dev->subdevices,0,size);
+       return 0;
+}
+
+static inline int alloc_private(comedi_device *dev,int size)
+{
+       dev->private=kmalloc(size,GFP_KERNEL);
+       if(!dev->private)
+               return -ENOMEM;
+       memset(dev->private,0,size);
+       return 0;
+}
+
+
+#ifdef LINUX_V20
+#ifdef CONFIG_EXPORT
+extern struct symbol_table comedi_syms;
+#endif
+#endif
+
+
+#endif /* _COMEDI_MODULE_H */
+
+
+
+
diff --git a/comedi/drivers.c b/comedi/drivers.c
new file mode 100644 (file)
index 0000000..b123ea4
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+    module/drivers.c
+    functions for manipulating drivers
+
+    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 <comedi_module.h>
+#include <kvmem.h>
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/io.h>
+
+static void postconfig(comedi_device *dev);
+
+comedi_driver *comedi_drivers;
+
+int comedi_modprobe(kdev_t minor)
+{
+       return -EINVAL;
+}
+
+int comedi_device_detach(comedi_device *dev)
+{
+       int i;
+       comedi_subdevice *s;
+
+       if(!dev->attached)
+               return 0;
+
+       /* this is not correct for the kmod case */
+       __MOD_DEC_USE_COUNT(dev->driver->module);
+
+       dev->attached=0;
+
+       for(i=0;i<dev->n_subdevices;i++){
+               s=dev->subdevices+i;
+               if(s->prealloc_buf)
+                       rvfree(s->prealloc_buf,s->prealloc_bufsz);
+       }
+
+       if(dev->driver){
+               dev->driver->detach(dev);
+       }else{
+               printk("BUG: dev->driver=NULL in comedi_device_detach()\n");
+       }
+
+       if(dev->subdevices)kfree(dev->subdevices);
+       if(dev->private)kfree(dev->private);
+
+       return 0;
+}
+
+int comedi_device_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       comedi_driver *driv;
+       int i,ret;
+
+       if(dev->attached)
+               return -EBUSY;
+
+       for(driv=comedi_drivers;driv;driv=driv->next){
+               if(driv->recognize){
+                       i=driv->recognize(it->board_name);
+                       if(i<0)continue;
+               }else{
+                       if(strcmp(driv->driver_name,it->board_name))
+                               continue;
+               }
+               ret=driv->attach(dev,it);
+               if(ret<0){
+                       driv->detach(dev);
+                       if(dev->subdevices)kfree(dev->subdevices);
+                       if(dev->private)kfree(dev->private);
+
+                       return ret;
+               }
+               goto attached;
+       }
+
+       return -EIO;
+
+attached:
+       init_waitqueue_head(&dev->wait);
+
+       /* do a little post-config cleanup */
+       postconfig(dev);
+
+       dev->attached=1;
+       dev->driver=driv;
+
+       __MOD_INC_USE_COUNT(driv->module);
+
+       return 0;
+}
+
+int comedi_driver_register(comedi_driver *driver)
+{
+       driver->next=comedi_drivers;
+       comedi_drivers=driver;
+
+       return 0;
+}
+
+int comedi_driver_unregister(comedi_driver *driver)
+{
+       comedi_driver *prev;
+       int i;
+
+       /* check for devices using this driver */
+       for(i=0;i<COMEDI_NDEVICES;i++){
+               comedi_device *dev;
+
+               dev=comedi_devices+i;
+               if(dev->attached && dev->driver==driver){
+                       if(dev->use_count)
+                               printk("BUG! detaching device with use_count=%d\n",dev->use_count);
+                       comedi_device_detach(dev);
+               }
+       }
+
+       if(comedi_drivers==driver){
+               comedi_drivers=driver->next;
+               return 0;
+       }
+
+       for(prev=comedi_drivers;prev->next;prev=prev->next){
+               if(prev->next==driver){
+                       prev->next=driver->next;
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+
+
+static void postconfig(comedi_device *dev)
+{
+       int i;
+       comedi_subdevice *s;
+
+       for(i=0;i<dev->n_subdevices;i++){
+               s=dev->subdevices+i;
+
+               if(s->type==COMEDI_SUBD_UNUSED)
+                       continue;
+
+               if(s->len_chanlist==0)
+                       s->len_chanlist=1;
+
+               /* XXX */
+               if(s->trig[1] || s->trig[2] || s->trig[3] ||s->trig[4]){
+                       s->prealloc_bufsz=1024*128;
+               }else{
+                       s->prealloc_bufsz=0;
+               }
+
+               if(s->prealloc_bufsz){
+                       /* XXX */
+                       s->prealloc_buf=rvmalloc(s->prealloc_bufsz*sizeof(sampl_t));
+                       if(!s->prealloc_buf){
+                               printk("ENOMEM\n");
+                       }
+               }
+
+               if(!s->range_type && !s->range_type_list)
+                       s->range_type=RANGE_unknown;
+       }
+
+}
+
+
+#define REG(x) {extern comedi_driver (x);comedi_driver_register(&(x));}
+
+void init_drivers(void)
+{
+       REG(driver_dummy);
+#if 0
+#ifdef CONFIG_COMEDI_DT282x
+       REG(driver_dt282x);
+#endif
+#ifdef CONFIG_COMEDI_NI_PCIMIO
+       REG(driver_pcimio);
+#endif
+#ifdef CONFIG_COMEDI_NI_ATMIO
+       REG(driver_atmio);
+#endif
+#ifdef CONFIG_COMEDI_DT2801
+       REG(driver_dt2801);
+#endif
+#ifdef CONFIG_COMEDI_DT2811
+       REG(driver_dt2811);
+#endif
+#ifdef CONFIG_COMEDI_DT2814
+       REG(driver_dt2814);
+#endif
+#ifdef CONFIG_COMEDI_DT2817
+       REG(driver_dt2817);
+#endif
+#ifdef CONFIG_COMEDI_DT3000
+       REG(driver_dt3000);
+#endif
+#ifdef CONFIG_COMEDI_8255
+       REG(driver_8255);
+#endif
+#ifdef CONFIG_NI_PCIDIO
+       REG(driver_nidio);
+#endif
+#ifdef CONFIG_COMEDI_DAS08
+       REG(driver_das08);
+#endif
+#ifdef CONFIG_COMEDI_PCL711
+       REG(driver_pcl711);
+#endif
+#ifdef CONFIG_COMEDI_PCL725
+       REG(driver_pcl725);
+#endif
+#ifdef CONFIG_COMEDI_PCL726
+       REG(driver_pcl726);
+#endif
+#ifdef CONFIG_COMEDI_RTI800
+       REG(driver_rti800);
+#endif
+#ifdef CONFIG_COMEDI_RTI802
+       REG(driver_rti802);
+#endif
+#ifdef CONFIG_COMEDI_RTI860
+       REG(driver_rti860);
+#endif
+#ifdef CONFIG_COMEDI_PARPORT
+       REG(driver_parport);
+#endif
+#ifdef CONFIG_COMEDI_DAS08JR
+       REG(driver_das08jr);
+#endif
+#ifdef CONFIG_COMEDI_DAS1600
+       REG(driver_das1600);
+#endif
+#ifdef CONFIG_COMEDI_DAS6402
+       REG(driver_das6402);
+#endif
+#ifdef CONFIG_COMEDI_MULTIQ3
+       REG(driver_multiq3);
+#endif
+#ifdef CONFIG_COMEDI_DT2815
+       REG(driver_dt2815);
+#endif
+#ifdef CONFIG_COMEDI_DAS16
+       REG(driver_das16);
+#endif
+#endif
+}
+
diff --git a/comedi/drivers/8255.c b/comedi/drivers/8255.c
new file mode 100644 (file)
index 0000000..2c21755
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+    module/8255.c
+    Driver for 8255
+
+    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.
+
+*/
+
+/*
+   This file contains an exported subdevice for driving an 8255.
+
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+
+
+#define _8255_SIZE 4
+
+#define _8255_DATA 0
+#define _8255_CR 3
+
+#define CR_C_LO_IO     0x01
+#define CR_B_IO                0x02
+#define CR_B_MODE      0x04
+#define CR_C_HI_IO     0x08
+#define CR_A_IO                0x10
+#define CR_A_MODE(a)   ((a)<<5)
+#define CR_CW          0x80
+
+#define CALLBACK_ARG   ((void *)(s->cb_arg))
+#define CALLBACK_FUNC  ((int (*)(int,int,int,void *))(s->cb_func))
+
+static int dev_8255_attach(comedi_device * dev, comedi_devconfig * it);
+static int dev_8255_detach(comedi_device * dev);
+comedi_driver driver_8255={
+       driver_name:    "8255",
+       module:         &__this_module,
+       attach:         dev_8255_attach,
+       detach:         dev_8255_detach,
+};
+
+static void do_config(comedi_device *dev,comedi_subdevice *s);
+
+static int subdev_8255_cb(int dir,int port,int data,void *arg)
+{
+       int iobase=(int)arg;
+
+       if(dir){
+               outb(data,iobase+port);
+               return 0;
+       }else{
+               return inb(iobase+port);
+       }
+}
+
+int subdev_8255_dio(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int mask,data_in;
+       int i;
+
+       if(it->flags & TRIG_CONFIG){
+               int bits;
+
+               for(i=0;i<it->n_chan;i++){
+                       mask=1<<CR_CHAN(it->chanlist[i]);
+                       if(mask&0x0000ff){
+                               bits=0x0000ff;
+                       }else if(mask&0x00ff00){
+                               bits=0x00ff00;
+                       }else if(mask&0x0f0000){
+                               bits=0x0f0000;
+                       }else{
+                               bits=0xf00000;
+                       }
+                       if(it->data[i]){
+                               s->io_bits|=bits;
+                       }else{
+                               s->io_bits&=~bits;
+                       }
+               }
+               do_config(dev,s);
+       }else{
+               if(it->flags&TRIG_WRITE){
+                       do_pack(&s->state,it);
+
+                       CALLBACK_FUNC(1,_8255_DATA,s->state&0xff,CALLBACK_ARG);
+                       CALLBACK_FUNC(1,_8255_DATA+1,(s->state>>8)&0xff,CALLBACK_ARG);
+                       CALLBACK_FUNC(1,_8255_DATA+2,(s->state>>16)&0xff,CALLBACK_ARG);
+               }else{
+                       data_in=CALLBACK_FUNC(0,_8255_DATA,0,CALLBACK_ARG);
+                       data_in|=(CALLBACK_FUNC(0,_8255_DATA+1,0,CALLBACK_ARG)<<8);
+                       data_in|=(CALLBACK_FUNC(0,_8255_DATA+2,0,CALLBACK_ARG)<<16);
+
+                       di_unpack(data_in,it);
+               }
+       }
+
+       return it->n_chan;
+}
+
+static void do_config(comedi_device *dev,comedi_subdevice *s)
+{
+       int config;
+
+       config=CR_CW;
+       /* 1 in io_bits indicates output, 1 in config indicates input */
+       if(!(s->io_bits&0x0000ff))
+               config|=CR_A_IO;
+       if(!(s->io_bits&0x00ff00))
+               config|=CR_B_IO;
+       if(!(s->io_bits&0x0f0000))
+               config|=CR_C_LO_IO;
+       if(!(s->io_bits&0xf00000))
+               config|=CR_C_HI_IO;
+       CALLBACK_FUNC(1,_8255_CR,config,CALLBACK_ARG);
+}
+
+
+int subdev_8255_init(comedi_device *dev,comedi_subdevice *s,int (*cb)(int,int,int,void *),void *arg)
+{
+       s->type=COMEDI_SUBD_DIO;
+       s->subdev_flags=SDF_READABLE|SDF_WRITEABLE|SDF_RT;
+       s->n_chan=24;
+       s->range_type=RANGE_digital;
+       s->maxdata=1;
+
+       /* commandeer range_list */
+       CALLBACK_ARG=arg;
+
+       /* same for flaglist */
+       if(cb==NULL){
+               CALLBACK_FUNC=subdev_8255_cb;
+       }else{
+               CALLBACK_FUNC=cb;
+       }
+       s->trig[0]=subdev_8255_dio;
+
+       s->state=0;
+       s->io_bits=0;
+       do_config(dev,s);
+
+       
+       return 0;
+}
+
+
+/*
+
+   Start of the 8255 standalone device
+
+ */
+
+static int dev_8255_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       int ret;
+       int iobase;
+       int i;
+
+       if(strcmp("8255",it->board_name))
+               return 0;
+
+       printk("comedi%d: 8255:",dev->minor);
+
+       dev->board_name="8255";
+
+       for(i=0;i<COMEDI_NDEVCONFOPTS;i++){
+               iobase=it->options[i];
+               if(!iobase)break;
+       }
+       if(i==0){
+               printk(" no devices specified\n");
+               return -EINVAL;
+       }
+       dev->n_subdevices=i;
+
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+
+       for(i=0;i<dev->n_subdevices;i++){
+               iobase=it->options[i];
+
+               printk(" 0x%04x",iobase);
+               if(check_region(iobase,_8255_SIZE)<0){
+                       printk(" (I/O port conflict)");
+
+                       dev->subdevices[i].type=COMEDI_SUBD_UNUSED;
+               }else{
+                       request_region(iobase,_8255_SIZE,"8255");
+
+                       subdev_8255_init(dev,dev->subdevices+i,NULL,(void *)iobase);
+               }
+       }
+
+       printk("\n");
+       return 0;
+}
+
+static int dev_8255_detach(comedi_device *dev)
+{
+       int i,iobase;
+       comedi_subdevice *s;
+
+       printk("comedi%d: 8255: remove\n",dev->minor);
+       
+       for(i=0;i<dev->n_subdevices;i++){
+               s=dev->subdevices+i;
+               if(s->type!=COMEDI_SUBD_UNUSED){
+                       iobase=(int)CALLBACK_ARG;
+                       release_region(iobase,_8255_SIZE);
+               }
+       }
+
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_8255);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_8255);
+}
+#endif
diff --git a/comedi/drivers/8255.h b/comedi/drivers/8255.h
new file mode 100644 (file)
index 0000000..19c42c7
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    module/8255.h
+    Header file for 8255
+
+    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 _8255_H
+#define _8255_H
+
+#include <comedi_module.h>
+
+#ifdef CONFIG_8255
+
+int subdev_8255_init(comedi_device *dev,comedi_subdevice *s,int (*cb)(int,int,int,void *),void *arg);
+
+#else
+
+static inline int subdev_8255_init(comedi_device *dev,comedi_subdevice *s,void *x,void *y)
+{
+       s->type=COMEDI_SUBD_UNUSED;
+
+       return 0;
+}
+
+
+#endif
+
+#endif
+
diff --git a/comedi/drivers/Makefile b/comedi/drivers/Makefile
new file mode 100644 (file)
index 0000000..2cdd293
--- /dev/null
@@ -0,0 +1,66 @@
+
+ALL_SUB_DIRS :=
+MOD_SUB_DIRS :=
+SUB_DIRS     :=
+MOD_LIST_NAME := MISC_MODULES
+
+EXTRA_CFLAGS := -I ../
+
+export-objs := mite.o 8255.o
+
+obj-y :=
+obj-m :=
+obj-n :=
+obj-  :=
+
+obj-$(CONFIG_COMEDI_8255)              += 8255.o
+
+obj-$(CONFIG_COMEDI_DAS08)             += das08.o
+obj-$(CONFIG_COMEDI_DAS08JR)           += das08jr.o
+obj-$(CONFIG_COMEDI_DAS16)             += das16.o
+obj-$(CONFIG_COMEDI_DAS1600)           += das1600.o
+obj-$(CONFIG_COMEDI_DAS6402)           += das6402.o
+
+obj-$(CONFIG_COMEDI_DT2801)            += dt2801.o
+obj-$(CONFIG_COMEDI_DT2811)            += dt2811.o
+obj-$(CONFIG_COMEDI_DT2814)            += dt2814.o
+obj-$(CONFIG_COMEDI_DT2815)            += dt2815.o
+obj-$(CONFIG_COMEDI_DT2817)            += dt2817.o
+obj-$(CONFIG_COMEDI_DT282x)            += dt282x.o
+obj-$(CONFIG_COMEDI_DT3000)            += dt3000.o
+
+obj-$(CONFIG_COMEDI_MULTIQ3)           += multiq3.o
+
+obj-$(CONFIG_COMEDI_NI_ATMIO)          += ni_atmio.o
+obj-$(CONFIG_COMEDI_NI_PCIMIO)         += ni_pcimio.o
+obj-$(CONFIG_COMEDI_NI_PCIDIO)         += ni_pcidio.o
+obj-$(CONFIG_COMEDI_MITE)              += mite.o
+
+obj-$(CONFIG_COMEDI_PCL711)            += pcl711.o
+obj-$(CONFIG_COMEDI_PCL725)            += pcl725.o
+obj-$(CONFIG_COMEDI_PCL726)            += pcl726.o
+
+obj-$(CONFIG_COMEDI_RTI800)            += rti800.o
+obj-$(CONFIG_COMEDI_RTI802)            += rti802.o
+
+obj-m += $(obj-y)
+
+#L_OBJS                := $(sort $(filter-out $(export-objs), $(obj-y)))
+#LX_OBJS       := $(sort $(filter     $(export-objs), $(obj-y)))
+#MI_OBJS       := $(sort $(filter-out $(export-objs), $(obj-y)))
+#MIX_OBJS      := $(sort $(filter     $(export-objs), $(obj-y)))
+M_OBJS         := $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS                := $(sort $(filter     $(export-objs), $(obj-m)))
+
+
+
+include $(TOPDIR)/Rules.make
+
+
+drivers.o:     $(obj-y)
+       $(LD) -r -o $@ $(obj-y)
+
+ni_pcimio.c:   ni_mio_common.c
+ni_atmio.c:    ni_mio_common.c
+
+
diff --git a/comedi/drivers/das08.c b/comedi/drivers/das08.c
new file mode 100644 (file)
index 0000000..2be3bf2
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+
+  DAS-08 adapter
+
+  hack by David Schleef <ds@stm.lbl.gov>
+
+  mostly borrowed from Warren J. Jasper <wjasper@tx.ncsu.edu>
+
+  should be compatible with boards from Keithley Metrabyte and
+  Computer Boards.
+
+ * Copyright (C) 1998  Warren Jasper, David Schleef
+ * All rights reserved.
+ *
+ * This software may be freely copied, modified, and redistributed
+ * provided that this copyright notice is preserved on all copies.
+ *
+ * You may not distribute this software, in whole or in part, as part of
+ * any commercial product without the express consent of the authors.
+ *
+ * There is no warranty or other guarantee of fitness of this software
+ * for any purpose.  It is provided solely "as is".
+
+
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <comedi_module.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <8255.h>
+
+/* general debugging messages */
+#define DEBUG
+
+/* specific debugging messages */
+#undef DAVE1
+#undef DAVE2
+#undef DAVE3
+
+/*
+       on the non-AO board, the DA registers don't exist
+       and the 8255 registers move up by 4
+       it also doesn't use the GAIN_REG
+*/
+
+
+#define DAS08_SIZE 0x10
+
+#define LSB_AND_CHNLS  0       /* A/D Data & Channel Register      */
+#define MSB_DATA_BYTE  1
+#define STATUS_REG     2       /* Channel Mux Scan Limits Register */
+#define GAIN_REG       3       /* Programmable Gain Register       */
+#define COUNTER_0_DATA 4       /* Counter 0 Register               */
+#define COUNTER_1_DATA 5       /* Counter 1 Register               */
+#define COUNTER_2_DATA 6       /* Counter 2 Register               */
+#define COUNTER_CONTROL        7       /* Conter Control  Register         */
+#define DA_CHAN0_LSB   8       /* DAC 0 Low Byte                   */
+#define DA_CHAN0_MSB   9       /* DAC 0 High Byte                  */
+#define DA_CHAN1_LSB   10      /* DAC 1 Low Byte                   */
+#define DA_CHAN1_MSB   11      /* DAC 1 High Byte                  */
+#define DIO_PORTA      12      /* Port A 8 bit I/O of 8255         */
+#define DIO_PORTB      13      /* Port B 8 bit I/O of 8255         */
+#define DIO_PORTC      14      /* Port C 4+4 bit of 8255           */
+#define DIO_CNTRL_REG  15      /* Mode and Direction Control Reg.  */
+
+/*
+
+ info provided by Carsten Zerbst about das08:
+ agrees with pdf file from www.computerboards.com
+
+port, read, write
+0      A/D Bits 3-0(LSB)               Start 8 bit A/D conversion
+1      A/D Bits 11-4(MSB)              Start 12 bit A/D conversion
+2      EOC,IP1-IP3,IRQ,MUX Address     OP1-OP4, INTE & MUX address
+3      not used                        not used
+4      Read counter 0                  Load Counter 0
+5      Read counter 1                  Load Counter 1
+6      Read counter 2                  Load Counter 2
+7      not used                        Counter control
+8      Port A input 8255               Port A Output
+9      Port B input                    Port A output
+10     Port C Input                    Port A Output
+11     None. No read Back on 8255      Configure 8255
+
+
+pgh model: gains .5,1,5,10,50,100,500,1000, unipolar and bipolar
+pgl model: gains .5,1.2,4,8, unipolar and bipolar
+
+pgh bipolar ranges: +/- 10, +/-5, etc.
+pgh unipolar ranges: 0 to { x,10,x,1,x,0.1,x,0.01 }
+  (what does x mean?)
+
+pgl bipolar ranges: +/- { 10,5,2.5,1.25,0.625 }
+pgl unipolar ranges: 0 to { N/A, 10, 5, 2.5, 1.25 }
+
+
+das08jr info:
+
+4      DAC0 lsb
+5      DAC0 msb
+6      DAC1 lsb
+7      DAC1 msb
+
+
+
+*/
+
+
+
+/*************************************************************************
+* STATUS_REG         base_reg+2             Status Register              *
+**************************************************************************/
+
+/* Read */
+#define MUX0  0x01      /* Current multiplexor channel */
+#define MUX1  0x02      /* Current multiplexor channel */
+#define MUX2  0x04      /* Current multiplexor channel */
+#define IRQ   0x08      /* 1 = positive edge detected  */
+#define IP1   0x10      /* digial input line IP1       */
+#define IP2   0x20      /* digial input line IP2       */
+#define IP3   0x40      /* digial input line IP3       */
+#define EOC   0x80      /* 1=A/D busy, 0=not busy      */
+
+/* Write */
+#define INTE  0x08     /* 1=enable interrupts, 0=disable */
+#define OP1   0x10     /* digital output line OP1        */
+#define OP2   0x20     /* digital output line OP2        */
+#define OP3   0x40     /* digital output line OP3        */
+#define OP4   0x80     /* digital output line OP4        */
+
+/*************************************************************************
+* Constants for dealing with the 8254 counters                           *
+**************************************************************************/
+
+#define MODE0 0x0
+#define MODE1 0x2
+#define MODE2 0x4
+#define MODE3 0x6
+#define MODE4 0x8
+#define MODE5 0xa
+
+#define C0 0x00
+#define C1 0x40
+#define C2 0x80
+
+#define _LATCH    0x00         /* LATCH gets caught up with timex.h */
+#define LSBONLY  0x10
+#define MSBONLY  0x20
+#define LSBFIRST 0x30
+
+#define S0       0x00
+#define S1       0x02
+
+static int das08_attach(comedi_device *dev,comedi_devconfig *it);
+static int das08_detach(comedi_device *dev);
+static int das08_recognize(char *name);
+comedi_driver driver_das08={
+       driver_name:    "das08",
+       module:         &__this_module,
+       attach:         das08_attach,
+       detach:         das08_detach,
+       recognize:      das08_recognize,
+};
+
+
+typedef struct{
+       int boardtype;
+       int dio;
+       int aip[16];
+}das08_private;
+#define devpriv ((das08_private *)dev->private)
+
+struct boardtype_struct{
+       char name[20];
+       int ai_chans;
+       int ao_chans;
+};
+static struct boardtype_struct boardtypes[]={
+  {"das08",8,0},
+  {"das08-aol",8,0},
+  {"das08-pgh",8,0},
+  {"das08-pgl",8,0}
+};
+#define this_board (boardtypes[devpriv->boardtype])
+#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtypes[0]))
+
+
+static int das08_ai(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int i,lsb,msb;
+       int chan;
+
+       chan=CR_CHAN(it->chanlist[0]);
+       
+#ifndef DAVE1
+       /* possibly clears A/D queue */
+       inb(dev->iobase+LSB_AND_CHNLS);
+       inb(dev->iobase+MSB_DATA_BYTE);
+#endif
+
+       /* set multiplexer */
+       outb_p(chan | devpriv->dio,dev->iobase+STATUS_REG);
+
+       /* XXX do we have to wait for MUX to settle?  how long? */
+       
+       /* how to set gain? */
+
+       /* trigger conversion */
+#ifndef DAVE3
+       outb_p(0,dev->iobase+LSB_AND_CHNLS);
+#else
+       outb_p(0,dev->iobase+MSB_DATA_BYTE);
+#endif
+#ifdef DAVE2
+       /* wait for conversion to take place */
+       udelay(25);
+       
+       msb=inb(dev->iobase+MSB_DATA_BYTE);
+       lsb=inb(dev->iobase+LSB_AND_CHNLS);
+       it->data[0]=(lsb >> 4) | (msb << 4);
+       return 1;
+#else
+       for(i=0;i<200;i++){
+               if(!(inb_p(dev->iobase+STATUS_REG) & EOC)){
+                       msb=inb(dev->iobase+MSB_DATA_BYTE);
+                       lsb=inb(dev->iobase+LSB_AND_CHNLS);
+                       it->data[0]=(lsb >> 4) | (msb << 4);
+                       return 1;
+               }
+               udelay(5);
+       }
+#ifdef DEBUG
+       rt_printk("das08: ai timeout\n");
+#endif
+       return -ETIME;
+#endif
+}
+
+
+static int das08_ao(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int lsb,msb;
+
+       lsb=it->data[0]&0xff;
+       msb=(it->data[0]>>8)&0xf;
+       if(CR_CHAN(it->chanlist[0])==0){
+               outb(lsb,dev->iobase+DA_CHAN0_LSB);
+               outb(msb,dev->iobase+DA_CHAN0_MSB);
+       }else{
+               outb(lsb,dev->iobase+DA_CHAN1_LSB);
+               outb(msb,dev->iobase+DA_CHAN1_MSB);
+       }
+       return 1;
+}
+
+static int das08_do(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       do_pack(&s->state,it);
+       
+       outb_p(s->state<<4,dev->iobase+STATUS_REG);
+
+       return it->n_chan;
+}
+
+static int das08_di(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       unsigned int bits;
+       
+       bits=(inb(dev->iobase+STATUS_REG)>>4)&0x7;
+       
+       return di_unpack(bits,it);
+}
+
+static int das08_recognize(char *name)
+{
+       int i;
+
+       for(i=0;i<n_boardtypes;i++){
+               if(!strcmp(boardtypes[i].name,name))return i;
+       }
+       return -1;
+}
+
+static int das08_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       int i;
+       comedi_subdevice *s;
+       int ret;
+       
+       dev->iobase=it->options[0];
+       printk("comedi%d: das08: 0x%04x",dev->minor,dev->iobase);
+       if(check_region(dev->iobase,DAS08_SIZE)<0){
+               printk(" I/O port conflict\n");
+               return -EIO;
+       }
+       dev->board_name="das08";
+       dev->iosize=DAS08_SIZE;
+       dev->irq=0;
+       
+#ifdef DEBUG
+       printk("\nboard fingerprint:\n");
+       for(i=0;i<DAS08_SIZE;i++){
+               printk("%02x ",inb_p(dev->iobase+i));
+       }
+#endif
+       dev->n_subdevices=5;
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+       if((ret=alloc_private(dev,sizeof(das08_private)))<0)
+               return ret;
+       
+       request_region(dev->iobase,DAS08_SIZE,"das08");
+       
+       devpriv->boardtype=0;
+       
+       s=dev->subdevices+0;
+       /* ai */
+       s->type=COMEDI_SUBD_AI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=8;
+       s->maxdata=0xfff;
+       s->range_type=RANGE_unknown;    /* XXX */
+       s->trig[0]=das08_ai;
+
+       s=dev->subdevices+1;
+       /* ao */
+       if(this_board.ao_chans>0){
+               s->type=COMEDI_SUBD_AO;
+               s->subdev_flags=SDF_WRITEABLE;
+               s->n_chan=2;
+               s->maxdata=0xfff;
+               s->range_type=RANGE_unknown;    /* XXX */
+       }else{
+               s->type=COMEDI_SUBD_UNUSED;
+       }
+       s->trig[0]=das08_ao;
+
+       s=dev->subdevices+2;
+       subdev_8255_init(dev,s,NULL,(void *)(dev->iobase+DIO_PORTA));
+
+       s=dev->subdevices+3;
+       /* ai */
+       s->type=COMEDI_SUBD_DI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=3;
+       s->maxdata=1;
+       s->range_type=RANGE_digital;
+       s->trig[0]=das08_di;
+
+       s=dev->subdevices+4;
+       /* ai */
+       s->type=COMEDI_SUBD_DO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->n_chan=4;
+       s->maxdata=1;
+       s->range_type=RANGE_digital;
+       s->trig[0]=das08_do;
+
+       devpriv->dio=0;
+       
+
+       if(this_board.ao_chans>0){
+               /* zero AO */
+               outb(0,dev->iobase+DA_CHAN0_LSB);
+               outb(0,dev->iobase+DA_CHAN0_MSB);
+               outb(0,dev->iobase+DA_CHAN1_LSB);
+               outb(0,dev->iobase+DA_CHAN1_MSB);
+       }
+       
+       outb_p(0,dev->iobase+STATUS_REG);
+       
+#if 0
+       outb_p(0,dev->iobase+DIO_PORTA);
+       outb_p(0,dev->iobase+DIO_PORTB);
+       outb_p(0,dev->iobase+DIO_PORTC);
+#endif
+       
+       printk("\n");
+
+       return 0;
+}
+
+static int das08_detach(comedi_device *dev)
+{
+       printk("comedi%d: das08: remove\n",dev->minor);
+       
+       release_region(dev->iobase,dev->iosize);
+       
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_das08);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_das08);
+}
+#endif
diff --git a/comedi/drivers/das08jr.c b/comedi/drivers/das08jr.c
new file mode 100644 (file)
index 0000000..f2b1ce1
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+   The comedi version of this driver was modified from the
+   original by ds.
+
+ * das08jr
+ * =======
+ *
+ * Linux device driver for Computer Boards CIO-DAS08/Jr-AO board.
+ *
+ * This board has 8 analog input lines and 2 analog output lines. Additionally
+ * there are 8 digital outputs.
+ * These lines are mapped onto minor 0 (digital output), 1 - 8 for analog
+ * input, and 1 + 2 for analog output.
+ *
+ * Copyright (C) 1998 Jochen Küpper
+ *
+ * $Id$
+ */
+
+
+#include <comedi_module.h>
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+
+/* delay for A/D- and D/A-conversions [us] */
+#define DAS08JR_DELAY ( 30 )
+
+
+/* Module Name */
+#define DAS08JR_NAME "das08jr"
+
+
+
+
+/* driver version */
+#if 0
+static const char das08jr_version[] = "PCI-DAS08Jr-AO driver ($Id$)";
+#endif
+
+
+
+
+
+/* Port Offsets */
+#define DAS08JR_SIZE                            ( 8 )  /* number of ports */
+#define DAS08JR_ANALOG_IN_LSB                   ( 0 )  /* read */
+#define DAS08JR_ANALOG_IN_MSB                   ( 1 )  /* read */
+#define DAS08JR_START                           ( 1 )
+#define DAS08JR_STATUS                          ( 2 )  /* read */
+#define DAS08JR_MUX                             ( 2 )
+#define DAS08JR_CONTROL                         ( 2 )
+#define DAS08JR_DIGITAL_IN                      ( 3 )  /* read */
+#define DAS08JR_DIGITAL_OUT                     ( 3 )
+#define DAS08JR_ANALOG_OUT_LSB0                 ( 4 )
+#define DAS08JR_ANALOG_OUT_MSB0                 ( 5 )
+#define DAS08JR_ANALOG_OUT_LSB1                 ( 6 )
+#define DAS08JR_ANALOG_OUT_MSB1                 ( 7 )
+
+
+
+/* number of available lines */
+#define DAS08JR_ANALOG_INPUTS   ( 8 )
+#define DAS08JR_ANALOG_OUTPUTS  ( 2 )
+#define DAS08JR_DIGITAL_LINES   ( 1 )
+#define DAS08JR_MAX_LINE        ( 8 )  /* we have nine lines starting at zero */
+
+
+static int das08jr_attach(comedi_device *dev,comedi_devconfig *it);
+static int das08jr_detach(comedi_device *dev);
+comedi_driver driver_das08jr={
+       driver_name:    "das08jr",
+       module:         &__this_module,
+       attach:         das08jr_attach,
+       detach:         das08jr_detach,
+};
+
+
+typedef struct{
+       int last_do;
+}das08jr_priv;
+#define devpriv ((das08jr_priv *)dev->private)
+
+static void release_resources(comedi_device *dev);
+
+
+
+/**
+ * Read digital lines
+ *
+ * @author Jochen Küpper
+ * @version $Id$
+ */
+static int das08jr_di(comedi_device * dev,comedi_subdevice *s, comedi_trig * it)
+{
+       int chan;
+       int data;
+       int i;
+
+       data = inb(dev->iobase + DAS08JR_DIGITAL_IN);
+
+       for(i=0;i<it->n_chan;i++){
+               chan=CR_CHAN(it->chanlist[i]);
+               it->data[i]=(data>>chan)&1;
+       }
+
+       return i;
+}
+
+
+
+/**
+ * write digital lines
+ *
+ * @author Jochen Küpper
+ * @version $Id$
+ */
+static int das08jr_do(comedi_device * dev,comedi_subdevice *s, comedi_trig * it)
+{
+       int data;
+       int chan;
+       int mask;
+       int i;
+
+       data=devpriv->last_do;
+       for(i=0;i<it->n_chan;i++){
+               chan=CR_CHAN(it->chanlist[i]);
+               mask=1<<chan;
+               data&=~mask;
+               if(it->data[i])
+                       data|=mask;
+       }
+       devpriv->last_do=data;
+
+       outb(data, dev->iobase + DAS08JR_DIGITAL_OUT);
+
+       return i;
+}
+
+
+
+/**
+ * Read from analog line
+ *
+ * @author Jochen Küpper
+ * @version $Id$
+ */
+static int das08jr_ai(comedi_device * dev, comedi_subdevice *s, comedi_trig * it)
+{
+       unsigned char lsb, msb;
+
+       /* we alway read one short value */
+
+       /* start daq */
+
+       /* XXX do something here to trigger it */
+
+       /* busy waiting for board to finish */
+       udelay( DAS08JR_DELAY );
+
+       /* read data and put it into user space */
+
+       lsb = inb(DAS08JR_ANALOG_IN_LSB);
+       msb = inb(DAS08JR_ANALOG_IN_MSB);
+
+       it->data[0] = (((int) msb) << 4) + (lsb >> 4);
+
+       return 1;
+}
+
+
+/*------------------------------*/
+
+/**
+ * Write to writeable analog line.
+ *
+ * We do not need to check wether this really is an writeable line, since open
+ * cares about that an we only get called on lines 1 or 2.
+ *
+ * We simply put one value to the output ports, start D/A conversion, and return.
+ *
+ * @return On success the number of written bytes ( always 2 ),
+ *         a negative error code otherwise.
+ *
+ * @author Jochen Küpper
+ * @version $Id$
+ */
+static int das08jr_ao(comedi_device * dev, comedi_subdevice *s, comedi_trig * it)
+{
+       unsigned short val = it->data[0];
+       int chan=CR_CHAN(it->chanlist[0]);
+
+       if (chan){
+               outb((unsigned char) val >> 4, DAS08JR_ANALOG_OUT_MSB1);
+               outb((unsigned char) val << 4, DAS08JR_ANALOG_OUT_LSB1);
+       } else {
+               outb((unsigned char) val >> 4, DAS08JR_ANALOG_OUT_MSB0);
+               outb((unsigned char) val << 4, DAS08JR_ANALOG_OUT_LSB0);
+       }
+
+       /* what does this do? */
+       outb(0xff, DAS08JR_START);
+
+       return 1;
+}
+
+
+
+
+/**
+ * Register ports,
+ *
+ * @author Jochen Küpper
+ * @version $Id$
+ */
+static int das08jr_attach(comedi_device * dev, comedi_devconfig * it)
+{
+       int result = 0;
+       int io;
+       comedi_subdevice *s;
+
+       printk("comedi%d: das08jr: ", dev->minor);
+
+       io = it->options[0];
+#if 0
+       if (0 == io) {
+               /* should autoprobe here */
+               io = DAS08JR_PORT;
+       }
+#endif
+       if ((result = check_region(io, DAS08JR_SIZE)) < 0) {
+               printk("Cannot register port memory at %x.\n", io);
+               return result;
+       }
+       request_region(io, DAS08JR_SIZE, DAS08JR_NAME);
+
+       dev->board_name = DAS08JR_NAME;
+       dev->iobase = io;
+
+       printk("Copyright (C) 1998 Jochen K\"upper.\n");
+
+       dev->n_subdevices = 3;
+       if((result=alloc_subdevices(dev))<0)
+               return result;
+       if((result=alloc_private(dev,sizeof(das08jr_priv)))<0)
+               return result;
+
+       s = dev->subdevices + 0;
+       /* ai subdevice */
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = 8;
+       s->maxdata = 0xfff;
+       s->range_type = RANGE_unknown;
+       s->trig[0] = das08jr_ai;
+
+       s = dev->subdevices + 1;
+       /* ao subdevice */
+       s->type = COMEDI_SUBD_AO;
+       s->subdev_flags = SDF_WRITEABLE;
+       s->n_chan = 2;
+       s->maxdata = 0xfff;
+       s->range_type = RANGE_unknown;
+       s->trig[0] = das08jr_ao;
+
+       s = dev->subdevices + 2;
+       /* di subdevice */
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = 8;
+       s->maxdata = 1;
+       s->range_type = RANGE_digital;
+       s->trig[0] = das08jr_di;
+
+       s = dev->subdevices + 3;
+       /* do subdevice */
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITEABLE;
+       s->n_chan = 8;
+       s->maxdata = 1;
+       s->range_type = RANGE_digital;
+       s->trig[0] = das08jr_do;
+
+       return 0;
+}
+
+
+static void release_resources(comedi_device *dev)
+{
+       if (dev->iobase)
+               release_region(dev->iobase, DAS08JR_SIZE);
+}
+
+/**
+ * @author Jochen Küpper
+ * @version $Id$
+ */
+static int das08jr_detach(comedi_device *dev)
+{
+       printk("comedi%d: das08jr: remove\n", dev->minor);
+
+       release_resources(dev);
+
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_das08jr);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_das08jr);
+}
+#endif
diff --git a/comedi/drivers/das16.c b/comedi/drivers/das16.c
new file mode 100644 (file)
index 0000000..a4c7586
--- /dev/null
@@ -0,0 +1,1188 @@
+/*
+ * Copyright (C) 1995,1996  Sam Moore, Warren Jasper
+ *               1999       David Schleef
+ * All rights reserved.
+ *
+ * This software may be freely copied, modified, and redistributed
+ * provided that this copyright notice is preserved on all copies.
+ *
+ * There is no warranty or other guarantee of fitness of this software
+ * for any purpose.  It is provided solely "as is".
+ *
+ */
+/*
+ * The comedi version of this driver was modified from the original
+ * by David Schleef.
+ */
+
+#include <comedi_module.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#define DEBUG
+
+/*
+ *   Note: Interrupts, when enabled, are generated differently depending
+ *   upon the mode of the board:
+ *   Compatibility mode: Interrupts generated every A/D conversion
+ *                       from either pacer clock or soft trigger.
+ *   Enhanced mode:      Interrupts generated when FIFO half full
+ *                       or when Total_Count = 0.
+ */
+
+/* DT Connection is not supported */
+
+#define DT_CONNECT FALSE
+
+
+#define AD_CHANNELS         16 /* 16 Single Ended or 8 Diff.  */
+#define DIO_CHANNEL         16 /* dio minor channel number    */
+
+#define PORT_SIZE        0x14  /* uses 20 bytes of registers                 */
+
+
+#define LSB_AND_CHNLS    0     /* A/D Data & Channel Register      */
+#define MSB_DATA_BYTE    1
+#define MUX_SCAN_LIMITS  2     /* Channel Mux Scan Limits Register */
+#define DIO_REG          3     /* 4 Bit Digital I/O Register       */
+#define STATUS_REG       8     /* Status Register                  */
+#define CNTRL_REG        9     /* DMA, Interrupt & Trigger Control */
+#define PACER_CLOCK_REG  10    /* Pacer Clock Control Register     */
+#define MISC_REG         11    /* Analog Input Range Reg., ETC     */
+
+#define COUNTERA_0_DATA  12    /* Pacer Clock A Counter 0 Register */
+#define COUNTERA_1_DATA  13    /* Pacer Clock A Counter 1 Register */
+#define COUNTERA_2_DATA  14    /* Pacer Clock A Counter 2 Register */
+#define COUNTERA_CONTROL 15    /* Pacer Clock A Control  Register  */
+
+#define COUNTERB_0_DATA  16    /* Pacer Clock B Counter 0 Register */
+#define COUNTERB_1_DATA  17    /* Pacer Clock B Counter 1 Register */
+#define COUNTERB_2_DATA  18    /* Pacer Clock B Counter 2 Register */
+#define COUNTERB_CONTROL 19    /* Pacer Clock B Control  Register  */
+
+
+/*************************************************************************
+* STATUS_REG         base_reg+8             Status Register              *
+**************************************************************************/
+#define ADSR_INT        0x10   /* INT=1 External pulse received, INT=0 waiting */
+#define ADSR_MUX        0x20   /* MUX=1 16 Single Ended, MUX=0 8 Differential  */
+#define ADSR_UB         0x40   /* U/B=1 Unipolar mode, U/B=0 Bipolar mode      */
+#define ADSR_EOC        0x80   /* EOC=1, A/D is busy, EOC=0 free               */
+
+/*************************************************************************
+* CNTRL_REG          base_reg+9        DMA, Interrupt & Trigger Control  *
+**************************************************************************/
+#define CNTL_TS0       0x1     /* TS0=0 External Trigger, TS0=1 Pacer Clock */
+#define CNTL_TS1       0x2     /* TS1=0 Software triggered A/D only         */
+#define CNTL_DMA       0x4     /* DMA Disabled = 0, DMA Enabled = 1         */
+#define CNTL_INTE      0x80    /* Interrupts Disabled = 0, Enabled = 1      */
+
+/*************************************************************************
+* MISC_REG        base_reg+11         Range, and Exteded Features        *
+**************************************************************************/
+
+#define ENHANCED_BIT  (0x10)   /* Enhanced Mode Enabled = 1, Disabled = 0 */
+#define MISC_OVERRUN  (0x20)   /* No overrun = 0, FIFP buffer full = 1    */
+#define MISC_PRETRIG  (0x40)   /* Pretrigger Enabled = 1, Disable = 0     */
+#define MISC_DT       (0x80)   /* DT-Connect Enable = 1, Disable = 0      */
+
+/*************************************************************************
+* Constants for dealing with the 8254 counters                           *
+**************************************************************************/
+
+#define MODE0 0x0
+#define MODE1 0x2
+#define MODE2 0x4
+#define MODE3 0x6
+#define MODE4 0x8
+#define MODE5 0xa
+
+#define C0 0x00
+#define C1 0x40
+#define C2 0x80
+
+//#define LATCH    0x00
+#define LSBONLY  0x10
+#define MSBONLY  0x20
+#define LSBFIRST 0x30
+
+#define S0       0x00
+#define S1       0x02
+
+#define PACKETSIZE  512
+#define MAX_COUNT   16384
+/* Interrupt context data. Controls interrupt service handler */
+
+typedef struct{
+       unsigned int ir;        /* shifted bits for board 0-7, 10->0, 11->1 */
+       unsigned int misc_reg;
+} das16_private ;
+#define devpriv ((das16_private *)dev->private)
+
+/* Values kept for each channel. */
+
+#if 0
+typedef struct CHANNEL_REC {
+       int open;               /* Is channel device open()                 */
+       int adc_dio_pretrig;    /* TRUE = write dio before sampling         */
+       LONG count;             /* Number of samples requested              */
+       BYTE dio_value;         /* DIO value to be written before sampling  */
+       BYTE mode;              /* 0 =  Soft Trigger                        */
+       /* 2 = External Trigger                     */
+       /* 4 = Pacer Clock                          */
+       /* 6 = Pacer Clock External Trig Enable     */
+       BYTE gain;              /* Voltage range                            */
+       BYTE lowChan;           /* Starting Channel of MUX scan limit       */
+       BYTE hiChan;            /* Ending Channel of MUX scan limit         */
+} ChanRec;
+
+#endif
+
+
+
+
+
+
+#if 0
+static WORD base_reg = PORT_MIN;       /* base register address */
+static int WordsToRead;                /* number of conversions until done  */
+static ChanRec *CurrChan = NULL;       /* pointer to Chan[minor]            */
+static int MajorNumber = DEFAULT_MAJOR_DEV;    /* Major number compiled in          */
+static ChanRec Chan[DIO_CHANNEL + 1];  /* Channel specific information      */
+static BoardRec BoardData;     /* Board specific information        */
+static WORD KernBuff[MAX_COUNT];       /* Kernel buffer where samples are   */
+                                           /* saved before write to user space  */
+                                           /* structure for timer data          */
+static WORD *KernBuffPtr;      /* pointer to kernbuf                */
+struct wait_queue *adc0_wait;  /* wait semaphore                    */
+struct timer_list TimerList =
+{NULL, NULL, 0, 0, adc0_TimerHandler};
+#endif
+
+int irq_list[]={ -1, -1, 2, 3, 4, 5, 6, 7, -1, -1, 0, 1, -1, -1, -1, -1 };
+
+static int compatibility_read(comedi_device *dev,comedi_trig *it);
+static void release_resources(comedi_device *dev);
+
+static int das16_attach(comedi_device *dev,comedi_devconfig *it);
+static int das16_detach(comedi_device *dev);
+comedi_driver driver_das16={
+       driver_name:    "das16",
+       module:         &__this_module,
+       attach:         das16_attach,
+       detach:         das16_detach,
+};
+
+
+/*
+   interrupt routine
+
+ */
+
+static void das16_interrupt(int irq,void *d,struct pt_regs *regs)
+{
+
+}
+
+
+/*
+   AD subsystem
+
+ */
+
+static void set_gain(comedi_device * dev, int gain)
+{
+       devpriv->misc_reg &= 0xf0;
+       devpriv->misc_reg |= (gain << 4);
+       outb_p(devpriv->misc_reg, dev->iobase + MISC_REG);
+}
+
+static int set_channel_mux(comedi_device * dev, int first_chan, int last_chan)
+{
+       int channel;
+
+       channel = (last_chan << 4) | first_chan;
+       outb_p(channel, dev->iobase + MUX_SCAN_LIMITS);
+
+       while ((inb_p(dev->iobase + MISC_REG) & MISC_OVERRUN))
+               /* spin */;
+
+       return 0;
+}
+
+static int das16_ai_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       int bReg;
+
+       /* Put in Compatibility Mode here just to be safe */
+       bReg = inb(dev->iobase + MISC_REG);
+       outb(bReg & ~ENHANCED_BIT, dev->iobase + MISC_REG);
+
+       /* Set modes for this channel */
+       set_gain(dev, CR_RANGE(it->chanlist[0]));
+
+       set_channel_mux(dev, CR_CHAN(it->chanlist[0]), CR_CHAN(it->chanlist[0]));
+
+       compatibility_read(dev,it);
+
+       return 1;
+}
+
+
+/*
+   DIO subsystem
+
+ */
+
+static int das16_dio(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       int data;
+
+       data = inb(dev->iobase + DIO_REG);
+
+       return di_unpack(data, it);
+}
+
+/*
+   AO subsystem
+
+ */
+
+/***************************************************************************
+ *
+ * Loads driver. Called when "insmod adc.o" is invoked on the command line.
+ *               The board is set to IRQ, 
+ *
+ ***************************************************************************/
+
+static int das16_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       int iobase,irq;
+       int ret=0;
+       int chan;
+
+       iobase=it->options[0];
+       if (check_region(iobase, PORT_SIZE) != 0) {     /* in use */
+               printk("comedi%d: das16: Can't allocate region port address 0x%x\n",
+                      dev->minor,iobase);
+               return -EIO;
+       }
+       request_region(iobase, PORT_SIZE, "das16");
+       dev->iobase=iobase;
+
+#if 0
+       /* we don't really want to search for boards... */
+       /* Look to see if ADC is installed */
+       for (base_reg = PORT_MIN; base_reg <= PORT_MAX; base_reg += PORT_STEP) {
+               if (check_region(base_reg, PORT_SIZE) == 0) {   /* not in use */
+                       request_region(base_reg, PORT_SIZE, "adc");
+                       if (adc_find(base_reg)) {       /* found the board */
+                               BoardData.base = base_reg;
+                               break;
+                       } else {
+                               release_region(base_reg, PORT_SIZE);
+                       }
+               }
+       }
+
+       if (base_reg > PORT_MAX) {
+               printk("%s: No boards found from address %#x to %#x.\n",
+                      ADAPTER_ID, PORT_MIN, PORT_MAX);
+               if (unregister_chrdev(MajorNumber, "adc") != 0) {
+                       printk("%s: unregister_chrdev() failed.\n", ADAPTER_ID);
+               }
+               return -ENODEV;
+       }
+#endif
+
+       /* Register interrupt handler. */
+
+       irq=it->options[1];
+
+       if(irq<0 || irq>=16 || irq_list[irq]<0){
+               return -EINVAL;
+       }
+       if (request_irq(irq, das16_interrupt, SA_INTERRUPT, "das16", dev) == 0) {
+               return -EIO;
+       }
+       dev->irq=irq;
+
+#if 0
+       printk("%s: address=%#x IRQ=%d.", ADAPTER_ID, BoardData.base, BoardData.irq);
+       printk(" 4/27/95 wjasper@tx.ncsu.edu sam@tx.ncsu.edu\n");
+#endif
+
+       if((ret=alloc_private(dev,sizeof(das16_private)))<0)
+               return ret;
+
+       dev->n_subdevices=3;
+
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+       
+       s=dev->subdevices;
+
+       if (inb(dev->iobase+STATUS_REG) & ADSR_MUX) {
+               chan = 16;
+       } else {
+               chan = 8;
+       }
+       /* ai subdevice */
+       s->type=COMEDI_SUBD_AI;
+       s->n_chan=8;
+       s->trig[0]=das16_ai_mode0;
+       s->maxdata=0xfff;
+
+       s++;
+       /* ao subdevice */
+       s->type=COMEDI_SUBD_AO;
+       s->n_chan=2;
+#if 0
+       s->trig[0]=das16_ao;
+#endif
+       s->maxdata=0xfff;
+
+       s++;
+       /* dio subdevice */
+       s->type=COMEDI_SUBD_DIO;
+       s->n_chan=8;
+       s->trig[0]=das16_dio;
+       s->maxdata=1;
+
+
+       /* Set interrupt level on board  2-7, 10->0 and 11->1 */
+       devpriv->ir = irq_list[irq] << 4;
+
+       /* ... clearing INTE bit in control reg */
+       outb(devpriv->ir, dev->iobase+CNTRL_REG);
+
+       outb(0x0, dev->iobase+MISC_REG);        /* Put in compatibility mode */
+
+       printk("\n");
+
+       return 0;
+}
+
+
+#if 0
+/***************************************************************************
+ *
+ * Test memory locations for exsistance of adc board.
+ *
+ ***************************************************************************/
+static int adc_find(WORD base_reg)
+{
+
+/*
+   To test if there is a DAS 16/330 board do the following:
+   1. write MUX_SCAN_LIMITS register.
+   2. read MUX_SCAN_LIMITS register. If fail return FALSE.
+   3. read STATUS_REG (bits 0-3).  If fail return FALSE.
+ */
+
+       BYTE bReg = 0x0;
+
+       bReg = 0xa3;            /* set mux from channel 3 to channel 10 */
+       outb_p(bReg, MUX_SCAN_LIMITS);  /* write to register */
+       if (inb_p(MUX_SCAN_LIMITS) != 0xa3)
+               return FALSE;   /* not a 16/330 board */
+       if ((inb_p(STATUS_REG) & 0xf) != 0x3)
+               return FALSE;   /* not a 16/330 board */
+       bReg = 0xd2;            /* set mux from channel 2 to channel 13 */
+       outb_p(bReg, MUX_SCAN_LIMITS);  /* write to register */
+       if (inb_p(MUX_SCAN_LIMITS) != 0xd2)
+               return FALSE;   /* not a 16/330 board */
+       if ((inb_p(STATUS_REG) & 0xf) != 0x2)
+               return FALSE;   /* not a 16/330 board */
+       return TRUE;            /* found it!          */
+}
+#endif
+
+/***************************************************************************
+ *
+ * Remove driver. Called when "rmmod adc" is run on the command line.
+ *
+ ***************************************************************************/
+
+static void release_resources(comedi_device *dev)
+{
+       if(dev->irq){
+               free_irq(dev->irq,dev);
+       }
+
+       if(dev->iobase)
+               release_region(dev->iobase, PORT_SIZE);
+
+}
+
+static int das16_detach(comedi_device *dev)
+{
+       release_resources(dev);
+
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_das16);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_das16);
+}
+#endif
+
+#if 0
+static int adc_open(struct inode *iNode, struct file *filePtr)
+{
+       int minor = MINOR(iNode->i_rdev);
+
+       MOD_INC_USE_COUNT;
+
+       /* 
+          check if device is already open: only one process may read from a
+          port at a time.  There is still the possibility of two processes 
+          reading from two different channels messing things up. However,
+          the overhead to check for this may not be worth it.
+        */
+
+       if (Chan[minor].open == TRUE) {
+               return -EBUSY;
+       }
+       Chan[minor].open = TRUE;        /* The device is open */
+       Chan[minor].gain = BP_10_00V;   /* +/- 10V */
+       Chan[minor].mode = filePtr->f_flags;    /* set trigger mode */
+       Chan[minor].lowChan = minor;    /* set MUX scan limits to current channel */
+       Chan[minor].hiChan = minor;
+
+       if (Chan[minor].mode == ADC_PACER_EXTERN_TRIG) {
+               BoardData.pacerReg = 0x1;
+       } else {
+               BoardData.pacerReg = 0x0;
+       }
+
+       outb_p(0x00, STATUS_REG);       /* Clear INT bit */
+       outb_p(0x0, MISC_REG);  /* Put in compatibility mode */
+
+#ifdef DEBUG
+       printk("%s: open(): minor %d mode %d.\n", ADAPTER_ID, minor, Chan[minor].mode);
+#endif
+       return 0;
+}
+#endif
+
+
+
+#if 0
+static int adc_read(struct inode *iNode, struct file *filePtr, char *buf, int count)
+{
+       int stat;
+       BYTE bReg;
+       int minor = MINOR(iNode->i_rdev);
+       register int i;
+
+       /* Read */
+
+       switch (Chan[minor].mode) {
+
+       case ADC_SOFT_TRIGGER:
+#ifdef DEBUG
+               printk("adc_read(): Entering ADC_SOFT_TRIGGER mode.\n");
+#endif
+               if (CompatibilityRead(KernBuff, &Chan[minor])) {
+                       printk("adc_read: SoftTrigRead() failed.\n");
+                       BoardData.busy = FALSE;
+                       return (-1);
+               }
+               break;
+
+       case ADC_EXTERNAL_TRIGGER:
+#ifdef DEBUG
+               printk("adc_read(): Entering ADC_EXTERNAL_TRIGGER mode.\n");
+#endif
+               if (Chan[minor].count == 1) {   /* use compatibility mode */
+                       if (CompatibilityRead(KernBuff, &Chan[minor])) {
+                               printk("adc_read: CompatibilityRead() failed with ADC_EXTERNAL_TRIGGER.\n");
+                               BoardData.busy = FALSE;
+                               return (-1);
+                       }
+               } else {
+                       StopPacer();    /* disable pacer if in pacer mode */
+                       if (EnhancedRead(KernBuff, &Chan[minor])) {
+                               printk("adc_read: ExternalRead() failed with ADC_EXTERNAL_TRIGGER.\n");
+                               BoardData.busy = FALSE;
+                               return (-1);
+                       }
+               }
+               break;
+
+       case ADC_PACER_CLOCK:
+#ifdef DEBUG
+               printk("adc_read(): Entering ADC_PACER_CLOCK mode.\n");
+#endif
+               if (Chan[minor].count == 1) {   /* use compatibility mode */
+                       if (CompatibilityRead(KernBuff, &Chan[minor])) {
+                               printk("adc_read: CompatibilityRead() failed with pacer.\n");
+                               BoardData.busy = FALSE;
+                               return (-1);
+                       }
+               } else {
+                       StopPacer();    /* disable pacer if in pacer mode */
+                       outb_p(0x1, PACER_CLOCK_REG);   /*IP0 controls counter gate */
+                       if (EnhancedRead(KernBuff, &Chan[minor])) {
+                               printk("adc_read: EnchancedRead() failed.\n");
+                               BoardData.busy = FALSE;
+                               return (-1);
+                       }
+               }
+               break;
+
+       case ADC_PACER_EXTERN_TRIG:
+#ifdef DEBUG
+               printk("adc_read(): Entering ADC_PACER_EXTERN_TRIG mode.\n");
+#endif
+               StopPacer();    /* disable pacer if in pacer mode */
+               outb_p(0x1, PACER_CLOCK_REG);   /*IP0 controls counter gate */
+               if (EnhancedRead(KernBuff, &Chan[minor])) {
+                       printk("adc_read: EnchancedRead() with external tigger failed.\n");
+                       BoardData.busy = FALSE;
+                       return (-1);
+               }
+               break;
+       }
+
+       /* Check that data can be written to file. */
+
+       if ((stat = verify_area(VERIFY_WRITE, buf, sizeof(WORD) * Chan[minor].count)) != 0) {
+               printk("adc_read: Failed VERIFY_WRITE.\n");
+               BoardData.busy = FALSE;
+               return -1;
+       }
+       /* Write data to user space */
+
+       for (i = 0; i < Chan[minor].count; i++) {
+               KernBuff[i] >>= 4;
+       }
+
+       if (Chan[minor].count == 1) {
+               put_fs_word(*KernBuff, (WORD *) buf);
+       } else {
+               memcpy_tofs(buf, KernBuff, Chan[minor].count * sizeof(WORD));
+       }
+
+       BoardData.busy = FALSE;
+       return (Chan[minor].count);     /* return number of samples read */
+}
+#endif
+
+
+
+
+#if 0
+static int dio_write(struct inode *iNode, struct file *filePtr, char *buf, int count)
+{
+       int minor = MINOR(iNode->i_rdev);
+       BYTE value;
+
+       if (minor != DIO_CHANNEL) {
+               printk("dio_write: bad minor number = %d\n", minor);
+               return -1;
+       }
+       cli();
+       value = get_fs_byte(buf) & 0xf;
+       outb_p(value, DIO_REG);
+       sti();
+#ifdef DEBUG
+       printk("DIO set to %#x\n", value & 0xf);
+#endif
+       return 1;
+}
+#endif
+
+
+#if 0
+static int adc_ioctl(struct inode *iNode, struct file *filePtr, unsigned int cmd, LONG arg)
+{
+       int minor = MINOR(iNode->i_rdev);
+
+       switch (cmd) {
+       case ADC_SET_GAINS:
+               Chan[minor].gain = (BYTE) arg;
+               break;
+       case ADC_GET_GAINS:
+               put_fs_long((long) Chan[minor].gain, (long *) arg);
+               break;
+       case ADC_SET_PACER_FREQ:
+               if (BoardData.freq > MAX_FREQ && Chan[minor].mode == ADC_PACER_CLOCK) {
+                       printk("ioctl:  BoardData.freq = %ld out of range.\n", BoardData.freq);
+                       return -1;
+               } else {
+                       BoardData.freq = (LONG) arg;
+                       SetPacerFreq(&BoardData);
+                       LoadPacer(&BoardData);  /* load the board frequency now */
+                       outb_p(BoardData.pacerReg, PACER_CLOCK_REG);
+               }
+               break;
+       case ADC_GET_PACER_FREQ:
+               put_fs_long(BoardData.freq, (long *) arg);
+               break;
+       case ADC_STOP_PACER:
+               StopPacer();
+               break;
+       case CTR0:
+               if (arg == 1) {
+                       BoardData.pacerReg |= 0x2;
+               } else {
+                       BoardData.pacerReg &= ~(0x2);
+               }
+               outb_p(BoardData.pacerReg, PACER_CLOCK_REG);
+               break;
+       case COUNTER0:
+               LoadCounter0((LONG) arg);
+               break;
+       case ADC_DIO_PRESET:
+               if (arg == -1) {
+                       Chan[minor].adc_dio_pretrig = FALSE;
+               } else {
+                       Chan[minor].adc_dio_pretrig = TRUE;
+                       Chan[minor].dio_value = (BYTE) (arg & 0xf);
+               }
+
+#ifdef DEBUG
+               printk("ioctl(ADC_DIO_PRESET):  value = %#x\n", Chan[minor].dio_value);
+#endif
+
+               break;
+       case ADC_GET_STATUS:
+               /* return status register */
+               put_fs_long(inb_p(STATUS_REG), (long *) arg);
+               break;
+       case SET_MUX_LOW:
+               Chan[minor].lowChan = (BYTE) arg;
+               break;
+       case SET_MUX_HIGH:
+               Chan[minor].hiChan = (BYTE) arg;
+               break;
+       case GET_MUX_SCAN_LIMITS:
+               put_fs_long(inb_p(MUX_SCAN_LIMITS), (long *) arg);
+               break;
+       default:
+               return (-EINVAL);
+               break;
+       }
+       return 0;
+}
+#endif
+
+/***************************************************************************
+ *
+ * Block copy of data samples from data register to kernel memory.
+ *
+ ***************************************************************************/
+
+#if 0
+inline char *
+ RegCopy(char *dest, const int reg, int n)
+{
+       __asm__ __volatile__(
+                                   "cld\n\t"
+                                   "rep\n\t"
+                                   "insw"
+                                   :
+                                   :"D"(dest), "d"(reg), "c"(n)
+                                   :"di", "dx", "cx");
+       return dest;
+}
+#endif
+
+/***************************************************************************
+ *
+ *  Alarm handler to handle situations where an interrupt is missed.
+ *
+ ***************************************************************************/
+
+#if 0
+static void adc0_TimerHandler(LONG unused)
+{
+       /* 
+          we should only be here if an interrupt occured while 
+          we were reading half the FIFO 
+        */
+
+#ifdef DEBUG
+       printk("TimerHandler: WordsToRead = %d\n", WordsToRead);
+#endif
+
+       cli();
+       outb_p(0x30, COUNTERB_CONTROL);         /* Set TOTALBAR to 1 */
+       RegCopy((char *) KernBuffPtr, base_reg, WordsToRead);
+       WordsToRead -= WordsToRead;
+       del_timer(&TimerList);
+       wake_up_interruptible(&adc0_wait);
+}
+#endif
+
+/***************************************************************************
+ *
+ * Interrupt handler used to service enhanced mode(interrupt) read().
+ *
+ ***************************************************************************/
+
+#if 0
+static void adc0_ReadInterrupt(int irq)
+{
+       WORD msb, lsb;
+
+#ifdef DEBUG
+       printk("Entering adc0_ReadInterrupt().  mode = %d\n", CurrChan->mode);
+#endif
+
+       cli();
+       switch (CurrChan->mode) {
+
+       case ADC_SOFT_TRIGGER:
+               /* Bang! Bang! Method: Force conversion, Conversion forces interrupt */
+               lsb = (WORD) inb(base_reg);     /* Sample: ADC -> kernel buffer */
+               msb = (WORD) inb(MSB_DATA_BYTE);
+               *KernBuffPtr++ = (msb << 8) | lsb;
+               WordsToRead--;  /* One sample at a time */
+               if (!WordsToRead) {
+                       wake_up_interruptible(&adc0_wait);
+               } else {
+                       outb_p(0x00, STATUS_REG);       /* Clear INT bit, allow Interrupt */
+                       outb_p(0x1, base_reg);  /* Force conversion */
+                       sti();
+               }
+               break;
+
+       case ADC_PACER_CLOCK:
+       case ADC_EXTERNAL_TRIGGER:
+               if (CurrChan->count == 1) {
+                       lsb = (WORD) inb(base_reg);     /* Sample: ADC -> kernel buffer */
+                       msb = (WORD) inb(MSB_DATA_BYTE);
+                       *KernBuffPtr++ = (msb << 8) | lsb;
+                       wake_up_interruptible(&adc0_wait);
+                       break;
+               }
+       case ADC_PACER_EXTERN_TRIG:
+               if (WordsToRead > PACKETSIZE) {
+                       RegCopy((char *) KernBuffPtr, base_reg, PACKETSIZE);
+                       WordsToRead -= PACKETSIZE;
+                       KernBuffPtr += PACKETSIZE;
+                       if (WordsToRead < PACKETSIZE) {
+                               TimerList.expires = WordsToRead * 100 / BoardData.freq + 2;
+                               add_timer(&TimerList);
+                       }
+                       outb_p(0x00, STATUS_REG);       /* Clear INT bit, allow Interrupt */
+                       sti();
+               } else {
+                       outb_p(0x30, COUNTERB_CONTROL);         /* Set TOTALBAR to 1 */
+                       RegCopy((char *) KernBuffPtr, base_reg, WordsToRead);
+                       del_timer(&TimerList);
+                       WordsToRead -= WordsToRead;
+                       wake_up_interruptible(&adc0_wait);
+                       return;
+               }
+               break;
+       default:
+               break;
+       }
+}
+#endif
+
+/***************************************************************************
+ *
+ * Handles software/pacer triggered read().
+ *
+ * Bang! Bang! Method: 
+ *    
+ *    o Runs in Compatibility Mode
+ *    o Force interrupt by forcing conversion.
+ *    o Get one sample at a time and put it in the kernel buffer(kernBuff).
+ *    o Get chan->count samples
+ *
+ ***************************************************************************/
+
+static int compatibility_read(comedi_device *dev,comedi_trig *it)
+{
+       int bReg;
+
+#ifdef DEBUG
+       printk("Entering CompatibilityRead().\n");
+#endif
+
+       /* Set interrupt level on board, disable interrupts, and pacer  */
+
+       outb(devpriv->ir, dev->iobase + CNTRL_REG);     /* & clear INTE bit in control reg */
+
+       bReg = inb(dev->iobase+MISC_REG);       /* put in compatibility mode */
+       outb(bReg & ~ENHANCED_BIT, MISC_REG);
+
+       bReg = inb(dev->iobase + MUX_SCAN_LIMITS);      /* Read/Write clears FIFO */
+       outb(bReg, dev->iobase + MUX_SCAN_LIMITS);
+
+#if 0
+       /* Prepare global data for parameterless interrupt handler */
+
+       CurrChan = chan;        /* pass chanel number to global */
+       KernBuffPtr = KernBuff; /* same with KernBuff           */
+       WordsToRead = chan->count;
+
+       bReg = inb(CNTRL_REG);  /* Turns on ADC interrupts */
+
+       switch (chan->mode) {
+
+       case ADC_SOFT_TRIGGER:
+               outb(bReg | CNTL_INTE, CNTRL_REG);
+               outb(0x00, STATUS_REG); /* Clears INT bit, allow Int. */
+               outb(0x1, base_reg);    /* Force first conversion */
+               break;
+
+       case ADC_EXTERNAL_TRIGGER:
+               outb(bReg | CNTL_INTE | 0x2, CNTRL_REG);
+               outb(0x00, STATUS_REG); /* Clears INT bit, allow Int. */
+               break;
+
+       case ADC_PACER_CLOCK:
+               outb(bReg | CNTL_INTE | 0x3, CNTRL_REG);
+               outb(0x00, STATUS_REG); /* Clears INT bit, allow Int. */
+               break;
+       }
+
+#endif
+       outb(devpriv->ir, dev->iobase+CNTRL_REG);       /* & clear INTE bit in control reg */
+
+       return 0;
+}
+
+
+/****************************************************************************
+ *   Set Pacer Clock Frequency
+ * 
+ *   Description: 
+ *       Set the counters so that the pacer clock runs at the
+ *       desired frequency.  The frequency is generated by dividing
+ *       down a 10 MHz clock, so all frequencies can not be generated.
+ *       This routine calculated the divisor to generate a frequency
+ *       as near as possible to the requested one.  It then calculates
+ *       the real frequency and returns it .
+ ****************************************************************************/
+
+#if 0
+static int SetPacerFreq(BoardRec * board)
+{
+       short unsigned ctr1, ctr2;
+       LONG product, error;
+
+       /* divide 10Mhz by frequency */
+       product = 10000000.0 / board->freq + 0.5;
+
+       /* Now the job is to find two 16 bit numbers, that when multiplied
+          together are approximately equal to product.  Start by setting
+          one of them, ctr1 to 2 (minimum settable value) and increment until
+          the error is minimized and ctr2 is less than 32768.
+
+          NOTE: In Mode 2, a value of 1 is illegal! Therefore, crt1 and crt2
+          can never be 1.
+
+        */
+
+       ctr1 = product / 32768;
+       if (ctr1 < 2)
+               ctr1 = 2;
+       ctr2 = product / ctr1;
+       error = abs(product - (long) ctr2 * (long) ctr1);
+
+       while (error && ctr1 < 32768 && ctr2 > 1) {
+               ctr1++;
+               ctr2 = product / ctr1;
+               error = abs(product - (long) ctr2 * (long) ctr1);
+       }
+
+       /* the frequency is prime, add 1 to it */
+       if (error) {
+               product++;
+               ctr1 = product / 32768;
+               if (ctr1 < 2)
+                       ctr1 = 2;
+               ctr2 = product / ctr1;
+               error = abs(product - (long) ctr2 * (long) ctr1);
+
+               while (error && ctr1 < 32768 && ctr2 > 1) {
+                       ctr1++;
+                       ctr2 = product / ctr1;
+                       error = abs(product - (long) ctr2 * (long) ctr1);
+               }
+       }
+       /* we can't have ctr2 equal to 1, or system hangs */
+       if (ctr2 == 1) {
+               ctr2++;
+               ctr1 /= 2;
+       }
+       board->ctr1 = ctr1;
+       board->ctr2 = ctr2;
+       board->freq = 10000000.0 / ((long) ctr1 * (long) ctr2) + 0.5;
+
+#ifdef DEBUG
+       printk("SetPacerFreq: Pacer Register set to %#x\n", BoardData.pacerReg);
+#endif
+
+       return 0;
+}
+#endif
+
+/***************************************************************************
+ *
+ * Load two part frequency to pacer counter chip.
+ *
+ ***************************************************************************/
+
+static void load_pacer(comedi_device *dev,int timer)
+{
+       unsigned int mask;
+       unsigned int ctr1,ctr2;
+
+       /* Write the values of ctr1 and ctr2 into counter A1 and A2 */
+
+       ctr1=timer&0xffff;
+       ctr2=(timer>>16)&0xffff;
+
+       mask = C2 | MODE2 | LSBFIRST;
+       outb(mask, dev->iobase+COUNTERA_CONTROL);
+
+       outb(ctr2 & 0xff , dev->iobase + COUNTERA_2_DATA);
+       outb(ctr2 >> 8, dev->iobase + COUNTERA_2_DATA);
+
+       mask = C1 | MODE2 | LSBFIRST;
+       outb(mask, dev->iobase+COUNTERA_CONTROL);
+
+       outb(ctr1 & 0xff, dev->iobase + COUNTERA_1_DATA);
+       outb(ctr1 >> 8, dev->iobase + COUNTERA_1_DATA);
+
+}
+
+/***************************************************************************
+ *
+ * Load value into Counter 0  XXXX    Mode    MSB     LSB
+ *                            Byte 3  Byte 2  Byte 1  Byte 0
+ *
+ ***************************************************************************/
+
+static void load_counter(comedi_device *dev,int mode,int value)
+{
+       unsigned int mask;
+
+       /* Write the value into Counter 0 Mode 2 */
+
+       /* the mode is in the thrid byte */
+       mask = (0xff & mode) | C0 | LSBFIRST;
+       outb(mask, dev->iobase + COUNTERA_CONTROL);
+
+#if 0
+       if (mode & 0xff00)      /* load control word only */
+               return;
+#endif
+       outb(value & 0xff, dev->iobase+COUNTERA_0_DATA);
+       outb((value>>8)&0xff, dev->iobase+COUNTERA_0_DATA);
+}
+
+
+/***************************************************************************
+ *
+ * Turn off pacer timer chip.
+ *
+ ***************************************************************************/
+
+static void stop_pacer(comedi_device *dev)
+{
+       unsigned int mask;
+
+       mask = C2 | MODE2 | LSBFIRST;
+       outb(mask, dev->iobase + COUNTERA_CONTROL);
+       mask = C1 | MODE2 | LSBFIRST;
+       outb(mask, dev->iobase + COUNTERA_CONTROL);
+}
+
+/***************************************************************************
+ *
+ * Handles 
+ *    o pacer/counter triggered read() with software start.
+ *    o pacer/counter triggered read() with external start.
+ *    o external triggered read().
+ *
+ * Pacer Method: 
+ *
+ *    o Runs in CIO-DAS 16/330 Enhanced Mode
+ *    o Pacer counter controls frequency of sample conversions
+ *    o Total counter controls number of samples taken
+ *    o Interrupts occur every 512(PACKETSIZE) samples and at last sample
+ *    o Get chan->count samples
+ *    o Samples are saved in kernel buffer(kernBuff) in readInterrupt()
+ *
+ ***************************************************************************/
+
+#if 0
+static int EnhancedRead(WORD * kernBuff, ChanRec * chan)
+{
+       BYTE bReg = 0x0;
+       WORD wReg = 0x0;
+
+#ifdef DEBUG
+       printk("Entering EnchacedRead().\n");
+#endif
+
+       cli();
+
+       /* Set interrupt level on board, disable interrupts, and pacer  */
+
+       outb_p(BoardData.ir, CNTRL_REG);        /* & clear INTE bit in control reg */
+
+       /*
+          There is some confusion exactly what is going on, but if bit 5 in reg+11
+          is equal to 1, the FIFO is over run and we can not write to reg+2,
+          or what I call MUX_SCAN_LIMITS.  So I will try to clear bit 5
+          and keep reading until the FIFO is not full.  This should not
+          happen, but Robert Wilhelm (robert@haegar.physiol.med.tu-muenchen.de)
+          claims that it is.
+        */
+
+       /* clear Overrun status bit (bit 5 in reg+11) */
+       bReg = (BYTE) inb_p(MISC_REG) & (~MISC_OVERRUN);
+       outb_p(bReg, MISC_REG);
+
+       do {
+               bReg = (BYTE) inb_p(MUX_SCAN_LIMITS);   /* Read/Write clears FIFO */
+               outb_p(bReg, MUX_SCAN_LIMITS);
+       }
+       while (((BYTE) inb_p(MISC_REG) & MISC_OVERRUN));
+
+       bReg = chan->gain | ENHANCED_BIT;       /* Set gain and enhanced mode */
+       outb(bReg, MISC_REG);
+       bReg = (BYTE) inb_p(MISC_REG);  /* check to make sure we are in */
+       if (!(bReg & ENHANCED_BIT)) {   /* enhanced mode                */
+               return (NO_ENHANCED_MODE);
+       }
+       do {
+               bReg = (BYTE) inb_p(MUX_SCAN_LIMITS);   /* Read/Write clears FIFO */
+               outb_p(bReg, MUX_SCAN_LIMITS);
+       }
+       while (((BYTE) inb_p(MISC_REG) & MISC_OVERRUN));
+
+       if (DT_CONNECT) {
+               bReg = (BYTE) inb_p(MISC_REG);
+               bReg |= MISC_DT;
+               outb_p(bReg, MISC_REG);
+       }
+       /* load the Total Counter  for the DAS 16/330 */
+
+       bReg = C0 + MODE0 + LSBFIRST;   /* CounterB -> mode 0, DON'T load it */
+       outb_p(bReg, COUNTERB_CONTROL);         /* Sets output HIGH? */
+
+       bReg = (BYTE) inb_p(CNTRL_REG);         /* S1=0 and S0=0 from above */
+       if (chan->mode == ADC_PACER_CLOCK || chan->mode == ADC_PACER_EXTERN_TRIG) {
+               bReg |= (0x3);  /* Set S1=1 and S0=1 Internal Pacer */
+       } else {
+               bReg |= (0x2);  /* Set S1=1 and S0=0 External Pacer (A/D Triggering) */
+       }
+       outb_p(bReg, CNTRL_REG);
+
+       wReg = (WORD) (chan->count >> 16);      /* get upper word of count */
+       if (chan->count & 0xffff)
+               wReg++;         /* increment if lower count is ! zero */
+
+       bReg = C0 + MODE0 + LSBFIRST;   /* Counter -> mode 0, DON'T load it */
+       outb_p(bReg, COUNTERB_CONTROL);         /* Sets output HIGH? */
+       bReg = (BYTE) (wReg & 0xff);    /* load the upper word */
+       outb_p(bReg, COUNTERB_0_DATA);
+       bReg = (BYTE) (wReg >> 8);
+       outb_p(bReg, COUNTERB_0_DATA);
+
+       /* Set the trigger counters. Counter 0 is changed to counter 1 */
+
+       bReg = C1 + MODE2 + LSBFIRST;   /* Ensure clock starts low */
+       outb_p(bReg, COUNTERB_CONTROL);
+
+       bReg = C1 + MODE0 + LSBFIRST;   /* Make C0 clock high */
+       outb_p(bReg, COUNTERB_CONTROL);
+
+       bReg = C1 + MODE2 + LSBFIRST;   /* Make C0 clock low */
+       outb_p(bReg, COUNTERB_CONTROL);         /* complete 1st clock cycle */
+
+       wReg = (WORD) (chan->count & 0xffff);
+
+       if (wReg == 1) {
+               wReg++;         /* wReg can not be 1 */
+               bReg = (BYTE) (wReg & 0xff);    /* load the lower word */
+               outb_p(bReg, COUNTERB_1_DATA);
+               bReg = (BYTE) (wReg >> 8);
+               outb_p(bReg, COUNTERB_1_DATA);
+               outb_p(0x1, base_reg);  /* Force conversion to get count = 1 */
+       } else {
+               bReg = (BYTE) (wReg & 0xff);    /* load the lower word */
+               outb_p(bReg, COUNTERB_1_DATA);
+               bReg = (BYTE) (wReg >> 8);
+               outb_p(bReg, COUNTERB_1_DATA);
+       }
+       outb_p(0x1, base_reg);  /* Force a conversion */
+       outw_p(0x0, COUNTERB_1_DATA);   /* rollover mode ?? */
+
+       if (chan->mode == ADC_PACER_CLOCK || chan->mode == ADC_PACER_EXTERN_TRIG) {
+               LoadPacer(&BoardData);  /* Establish sample frequency */
+       }
+       do {
+               bReg = (BYTE) inb_p(MUX_SCAN_LIMITS);   /* Read/Write clears FIFO */
+               outb_p(bReg, MUX_SCAN_LIMITS);
+       }
+       while (((BYTE) inb_p(MISC_REG) & MISC_OVERRUN));
+
+       outb_p(0x00, STATUS_REG);       /* Clear INT bit, allow Interrupt */
+
+       /* Prepare global data for parameterless interrupt handler */
+
+       CurrChan = chan;        /* pass chanel number to global */
+       KernBuffPtr = KernBuff; /* same with KernBuff           */
+       WordsToRead = chan->count;
+
+       bReg = (BYTE) inb_p(CNTRL_REG);         /* Enable interrupts */
+       bReg |= CNTL_INTE;
+       outb_p(bReg, CNTRL_REG);
+
+       if (chan->mode == ADC_PACER_CLOCK || chan->mode == ADC_PACER_EXTERN_TRIG) {
+               outb_p(BoardData.pacerReg, PACER_CLOCK_REG);    /* Start pacer begins sampling. */
+       }
+       if (chan->adc_dio_pretrig) {
+               /* enable interrupts before setting dio bits */
+               sti();
+               outb_p(chan->dio_value, DIO_REG);
+       }
+       interruptible_sleep_on(&adc0_wait);     /* Block in wait state */
+
+       /* turn off interrupts, and trigger control.  Leave pacer clock on if enabled */
+       outb_p(0x0, CNTRL_REG);
+
+       if (WordsToRead != 0) {
+               printk("Timing error in EnchacedRead: WordsToRead = %d\n", WordsToRead);
+               return -1;
+       }
+       outb_p(0x00, STATUS_REG);       /* Clear INT bit */
+       return 0;
+}
+#endif
diff --git a/comedi/drivers/das1600.c b/comedi/drivers/das1600.c
new file mode 100644 (file)
index 0000000..7a92a62
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+   module/das1600.c
+   hardware driver for Keithley Metrabyte DAS1600 and compatibles
+
+   COMEDI - Linux Control and Measurement Device Interface
+   Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
+
+   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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+#include <8255.h>
+
+
+#define DAS1600_BASE_SIZE 16
+#define DAS1600_DIO_SIZE 8
+
+#define DAS1600_ADC_Low         0x000  // ADC-Low register
+#define DAS1600_ADC_High        0x001  // ADC-High register
+#define DAS1600_Channel_Mux     0x002  // Channel MUX register
+#define DAS1600_Digital_4_Bit   0x003  // 4-bit digital in/out
+#define DAS1600_DA0_Low         0x004  // DA0-Low register
+#define DAS1600_DA0_High        0x005  // DA0-High register
+#define DAS1600_DA1_Low         0x006  // DA1-Low register
+#define DAS1600_DA1_High        0x007  // DA1-High register
+#define DAS1600_Status          0x008  // Status register
+#define DAS1600_Control         0x009  // DMA, interrupt and trigger control
+#define DAS1600_Pacer_Control   0x00a  // Pacer clock control
+#define DAS1600_Gain_Control    0x00b  // Gain control
+#define DAS1600_Counter_0       0x00c  // 8254 counter 0 data
+#define DAS1600_Counter_1       0x00d  // 8254 counter 1 data
+#define DAS1600_Counter_2       0x00e  // 8254 counter 2 data
+#define DAS1600_Counter_Control 0x00f  // 8254 counter control
+#define DAS1600_Port_A          0x400  // 8255 port A
+#define DAS1600_Port_B          0x401  // 8255 port B
+#define DAS1600_Port_C          0x402  // 8255 port C
+#define DAS1600_Control_8255    0x403  // 8255 control
+#define DAS1600_Convert_Disable 0x404  // Disable AD conversion
+#define DAS1600_Mode_Enable     0x405  // Enable DAS1600 mode
+#define DAS1600_Burst_Enable    0x406  // Enable burst mode
+#define DAS1600_Burst_Status    0x407  // Burst mode status
+
+
+static void das1600_release_resources(comedi_device * dev);
+
+/*
+--BEGIN-RANGE-DEFS--
+RANGE_das1601_ai_10_bipolar
+        -10     10
+        -1      1
+        -0.1    0.1
+        -0.01   0.01
+RANGE_das1601_ai_10_unipolar
+        0       10
+        0       1
+        0       0.1
+        0       0.01
+RANGE_das1602_ai_10_bipolar
+        -10     10
+        -5      5
+        -2.5    2.5
+        -1.25   1.25
+RANGE_das1602_ai_10_unipolar
+        0       10
+        0       5
+        0       2.5
+        0       1.25
+RANGE_das1600_ao_extern_bipolar
+       -1      1       ext
+RANGE_das1600_ao_extern_unipolar
+       0       1       ext
+---END-RANGE-DEFS---
+*/
+
+static int das1600_attach(comedi_device *dev,comedi_devconfig *it);
+static int das1600_detach(comedi_device *dev);
+static int das1600_recognize(char *name);
+comedi_driver driver_das1600={
+       driver_name:    "das1600",
+       module:         &__this_module,
+       attach:         das1600_attach,
+       detach:         das1600_detach,
+       recognize:      das1600_recognize,
+};
+
+
+typedef struct {
+       enum {
+               card_1601_12, card_1602_12, card_1602_16
+       } card;
+       int dma;
+       int crystal;
+       enum {
+               adc_diff, adc_singleended
+       } adc_mux;
+       enum {
+               adc_bipolar10, adc_unipolar10
+       } adc_range;
+       enum {
+               dac_bipolar10, dac_bipolar5, dac_bipolaruser,
+               dac_unipolar10, dac_unipolar5, dac_unipolaruser,
+       } dac_range[2];
+       int range_type_list[2];
+} das1600_private;
+
+#define devpriv ((das1600_private *)dev->private)
+
+static int range_types[] =
+{
+       RANGE_bipolar10,
+       RANGE_bipolar5,
+       RANGE_das1600_ao_extern_bipolar,
+       RANGE_unipolar10,
+       RANGE_unipolar5,
+       RANGE_das1600_ao_extern_unipolar
+};
+
+#define DAS1600_TIMEOUT 100
+
+
+static int das1600_ai(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+  int i;
+  for(i=0 ; i < it->n_chan ; i++) {
+    int t, gain, hi, lo, chan;
+
+       if (it->mode != 0)
+               return -EINVAL;
+
+    gain = CR_RANGE(it->chanlist[i]);
+    chan = CR_CHAN(it->chanlist[i]);
+
+       outb(gain, dev->iobase + DAS1600_Gain_Control);
+       outb(chan + (chan << 4), dev->iobase + DAS1600_Channel_Mux);
+       outb(0, dev->iobase + DAS1600_ADC_Low);
+    for (t = 0; t < DAS1600_TIMEOUT; t++) {
+               if ((inb(dev->iobase + DAS1600_Status) & 0x80) == 0) {
+                       break;
+               }
+       }
+    if (t == DAS1600_TIMEOUT) {
+               rt_printk("das1600: timeout\n");
+       }
+       lo = inb(dev->iobase + DAS1600_ADC_Low);
+       hi = inb(dev->iobase + DAS1600_ADC_High);
+
+       switch (devpriv->card) {
+       case card_1601_12:
+       case card_1602_12:
+       it->data[i] = (hi << 4) | (lo >> 4);
+               break;
+       case card_1602_16:
+       it->data[i] = (hi << 8) | lo;
+               break;
+       }
+  }
+  return i;
+}
+
+
+static int das1600_ao(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+  int i;
+  for(i=0 ; i < it->n_chan ; i++) {
+       int chan;
+       int data;
+
+               chan = CR_CHAN(it->chanlist[i]);
+               data = it->data[i];
+
+    outb((data & 0x00f) << 4, dev->iobase + ((chan) ? DAS1600_DA1_Low : DAS1600_DA0_Low));
+    outb((data & 0xff0) >> 4, dev->iobase + ((chan) ? DAS1600_DA1_High : DAS1600_DA0_High));
+       }
+       return i;
+}
+
+#if 1
+
+static int das1600_di(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       unsigned int bits;
+
+       bits = inb(dev->iobase + DAS1600_Digital_4_Bit);
+
+       di_unpack(bits,it);
+
+       return it->n_chan;
+}
+
+static int das1600_do(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       do_pack(&s->state,it);
+
+       /* the outputs are inverted... dumb... (Is this really true? can't
+          find it in my docs...) */
+       outb(s->state ^ 0xff, dev->iobase + DAS1600_Digital_4_Bit);
+
+       return it->n_chan;
+}
+
+#else
+
+static int das1600_di(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       int data;
+       int chan;
+       int i;
+
+       data= inb(dev->iobase + DAS1600_DI);
+       for(i=0;i<it->n_chan;i++){
+               chan=CR_CHAN(it->chanlist[i]);
+               it->data[i]=(i>>chan)&1;
+       }
+
+       return i;
+}
+
+static int das1600_do(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       int data;
+       int chan;
+       int mask;
+       int i;
+
+       data=devpriv->last_do;
+
+       for(i=0;i<it->n_chan;i++){
+               chan=CR_CHAN(it->chanlist[i]);
+               mask=1<<chan;
+               data &= ~mask;
+               if(it->data[i])
+                       data |=mask;
+       }
+       devpriv->last_do=data;
+
+       /* why are outputs inverted?  dumb... */
+       outb(data ^ 0xff, dev->iobase + DAS1600_DO);
+
+       return i;
+}
+
+#endif
+
+
+/*
+  options[0]   Board base address
+  options[1]   IRQ
+  options[2]   DMA level select configuration
+                 0 == no DMA
+                 1 == DMA level 1
+                 3 == DMA level 3
+  options[3]   Crystal select configuration
+                 0 == 10 MHz
+                 1 == 1 MHz
+                 10 == 10 MHz
+  options[4]   Input configuration
+                 0 == differential
+                 1 == single-ended
+  options[5]   Analog input range configuration
+                 0 == bipolar 10V  (-10V -- +10V)
+                 1 == unipolar 10V  (0V -- +10V)
+  options[6]   Analog output 0 range configuration
+                 0 == bipolar 10V  (-10V -- +10V)
+                 1 == bipolar 5V  (-5V -- +5V)
+                 2 == bipolar user supplied  (-xV -- +xV)
+                 3 == unipolar 10V  (0V -- +10V)
+                 4 == unipolar 5V  (0V -- +5V)
+                 5 == unipolar user supplied  (0V -- +xV)
+  options[7]   Analog output 1 range configuration
+                 0 == bipolar 10V  (-10V -- +10V)
+                 1 == bipolar 5V  (-5V -- +5V)
+                 2 == bipolar user supplied  (-xV -- +xV)
+                 3 == unipolar 10V  (0V -- +10V)
+                 4 == unipolar 5V  (0V -- +5V)
+                 5 == unipolar user supplied  (0V -- +xV)
+ */
+static int das1600_recognize(char *name)
+{
+       if (!strcmp("das1601/12", name)) return card_1601_12;
+       if (!strcmp("das1602/12", name)) return card_1602_12;
+       if (!strcmp("das1602/16", name)) return card_1602_16;
+
+       return -1;
+}
+
+static int das1600_attach(comedi_device * dev, comedi_devconfig * it)
+{
+       int result = 1;
+       int board = -1;
+       comedi_subdevice *s;
+
+       dev->iobase = it->options[0];
+
+       printk("comedi%d: das1600: 0x%04x ", dev->minor, dev->iobase);
+       if (check_region(dev->iobase, DAS1600_BASE_SIZE) < 0 ||
+           check_region(dev->iobase + 0x400, DAS1600_DIO_SIZE) < 0) {
+               printk("I/O port conflict\n");
+               return -EIO;
+       }
+       request_region(dev->iobase, DAS1600_BASE_SIZE, "das1600");
+       request_region(dev->iobase + 0x400, DAS1600_DIO_SIZE, "das1600");
+
+       if (board == card_1601_12) {
+               dev->board_name = "das1601/12";
+       } else if (board == card_1602_12) {
+               dev->board_name = "das1602/12";
+       } else if (board == card_1602_16) {
+               dev->board_name = "das1602/16";
+       }
+
+       dev->n_subdevices=5;
+
+       if((result=alloc_private(dev,sizeof(das1600_private)))<0)
+               return result;
+       if((result=alloc_subdevices(dev))<0)
+               return result;
+
+       devpriv->card = board;
+       devpriv->dma = it->options[2];
+       devpriv->crystal = it->options[3];
+       devpriv->adc_mux = (it->options[4] == 1) ? adc_singleended : adc_diff;
+       devpriv->adc_range = it->options[5];
+       devpriv->dac_range[0] = it->options[6];
+       devpriv->dac_range[1] = it->options[7];
+
+       s = dev->subdevices + 0;
+       /* ai subdevice */
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = (devpriv->adc_mux == adc_singleended) ? 16 : 8;
+       s->maxdata = (board == card_1602_16) ? 0xffff : 0xfff;
+       s->timer_type = 0;      /* XXX need timer */
+       s->trig[0] = das1600_ai;
+       switch (board) {
+       case card_1601_12:
+               switch (devpriv->adc_range) {
+               case adc_bipolar10:
+                       s->range_type = RANGE_das1601_ai_10_bipolar;
+                       break;
+               case adc_unipolar10:
+                       s->range_type = RANGE_das1601_ai_10_unipolar;
+                       break;
+               };
+               break;
+       case card_1602_12:
+       case card_1602_16:
+               switch (devpriv->adc_range) {
+               case adc_bipolar10:
+                       s->range_type = RANGE_das1602_ai_10_bipolar;
+                       break;
+               case adc_unipolar10:
+                       s->range_type = RANGE_das1602_ai_10_unipolar;
+                       break;
+               };
+               break;
+       }
+
+       s++;
+       /* ao subdevice */
+       s->type = COMEDI_SUBD_AO;
+       s->subdev_flags = SDF_WRITEABLE;
+       s->n_chan = 2;
+       s->maxdata = 0xfff;
+       s->trig[0] = das1600_ao;
+       s->range_type_list = devpriv->range_type_list;
+       devpriv->range_type_list[0] = range_types[devpriv->dac_range[0]];
+       devpriv->range_type_list[1] = range_types[devpriv->dac_range[1]];
+
+       s++;
+       /* di subdevice */
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->trig[0] = das1600_di;
+       s->n_chan = 4;
+       s->maxdata = 1;
+       s->range_type = RANGE_digital;
+
+       s++;
+       /* do subdevice */
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITEABLE;
+       s->trig[0] = das1600_do;
+       s->n_chan = 4;
+       s->maxdata = 1;
+       s->range_type = RANGE_digital;
+
+       s++;
+       /* 8255 subdevice */
+       subdev_8255_init(dev,s,NULL,(void *)(dev->iobase+DAS1600_Port_A));
+
+       printk("\n");
+       return 0;
+}
+
+static void das1600_release_resources(comedi_device * dev)
+{
+       if (dev->iobase) {
+               release_region(dev->iobase, DAS1600_BASE_SIZE);
+               release_region(dev->iobase + 0x400, DAS1600_DIO_SIZE);
+       }
+       if (dev->irq) {
+               free_irq(dev->irq, dev);
+       }
+}
+
+static int das1600_detach(comedi_device * dev)
+{
+       printk("comedi%d: das1600: remove\n", dev->minor);
+
+       das1600_release_resources(dev);
+
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_das1600);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_das1600);
+}
+#endif
diff --git a/comedi/drivers/das6402.c b/comedi/drivers/das6402.c
new file mode 100644 (file)
index 0000000..89081dd
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+   Some comments on the code..
+
+   - it shouldn't be necessary to use outb_p().
+
+   - ignoreirq creates a race condition.  It needs to be fixed.
+
+
+ */
+
+/*
+   An experimental driver for Computerboards' DAS6402 I/O card
+
+   Copyright (C) 1999 Oystein Svendsen <svendsen@pvv.org>
+
+   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 <linux/module.h>
+#include <comedi_module.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+
+#define DAS6402_SIZE 16
+
+#define N_WORDS 3000*64
+
+#define STOP    0
+#define START   1
+
+
+#define SCANL 0x3f00
+#define BYTE unsigned char
+#define WORD unsigned short
+
+/*----- register 8 ----*/
+#define CLRINT 0x01
+#define CLRXTR 0x02
+#define CLRXIN 0x04
+#define EXTEND 0x10
+#define ARMED 0x20   /* enable conting of post sample conv */
+#define POSTMODE 0x40
+#define MHZ 0x80    /* 10 MHz clock */
+/*---------------------*/
+
+/*----- register 9 ----*/
+#define IRQ (0x04 << 4)   /* these two are                         */
+#define IRQV 10           /*               dependent on each other */
+
+#define CONVSRC 0x03  /* trig src is Intarnal pacer */
+#define BURSTEN 0x04  /* enable burst */
+#define XINTE 0x08     /* use external int. trig */
+#define INTE 0x80      /* enable analog interrupts */
+/*---------------------*/
+
+/*----- register 10 ---*/
+#define TGEN 0x01   /* Use pin DI1 for externl trigging? */
+#define TGSEL 0x02  /* Use edge triggering */
+#define TGPOL 0x04  /* active edge is falling */
+#define PRETRIG 0x08 /* pretrig */
+/*---------------------*/
+
+/*----- register 11 ---*/
+#define EOB 0x0c
+#define FIFOHFULL 0x08
+#define GAIN 0x01
+#define FIFONEPTY 0x04
+#define MODE 0x10
+#define SEM 0x20
+#define BIP 0x40
+/*---------------------*/
+
+#define M0 0x00
+#define M2 0x04
+
+#define        C0 0x00
+#define        C1 0x40
+#define        C2 0x80
+#define        RWLH 0x30 
+
+static int das6402_attach(comedi_device *dev,comedi_devconfig *it);
+static int das6402_detach(comedi_device *dev);
+comedi_driver driver_das6402={
+       driver_name:    "das6402",
+       module:         &__this_module,
+       attach:         das6402_attach,
+       detach:         das6402_detach,
+};
+
+
+typedef struct{
+       int ai_bytes_to_read;
+
+       int das6402_ignoreirq;
+}das6402_private;
+#define devpriv ((das6402_private *)dev->private)
+
+
+static void das6402_ai_fifo_dregs(comedi_device *dev,comedi_subdevice *s);
+
+static void das6402_setcounter(comedi_device *dev)
+{
+       BYTE p;
+       unsigned short ctrlwrd;
+
+       /* set up counter0 first, mode 0 */    
+       p=M0|C0|RWLH;
+       outb_p(p,dev->iobase+15);
+       ctrlwrd=2000;
+       p=(BYTE) (0xff & ctrlwrd);
+       outb_p(p,dev->iobase+12);
+       p=(BYTE) (0xff & (ctrlwrd >> 8));
+       outb_p(p,dev->iobase+12);
+
+       /* set up counter1, mode 2 */
+       p=M2|C1|RWLH;
+       outb_p(p,dev->iobase+15);
+       ctrlwrd=10;
+       p=(BYTE) (0xff & ctrlwrd);
+       outb_p(p,dev->iobase+13);
+       p=(BYTE) (0xff & (ctrlwrd >> 8));
+       outb_p(p,dev->iobase+13);
+
+       /* set up counter1, mode 2 */
+       p=M2|C2|RWLH;
+       outb_p(p,dev->iobase+15);
+       ctrlwrd=1000;
+       p=(BYTE) (0xff & ctrlwrd);
+       outb_p(p,dev->iobase+14);
+       p=(BYTE) (0xff & (ctrlwrd >> 8));
+       outb_p(p,dev->iobase+14);
+}
+
+static void intr_handler(int irq,void *d,struct pt_regs *regs)
+{
+       comedi_device *dev=d;
+       comedi_subdevice *s=dev->subdevices;
+
+       if (devpriv->das6402_ignoreirq){
+               printk("das6402: BUG: spurious interrupt\n");
+               return;
+       }
+
+#ifdef DEBUG
+       printk("das6402: interrupt! das6402_irqcount=%i\n",devpriv->das6402_irqcount);
+       printk("das6402: iobase+2=%i\n",inw_p(dev->iobase+2));
+#endif
+
+       das6402_ai_fifo_dregs(dev,s);
+
+       if(s->buf_int_count >= devpriv->ai_bytes_to_read){
+               outw_p(SCANL,dev->iobase+2);  /* clears the fifo */
+               outb(0x07,dev->iobase+8);  /* clears all flip-flops */
+#ifdef DEBUG
+               printk("das6402: Got %i samples\n\n",devpriv->das6402_wordsread-diff);
+#endif
+               comedi_done(dev,dev->subdevices+0);
+       }
+
+       outb(0x01,dev->iobase+8);   /* clear only the interrupt flip-flop */
+
+       return;
+}
+
+#if 0
+static void das6402_ai_fifo_read(comedi_device *dev,sampl_t *data,int n)
+{
+       int i;
+
+       for(i=0;i<n;i++)data[i]=inw(dev->iobase);
+}
+#endif
+
+
+/*
+ *  I know that this looks like terribly complicated code for doing
+ *  such a simple task, but it is fast.
+ */
+static void das6402_ai_fifo_dregs(comedi_device *dev,comedi_subdevice *s)
+{
+       int n,i;
+       sampl_t *data;
+
+       data=((void *)s->cur_trig.data);
+       while(1){
+               n=(s->cur_trig.data_len-s->buf_int_ptr)/sizeof(sampl_t);
+               for(i=0;i<n;i++){
+                       if(!(inb(dev->iobase+8)&0x01))
+                               return;
+                       *data=inw(dev->iobase);
+                       data++;
+                       s->buf_int_ptr+=sizeof(sampl_t);
+                       s->buf_int_count+=sizeof(sampl_t);
+               }
+               s->buf_int_ptr=0;
+               data=s->cur_trig.data;
+               comedi_eobuf(dev,s);
+       }
+#if 0
+       if (n>1024) {
+               printk("das6402: Someting is very wrong with the card fifo!\n");
+               /*
+                *  Not really...  Additional samples might be placed into the
+                *  fifo after the interrupt, but before the last one is read.
+                */
+       }
+#endif
+}
+
+static int das6402_ai_cancel(comedi_device *dev,comedi_subdevice *s)
+{
+       /*
+        *  This function should reset the board from whatever condition it
+        *  is in (i.e., acquiring data), to a non-active state.
+        */
+
+       devpriv->das6402_ignoreirq=1;
+#ifdef DEBUG
+       printk("das6402: Stopping acquisition\n");
+#endif
+       devpriv->das6402_ignoreirq=1;
+       outb_p(0x02,dev->iobase+10);  /* disable external trigging */
+       outw_p(SCANL,dev->iobase+2);  /* resets the card fifo */
+       outb_p(0,dev->iobase+9); /* disables interrupts */
+
+       outw_p(SCANL,dev->iobase+2);    
+
+       return 0;
+}
+
+static int das6402_ai_mode2(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       devpriv->das6402_ignoreirq=1;
+
+#ifdef DEBUG
+       printk("das6402: Starting acquisition\n");
+#endif
+       outb_p(0x03,dev->iobase+10);  /* enable external trigging */
+       outw_p(SCANL,dev->iobase+2);  /* resets the card fifo */
+       outb_p(IRQ|CONVSRC|BURSTEN|INTE,dev->iobase+9);
+
+       devpriv->ai_bytes_to_read=it->n*sizeof(sampl_t);
+
+       /* um... ignoreirq is a nasty race condition */
+       devpriv->das6402_ignoreirq=0;
+
+       outw_p(SCANL,dev->iobase+2);    
+
+       return 0;
+}
+
+
+static int board_init(comedi_device *dev)
+{
+       BYTE b;
+
+       devpriv->das6402_ignoreirq=1;
+
+       outb(0x07,dev->iobase+8);
+       
+       /* register 11  */
+       outb_p(MODE,dev->iobase+11);
+       b=BIP|SEM|MODE|GAIN|FIFOHFULL;
+       outb_p(b, dev->iobase+11);
+       
+       /* register 8   */
+       outb_p(EXTEND,dev->iobase+8);
+       b=EXTEND|MHZ;
+       outb_p(b,dev->iobase+8);
+       b=       MHZ|CLRINT|CLRXTR|CLRXIN;
+       outb_p(b,dev->iobase+8);
+
+       /* register 9    */
+       b=IRQ|CONVSRC|BURSTEN|INTE;
+       outb_p(b,dev->iobase+9);
+
+       /* register 10   */
+       b=TGSEL|TGEN; 
+       outb_p(b,dev->iobase+10);
+
+       b=0x07;
+       outb_p(b,dev->iobase+8);
+
+       das6402_setcounter(dev);
+
+       outw_p(SCANL,dev->iobase+2);  /* reset card fifo */
+
+       devpriv->das6402_ignoreirq=0;
+
+        return 0;
+}
+
+static int das6402_detach(comedi_device *dev)
+{
+        if(dev->irq)free_irq(dev->irq,dev);
+       if(dev->iobase)release_region(dev->iobase,DAS6402_SIZE);
+
+       return 0;
+}
+
+static int das6402_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       int irq;
+       int iobase;
+       int ret;
+       comedi_subdevice *s;
+
+       dev->board_name="das6402";
+
+       iobase=it->options[0];
+       if(iobase==0)iobase=0x300;
+
+       printk("comedi%d: das6402: 0x%04x",dev->minor,iobase);
+
+       if(check_region(iobase,DAS6402_SIZE)<0){
+               printk(" I/O port conflict\n");
+               return -EIO;
+       }
+       dev->iobase=iobase;
+       request_region(dev->iobase,DAS6402_SIZE,"das6402");
+
+       /* should do a probe here */
+
+       irq=it->options[0];
+       printk(" ( irq = %d )", irq);
+       ret=request_irq(irq, intr_handler, 0, "das6402", dev);
+       if(ret<0){
+               printk("irq conflict\n");
+               return ret;
+       }
+       dev->irq=irq;
+
+       if((ret=alloc_private(dev,sizeof(das6402_private)))<0)
+               return ret;
+
+       dev->n_subdevices=1;
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+
+       /* ai subdevice */
+       s=dev->subdevices+0;
+       s->type=COMEDI_SUBD_AI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=8;
+       s->trig[2]=das6402_ai_mode2;
+       s->cancel=das6402_ai_cancel;
+       s->maxdata=(1<<12)-1;
+       s->len_chanlist=16;     /* ? */
+       s->range_type = RANGE_unknown;
+       s->timer_type = TIMER_nanosec;
+
+       board_init(dev);
+
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_das6402);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_das6402);
+}
+#endif
diff --git a/comedi/drivers/dt2801.c b/comedi/drivers/dt2801.c
new file mode 100644 (file)
index 0000000..5837ba1
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * Device Driver for DataTranslation DT2801
+ *
+ */
+
+#include <comedi_module.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+
+#define DT2801_TIMEOUT 1000
+
+
+/* Hardware Configuration */
+/* ====================== */
+
+#define DT2801_MAX_DMA_SIZE (64 * 1024)
+
+/* Ports */
+#define DT2801_IOSIZE 2
+
+
+/* define's & typedef's */
+/* ====================== */
+
+/* Commands */
+#define DT_C_RESET       0x0
+#define DT_C_CLEAR_ERR   0x1
+#define DT_C_READ_ERRREG 0x2
+#define DT_C_SET_CLOCK   0x3
+
+#define DT_C_TEST        0xb
+#define DT_C_STOP        0xf
+
+#define DT_C_SET_DIGIN   0x4
+#define DT_C_SET_DIGOUT  0x5
+#define DT_C_READ_DIG    0x6
+#define DT_C_WRITE_DIG   0x7
+
+#define DT_C_WRITE_DAIM  0x8
+#define DT_C_SET_DA      0x9
+#define DT_C_WRITE_DA    0xa
+
+#define DT_C_READ_ADIM   0xc
+#define DT_C_SET_AD      0xd
+#define DT_C_READ_AD     0xe
+
+/* Command modifiers (only used with read/write), EXTTRIG can be
+   used with some other commands.
+*/
+#define DT_MOD_DMA     (1<<4)
+#define DT_MOD_CONT    (1<<5)
+#define DT_MOD_EXTCLK  (1<<6)
+#define DT_MOD_EXTTRIG (1<<7)
+
+/* Bits in status register */
+#define DT_S_DATA_OUT_READY   (1<<0)
+#define DT_S_DATA_IN_FULL     (1<<1)
+#define DT_S_READY            (1<<2)
+#define DT_S_COMMAND          (1<<3)
+#define DT_S_COMPOSITE_ERROR  (1<<7)
+
+
+/* registers */
+#define DT2801_DATA            0
+#define DT2801_STATUS          1
+#define DT2801_CMD             1
+
+static int dt2801_attach(comedi_device *dev,comedi_devconfig *it);
+static int dt2801_detach(comedi_device *dev);
+comedi_driver driver_dt2801={
+       driver_name:    "dt2801",
+       module:         &__this_module,
+       attach:         dt2801_attach,
+       detach:         dt2801_detach,
+};
+
+
+typedef struct{
+       char *name;
+       int boardcode;
+       int adbits;
+       int adrangetype;
+       int dabits;
+} boardtype_t;
+
+/* Typeid's for the different boards of the DT2801-series
+   (taken from the test-software, that comes with the board)
+   */
+static boardtype_t boardtypes[] =
+{
+       {"dt2801",0x09,         12,RANGE_unknown,12},
+       {"dt2801-a",0x52,       12,RANGE_unknown,12},
+       {"dt2801/5716a",0x82,   16,RANGE_unknown,12},
+       {"dt2805",0x12,         12,RANGE_unknown,12},
+       {"dt2805/5716a",0x92,   16,RANGE_unknown,12},
+       {"dt2808",0x20,         12,RANGE_unknown,8},
+       {"dt2818",0xa2,         12,RANGE_unknown,12},
+       {"dt2809",0xb0,         12,RANGE_unknown,12},
+};
+#define n_boardtypes ((sizeof(boardtypes))/(sizeof(boardtypes[0])))
+
+
+typedef struct{
+       boardtype_t *board;
+       unsigned int dac_range_types[2];
+}dt2801_private;
+#define devpriv ((dt2801_private *)dev->private)
+#define boardtype (*devpriv->board)
+
+
+static int dt2801_ai_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it);
+static int dt2801_ao_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it);
+static int dt2801_dio(comedi_device *dev,comedi_subdevice *s,comedi_trig *it);
+
+/* These are the low-level routines:
+   writecommand: write a command to the board
+   writedata: write data byte
+   readdata: read data byte
+ */
+
+/* Only checks DataOutReady-flag, not the Ready-flag as it is done
+   in the examples of the manual. I don't see why this should be
+   necessary. */
+static int dt2801_readdata(comedi_device *dev, int* data)
+{
+       int stat = 0;
+       int timeout = DT2801_TIMEOUT;
+
+       do{
+               stat = inb_p(dev->iobase+DT2801_STATUS);
+               if (stat & DT_S_COMPOSITE_ERROR) {
+                               printk("dt2801: composite-error in dt2801_readdata()\n");
+                       return -EIO;
+               }
+               if(stat & DT_S_DATA_OUT_READY){
+                       *data = inb_p(dev->iobase+DT2801_DATA);
+                       return 0;
+               }
+               if(stat & DT_S_READY){
+                       printk("dt2801: no data to read in dt2801_readdata()\n");
+                       return -EIO;
+               }
+       }while(--timeout>0);
+       printk("dt2801: timeout in dt2801_readdata()\n");
+       return -ETIME;
+}
+
+static int dt2801_readdata2(comedi_device *dev, int *data)
+{
+       int lb, hb;
+       int ret;
+
+       ret=dt2801_readdata(dev, &lb);
+       if(ret<0)return ret;
+       ret=dt2801_readdata(dev, &hb);
+       if(ret<0)return ret;
+
+       *data = (hb<<8)+lb;
+       return 0;
+}
+
+static int dt2801_writedata(comedi_device *dev, unsigned int data)
+{
+       int stat = 0;
+       int timeout = DT2801_TIMEOUT;
+
+       do{
+               stat = inb_p(dev->iobase+DT2801_STATUS);
+
+               if (stat & DT_S_COMPOSITE_ERROR) {
+                       printk("dt2801: composite-error in dt2801_writedata()\n");
+                       return -EIO;
+               }
+               if (!(stat & DT_S_DATA_IN_FULL)) {
+                       outb_p(data & 0xff, dev->iobase+DT2801_DATA);
+                       return 0;
+               }
+               if(stat & DT_S_READY){
+                       printk("dt2801: ready flag set (bad!) in dt2801_writedata()\n");
+                       return -EIO;
+               }
+       }while(--timeout>0);
+
+       printk("dt2801: timeout in dt2801_writedata()\n");
+
+       return -ETIME;
+}
+
+static int dt2801_writedata2(comedi_device *dev, unsigned int data)
+{
+       int ret;
+
+       ret=dt2801_writedata(dev,  data & 0xff);
+       if(ret<0)return ret;
+       ret=dt2801_writedata(dev, (data >> 8) );
+       if(ret<0)return ret;
+
+       return 0;
+}
+
+static int dt2801_wait_for_ready(comedi_device *dev)
+{
+       int timeout = DT2801_TIMEOUT;
+       int stat;
+
+       printk("dt2801: dt2801_wait_for_ready()\n");
+       stat = inb_p(dev->iobase+DT2801_STATUS);
+       if(stat & DT_S_READY){
+               printk("dt2801: board immediately ready\n");
+               return 0;
+       }
+       do{
+               stat = inb_p(dev->iobase+DT2801_STATUS);
+
+               if (stat & DT_S_COMPOSITE_ERROR) {
+                       printk("dt2801: composite-error in dt2801_wait_for_ready()\n");
+                       return -EIO;
+               }
+               if(stat & DT_S_READY){
+                       printk("dt2801: waited %d cycles\n",DT2801_TIMEOUT-timeout);
+                       return 0;
+               }
+       }while(--timeout>0);
+
+       printk("dt2801: timeout in dt2801_wait_for_ready() status=0x%02x\n",stat);
+
+       return -ETIME;
+}
+
+static int dt2801_writecmd(comedi_device * dev, int command)
+{
+       int stat;
+
+       dt2801_wait_for_ready(dev);
+
+       stat = inb_p(dev->iobase+DT2801_STATUS);
+       if (stat & DT_S_COMPOSITE_ERROR) {
+               printk("dt2801: composite-error in dt2801_writecmd()\n");
+               return -EIO;
+       }
+       if (!(stat & DT_S_READY)) {
+               printk("dt2801: !ready in dt2801_writecmd(), ignoring\n");
+       }
+       outb_p(command, dev->iobase+DT2801_CMD);
+
+       return 0;
+}
+
+
+static int dt2801_reset(comedi_device *dev)
+{
+       int board_code=0;
+
+       printk("dt2801: resetting board...\n");
+
+       printk("dt2801: stop\n");
+       dt2801_writecmd(dev,DT_C_STOP);
+       printk("dt2801: reading dummy\n");
+       dt2801_readdata(dev,&board_code);
+
+       printk("dt2801: reset\n");
+       dt2801_writecmd(dev,DT_C_RESET);
+
+       printk("dt2801: reading code\n");
+       dt2801_readdata(dev,&board_code);
+
+       printk("dt2801: ok.  code=0x%02x\n",board_code);
+
+
+       return 0;
+}
+
+static int dac_range_lkup(int bip,int v10)
+{
+       if(bip){
+               if(v10)return RANGE_unipolar10;
+               return RANGE_unipolar5;
+       }else{
+               if(v10)return RANGE_bipolar10;
+               return RANGE_bipolar5;
+       }
+}
+
+
+/*
+   options:
+       [0] - i/o base
+       [1] - unused
+       [2] - a/d 0=differential, 1=single-ended
+       [3] - dac0 unipolar=0, bipolar=1
+       [4] - dac0 5 V reference =0, 10 V ref = 1
+       [5] - dac1 unipolar=0, bipolar=1
+       [6] - dac0 5 V reference =0, 10 V ref = 1
+*/
+static int dt2801_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       int iobase;
+       int board_code,type;
+       int ret=0;
+
+       iobase=it->options[0];
+       if(check_region(iobase,DT2801_IOSIZE)<0){
+               comedi_error(dev,"I/O port conflict");
+               return -EIO;
+       }
+       request_region(dev->iobase, DT2801_IOSIZE, "dt2801");
+       dev->iobase=iobase;
+
+       /* do some checking */
+
+       board_code=dt2801_reset(dev);
+
+       for(type=0;type<n_boardtypes;type++){
+               if(boardtypes[type].boardcode==board_code)
+                       goto havetype;
+       }
+       printk("dt2801: unrecognized board code=0x%02x, contact author\n",board_code);
+       type=0;
+
+havetype:
+       printk("dt2801: %s at port 0x%x\n",boardtypes[type].name,iobase);
+
+       dev->n_subdevices=4;
+
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+
+       if((ret=alloc_private(dev,sizeof(dt2801_private)))<0)
+               return ret;
+
+       devpriv->board=boardtypes+type;
+
+       s=dev->subdevices+0;
+       /* ai subdevice */
+       s->type=COMEDI_SUBD_AI;
+       s->subdev_flags=SDF_READABLE;
+       if(it->options[2])s->n_chan=16;
+       else s->n_chan=8;
+       s->maxdata=(1<<boardtype.adbits)-1;
+       s->range_type=boardtype.adrangetype;
+       s->trig[0]=dt2801_ai_mode0;
+
+       s++;
+       /* ao subdevice */
+       s->type=COMEDI_SUBD_AO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->n_chan=2;
+       s->maxdata=(1<<boardtype.dabits)-1;
+       s->range_type_list=devpriv->dac_range_types;
+       devpriv->dac_range_types[0]=dac_range_lkup(it->options[3],it->options[4]);
+       devpriv->dac_range_types[1]=dac_range_lkup(it->options[5],it->options[6]);
+       s->trig[0]=dt2801_ao_mode0;
+
+       s++;
+       /* 1st digital subdevice */
+       s->type=COMEDI_SUBD_DIO;
+       s->subdev_flags=SDF_READABLE|SDF_WRITEABLE;
+       s->n_chan=8;
+       s->maxdata=1;
+       s->range_type=RANGE_digital;
+       s->trig[0]=dt2801_dio;
+
+       s++;
+       /* 2nd digital subdevice */
+       s->type=COMEDI_SUBD_DIO;
+       s->subdev_flags=SDF_READABLE|SDF_WRITEABLE;
+       s->n_chan=8;
+       s->maxdata=1;
+       s->range_type=RANGE_digital;
+       s->trig[0]=dt2801_dio;
+
+       return 0;
+}
+
+static int dt2801_detach(comedi_device *dev)
+{
+       if(dev->iobase)
+               release_region(dev->iobase,DT2801_IOSIZE);
+
+       return 0;
+}
+
+static int dt2801_ai_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+        int data;
+        int stat;
+
+
+        stat = dt2801_writecmd(dev, DT_C_READ_ADIM);
+        dt2801_writedata(dev, CR_RANGE(it->chanlist[0]));
+        dt2801_writedata(dev, CR_CHAN(it->chanlist[0]));
+        stat = dt2801_readdata2(dev, &data);
+
+        if (stat != 0) {
+             printk("dt2801: stat = %x\n", stat);
+             return -EIO;
+        }
+
+       it->data[0]=data;
+
+        return 1;
+}
+
+static int dt2801_ao_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int chan=CR_CHAN(it->chanlist[0]);
+
+       dt2801_writecmd(dev, DT_C_WRITE_DAIM);
+       dt2801_writedata(dev, chan);
+       dt2801_writedata2(dev, it->data[0]);
+       
+       return 1;
+}
+
+static int dt2801_dio(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       unsigned int bits;
+       int which=0;
+       
+       if(s==dev->subdevices+4)which=1;
+       
+       if(it->flags & TRIG_CONFIG){
+               /* configure */
+               if(it->chanlist[it->n_chan-1]){
+                       s->io_bits=0xff;
+                       dt2801_writecmd(dev, DT_C_SET_DIGOUT);
+               }else{
+                       s->io_bits=0;
+                       dt2801_writecmd(dev, DT_C_SET_DIGIN);
+               }
+               dt2801_writedata(dev, which);
+       }else{
+               if(s->io_bits){
+                       do_pack(&s->state,it);
+                       dt2801_writecmd(dev, DT_C_WRITE_DIG);
+                       dt2801_writedata(dev, which);
+                       dt2801_writedata(dev, s->state);
+               }else{
+                       dt2801_writecmd(dev, DT_C_READ_DIG);
+                       dt2801_writedata(dev, which);
+                       dt2801_readdata(dev, &bits);
+                       di_unpack(bits,it);
+               }
+       }
+       return it->n_chan;
+}
+
+
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_dt2801);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_dt2801);
+}
+#endif
diff --git a/comedi/drivers/dt2811.c b/comedi/drivers/dt2811.c
new file mode 100644 (file)
index 0000000..be12eb8
--- /dev/null
@@ -0,0 +1,567 @@
+/*
+   module/dt2811.c
+   hardware driver for Data Translation DT2811
+
+   COMEDI - Linux Control and Measurement Device Interface
+   History:
+   Base Version  - David A. Schleef <ds@stm.lbl.gov>
+   December 1998 - Updated to work.  David does not have a DT2811
+   board any longer so this was suffering from bitrot.
+   Updated performed by ...
+
+   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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+static char *driver_name = "dt2811";
+
+/*
+--BEGIN-RANGE-DEFS--
+RANGE_dt2811_pgh_ai_5_unipolar
+        0         5
+        0         2.5
+        0         1.25
+        0         0.625
+RANGE_dt2811_pgh_ai_2_5_bipolar
+        -2.5      2.5
+        -1.25     1.25
+        -0.625    0.625
+        -0.3125   0.3125
+RANGE_dt2811_pgh_ai_5_bipolar
+        -5        5
+        -2.5      2.5
+        -1.25     1.25
+        -0.625    0.625
+RANGE_dt2811_pgl_ai_5_unipolar
+        0         5
+        0         0.5
+        0         0.05
+        0         0.01
+RANGE_dt2811_pgl_ai_2_5_bipolar
+        -2.5      2.5
+        -0.25     0.25
+        -0.025    0.025
+        -0.005    0.005
+RANGE_dt2811_pgl_ai_5_bipolar
+        -5        5
+        -0.5      0.5
+        -0.05     0.05
+        -0.01     0.01
+RANGE_dt2811_ao_5_bipolar
+        -5        5
+RANGE_dt2811_ao_2_5_bipolar
+        -2.5      2.5
+RANGE_dt2811_ao_5_uniipolar
+        0         5
+---END-RANGE-DEFS---
+*/
+
+/*
+
+   0x00    ADCSR R/W  A/D Control/Status Register
+   bit 7 - (R) 1 indicates A/D conversion done
+   reading ADDAT clears bit
+   (W) ignored
+   bit 6 - (R) 1 indicates A/D error
+   (W) ignored
+   bit 5 - (R) 1 indicates A/D busy, cleared at end
+   of conversion
+   (W) ignored
+   bit 4 - (R) 0
+   (W)
+   bit 3 - (R) 0
+   bit 2 - (R/W) 1 indicates interrupts enabled
+   bits 1,0 - (R/W) mode bits
+   00  single conversion on ADGCR load
+   01  continuous conversion, internal clock,
+   (clock enabled on ADGCR load)
+   10  continuous conversion, internal clock,
+   external trigger
+   11  continuous conversion, external clock,
+   external trigger
+
+   0x01    ADGCR R/W A/D Gain/Channel Register
+   bit 6,7 - (R/W) gain select
+   00  gain=1, both PGH, PGL models
+   01  gain=2 PGH, 10 PGL
+   10  gain=4 PGH, 100 PGL
+   11  gain=8 PGH, 500 PGL
+   bit 4,5 - reserved
+   bit 3-0 - (R/W) channel select
+   channel number from 0-15
+
+   0x02,0x03 (R) ADDAT A/D Data Register
+   (W) DADAT0 D/A Data Register 0
+   0x02 low byte
+   0x03 high byte
+
+   0x04,0x05 (W) DADAT0 D/A Data Register 1
+
+   0x06 (R) DIO0 Digital Input Port 0
+   (W) DIO1 Digital Output Port 1
+
+   0x07 TMRCTR (R/W) Timer/Counter Register
+   bits 6,7 - reserved
+   bits 5-3 - Timer frequency control (mantissa)
+   543  divisor  freqency (kHz)
+   000  1        600
+   001  10       60
+   010  2        300
+   011  3        200
+   100  4        150
+   101  5        120
+   110  6        100
+   111  12       50
+   bits 2-0 - Timer frequency control (exponent)
+   210  multiply divisor/divide frequency by
+   000  1
+   001  10
+   010  100
+   011  1000
+   100  10000
+   101  100000
+   110  1000000
+   111  10000000
+
+ */
+
+#define DT2811_SIZE 8
+
+#define DT2811_ADCSR 0
+#define DT2811_ADGCR 1
+#define DT2811_ADDATLO 2
+#define DT2811_ADDATHI 3
+#define DT2811_DADAT0LO 2
+#define DT2811_DADAT0HI 3
+#define DT2811_DADAT1LO 4
+#define DT2811_DADAT1HI 5
+#define DT2811_DIO 6
+#define DT2811_TMRCTR 7
+
+/*
+ * flags
+ */
+
+/* ADCSR */
+
+#define DT2811_ADDONE   0x80
+#define DT2811_ADERROR  0x40
+#define DT2811_ADBUSY   0x20
+#define DT2811_CLRERROR 0x10
+#define DT2811_INTENB   0x04
+#define DT2811_ADMODE   0x03
+
+static int dt2811_attach(comedi_device *dev,comedi_devconfig *it);
+static int dt2811_detach(comedi_device *dev);
+static int dt2811_recognize(char *name);
+comedi_driver driver_dt2811={
+       driver_name:    "dt2811",
+       module:         &__this_module,
+       attach:         dt2811_attach,
+       detach:         dt2811_detach,
+       recognize:      dt2811_recognize,
+};
+
+static int dt2811_ai(comedi_device * dev, comedi_subdevice * s, comedi_trig * it);
+#if 0
+static int dt2811_ai_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it);
+#endif
+static int dt2811_ao(comedi_device * dev, comedi_subdevice * s, comedi_trig * it);
+static int dt2811_di(comedi_device * dev, comedi_subdevice * s, comedi_trig * it);
+static int dt2811_do(comedi_device * dev, comedi_subdevice * s, comedi_trig * it);
+
+enum { card_2811_pgh, card_2811_pgl };
+typedef struct {
+       int ntrig;
+       int curadchan;
+        enum {
+         adc_singleended, adc_diff, adc_pseudo_diff
+        } adc_mux;
+        enum {
+         adc_bipolar_5, adc_bipolar_2_5, adc_unipolar_5
+        } adc_range;
+        enum {
+         dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
+       } dac_range[2];
+        int range_type_list[2];
+} dt2811_private;
+
+#define devpriv ((dt2811_private *)dev->private)
+
+static int adc_range_types[][2] =
+{
+  /*     dt2811-pgh                       dt2811-pgl */
+  { RANGE_dt2811_pgh_ai_5_bipolar,   RANGE_dt2811_pgl_ai_5_bipolar },
+  { RANGE_dt2811_pgh_ai_2_5_bipolar, RANGE_dt2811_pgl_ai_2_5_bipolar },
+  { RANGE_dt2811_pgh_ai_5_unipolar,  RANGE_dt2811_pgl_ai_5_unipolar }
+};
+static int dac_range_types[] =
+{
+  RANGE_dt2811_ao_5_bipolar,
+  RANGE_dt2811_ao_2_5_bipolar,
+  RANGE_dt2811_ao_5_uniipolar
+};
+
+#define DT2811_TIMEOUT 5
+
+static void dt2811_interrupt(int irq, void *d, struct pt_regs *regs)
+{
+       int lo, hi;
+       int data;
+       comedi_device *dev = d;
+
+       lo = inb(dev->iobase + DT2811_ADDATLO);
+       hi = inb(dev->iobase + DT2811_ADDATHI);
+
+       data = lo + (hi << 8);
+
+       if (!(--devpriv->ntrig)) {
+               /* XXX */
+               /* how to turn off acquisition */
+               comedi_done(dev, dev->subdevices + 0);
+       }
+}
+
+/*
+  options[0]   Board base address
+  options[1]   IRQ
+  options[2]   Input configuration
+                 0 == single-ended
+                 1 == differential
+                 2 == pseudo-differential
+  options[3]   Analog input range configuration
+                 0 == bipolar 5  (-5V -- +5V)
+                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
+                 2 == unipolar 5V  (0V -- +5V)
+  options[4]   Analog output 0 range configuration
+                 0 == bipolar 5  (-5V -- +5V)
+                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
+                 2 == unipolar 5V  (0V -- +5V)
+  options[5]   Analog output 1 range configuration
+                 0 == bipolar 5  (-5V -- +5V)
+                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
+                 2 == unipolar 5V  (0V -- +5V)
+*/
+static int dt2811_recognize(char *name)
+{
+       if(!strcmp("dt2811-pgh", name))return card_2811_pgh;
+       if(!strcmp("dt2811-pgl", name))return card_2811_pgl;
+
+       return -1;
+}
+
+static int dt2811_attach(comedi_device * dev, comedi_devconfig * it)
+{
+       int i, irq, irqs;
+       long flags;
+       int ret;
+       comedi_subdevice *s;
+        int board = -1;
+
+       dev->iobase = it->options[0];
+
+       printk("comedi%d: dt2811: base=0x%04x\n", dev->minor, dev->iobase);
+
+       if (check_region(dev->iobase, DT2811_SIZE) < 0) {
+               printk("I/O port conflict\n");
+               return -EIO;
+       }
+       request_region(dev->iobase, DT2811_SIZE, driver_name);
+       if (board == card_2811_pgh) {
+         dev->board_name = "dt2811-pgh";
+        } else if (board == card_2811_pgl) {
+         dev->board_name = "dt2811-pgl";
+        }
+       dev->iosize = DT2811_SIZE;
+
+#if 0
+       outb(0, dev->iobase + DT2811_ADCSR);
+       udelay(100);
+       i = inb(dev->iobase + DT2811_ADDATLO);
+       i = inb(dev->iobase + DT2811_ADDATHI);
+#endif
+
+#if 1
+       irq = it->options[1];
+       if (irq < 0) {
+               save_flags(flags);
+               sti();
+               irqs = probe_irq_on();
+
+               outb(DT2811_CLRERROR | DT2811_INTENB, dev->iobase + DT2811_ADCSR);
+               outb(0, dev->iobase + DT2811_ADGCR);
+
+               udelay(100);
+
+               irq = probe_irq_off(irqs);
+               restore_flags(flags);
+
+               /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */
+
+               if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) {
+                       printk("error probing irq (bad) \n");
+               }
+               dev->irq = 0;
+               if (irq > 0) {
+                       i = inb(dev->iobase + DT2811_ADDATLO);
+                       i = inb(dev->iobase + DT2811_ADDATHI);
+                       printk("(irq = %d)\n", irq);
+                       request_irq(irq, dt2811_interrupt, 0 * SA_INTERRUPT, driver_name, dev);
+                       dev->irq = irq;
+               } else if (irq == 0) {
+                       printk("(no irq)\n");
+               } else {
+                       printk("( multiple irq's -- this is bad! )\n");
+               }
+       }
+#endif
+
+       dev->n_subdevices = 4;
+       if ((ret = alloc_subdevices(dev)) < 0)
+               return ret;
+       if ((ret = alloc_private(dev, sizeof(dt2811_private))) < 0)
+               return ret;
+       switch (it->options[2]) {
+         case 0: devpriv->adc_mux = adc_singleended; break;
+         case 1: devpriv->adc_mux = adc_diff; break;
+         case 2: devpriv->adc_mux = adc_pseudo_diff; break;
+         default:devpriv->adc_mux = adc_singleended; break;
+       }
+       switch (it->options[3]) {
+         case 0: devpriv->adc_range = adc_bipolar_5; break;
+         case 1: devpriv->adc_range = adc_bipolar_2_5; break;
+         case 2: devpriv->adc_range = adc_unipolar_5; break;
+         default:devpriv->adc_range = adc_bipolar_5; break;
+       }
+       switch (it->options[4]) {
+         case 0: devpriv->dac_range[0] = dac_bipolar_5; break;
+         case 1: devpriv->dac_range[0] = dac_bipolar_2_5; break;
+         case 2: devpriv->dac_range[0] = dac_unipolar_5; break;
+         default:devpriv->dac_range[0] = dac_bipolar_5; break;
+       }
+       switch (it->options[5]) {
+         case 0: devpriv->dac_range[1] = dac_bipolar_5; break;
+         case 1: devpriv->dac_range[1] = dac_bipolar_2_5; break;
+         case 2: devpriv->dac_range[1] = dac_unipolar_5; break;
+         default:devpriv->dac_range[1] = dac_bipolar_5; break;
+       }
+
+       s = dev->subdevices + 0;
+       /* initialize the ADC subdevice */
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
+       s->trig[0] = dt2811_ai;
+       s->maxdata = 0xfff;
+       s->range_type = adc_range_types[devpriv->adc_range][board];
+
+       s = dev->subdevices + 1;
+       /* ao subdevice */
+       s->type = COMEDI_SUBD_AO;
+       s->subdev_flags = SDF_WRITEABLE;
+       s->n_chan = 2;
+       s->trig[0] = dt2811_ao;
+       s->maxdata = 0xfff;
+        s->range_type_list = devpriv->range_type_list;
+        devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
+        devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
+
+       s = dev->subdevices + 2;
+       /* di subdevice */
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = 8;
+       s->trig[0] = dt2811_di;
+       s->maxdata = 1;
+       s->range_type = RANGE_digital;
+
+       s = dev->subdevices + 3;
+       /* do subdevice */
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITEABLE;
+       s->n_chan = 8;
+       s->trig[0] = dt2811_do;
+       s->maxdata = 1;
+       s->state = 0;
+       s->range_type = RANGE_digital;
+
+       return 0;
+}
+
+
+static int dt2811_detach(comedi_device * dev)
+{
+       printk("comedi%d: dt2811: remove\n", dev->minor);
+
+       if (dev->irq) {
+               free_irq(dev->irq, dev);
+       }
+       release_region(dev->iobase, dev->iosize);
+
+       return 0;
+}
+
+
+static int dt2811_ai(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+  int i;
+  for(i=0 ; i < it->n_chan ; i++) {
+    int lo, hi;
+    int chan;
+
+    chan = CR_CHAN(it->chanlist[i]);
+
+    outb(chan, dev->iobase + DT2811_ADGCR);
+    while (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
+      udelay(25);
+    lo = inb(dev->iobase + DT2811_ADDATLO);
+    hi = inb(dev->iobase + DT2811_ADDATHI);
+
+    it->data[i] = lo + 0x100 * hi;
+  }
+  return i;
+}
+
+#if 0
+static int dt2811_ai(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+
+       switch (it->mode) {
+       case 0:
+               return dt2811_ai_mode0(dev, s, it);
+       case 1:
+#if defined(FROM_DT2814)
+               outb(it->chan | DT2814_ENB | (it->trigvar << 5), dev->iobase + DT2814_CSR);
+#endif
+       default:
+               return -EINVAL;
+       }
+}
+
+static int dt2811_ai_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       int lo, hi;
+       int chan;
+
+       chan = CR_CHAN(it->chanlist[0]);
+
+       outb(chan, dev->iobase + DT2811_ADGCR);
+       while (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
+               udelay(25);
+       lo = inb(dev->iobase + DT2811_ADDATLO);
+       hi = inb(dev->iobase + DT2811_ADDATHI);
+
+       it->data[0] = lo + 0x100 * hi;
+
+       return 0;
+}
+#endif
+
+
+#if 0
+int dt2811_adtrig(kdev_t minor, comedi_adtrig * adtrig)
+{
+       comedi_device *dev = comedi_devices + minor;
+
+       if (adtrig->n < 1)
+               return 0;
+       dev->curadchan = adtrig->chan;
+       switch (dev->i_admode) {
+       case COMEDI_MDEMAND:
+               dev->ntrig = adtrig->n - 1;
+               /*printk("dt2811: AD soft trigger\n"); */
+               /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); *//* not neccessary */
+               outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
+               do_gettimeofday(&trigtime);
+               break;
+       case COMEDI_MCONTS:
+               dev->ntrig = adtrig->n;
+               break;
+       }
+
+       return 0;
+}
+#endif
+
+static int dt2811_ao(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+  int i;
+  for(i=0 ; i < it->n_chan ; i++) {
+       int lo, hi;
+       int chan;
+
+    chan = CR_CHAN(it->chanlist[i]);
+
+    lo = (it->data[i] & 0xff);
+    hi = (it->data[i] >> 8) & 0x0f;
+
+       switch (chan) {
+       case 0:
+               outb(lo, dev->iobase + DT2811_DADAT0LO);
+               outb(hi, dev->iobase + DT2811_DADAT0HI);
+               break;
+       case 1:
+               outb(lo, dev->iobase + DT2811_DADAT1LO);
+               outb(hi, dev->iobase + DT2811_DADAT1HI);
+               break;
+       }
+  }
+  return i;
+}
+
+static int dt2811_di(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       unsigned int bits;
+       
+       bits=inb(dev->iobase + DT2811_DIO);
+
+       return di_unpack(bits,it);
+}
+
+static int dt2811_do(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       do_pack(&s->state,it);
+       
+       outb(s->state, dev->iobase + DT2811_DIO);
+
+       return it->n_chan;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_dt2811);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_dt2811);
+}
+#endif
diff --git a/comedi/drivers/dt2814.c b/comedi/drivers/dt2814.c
new file mode 100644 (file)
index 0000000..134de7e
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+    module/dt2814.c
+    hardware driver for Data Translation DT2814
+
+    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.
+
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+
+
+#define DT2814_SIZE 2
+
+#define DT2814_CSR 0
+#define DT2814_DATA 1
+
+/*
+ * flags
+ */
+
+#define DT2814_FINISH 0x80
+#define DT2814_ERR 0x40
+#define DT2814_BUSY 0x20
+#define DT2814_ENB 0x10
+#define DT2814_CHANMASK 0x0f
+
+static int dt2814_attach(comedi_device *dev,comedi_devconfig *it);
+static int dt2814_detach(comedi_device *dev);
+comedi_driver driver_dt2814={
+       driver_name:    "dt2814",
+       module:         &__this_module,
+       attach:         dt2814_attach,
+       detach:         dt2814_detach,
+};
+
+static void dt2814_interrupt(int irq,void *dev,struct pt_regs * regs);
+
+typedef struct{
+       int ntrig;
+       int curadchan;
+}dt2814_private;
+#define devpriv ((dt2814_private *)dev->private)
+
+
+#define DT2814_TIMEOUT 100
+
+static int dt2814_ai_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int i,hi,lo;
+       int chan;
+
+       chan=CR_CHAN(it->chanlist[0]);
+
+       outb(chan,dev->iobase+DT2814_CSR);
+       for(i=0;i<DT2814_TIMEOUT;i++){
+               if(inb(dev->iobase+DT2814_CSR)&DT2814_FINISH)
+                       break;
+       }
+       hi=inb(dev->iobase+DT2814_DATA);
+       lo=inb(dev->iobase+DT2814_DATA);
+
+       it->data[0]=(hi<<4)|(lo>>4);
+       
+       return 1;
+}
+
+static int dt2814_ai_mode1(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int chan;
+
+       chan=CR_CHAN(it->chanlist[0]);
+
+       devpriv->ntrig=it->n;
+       outb(chan|DT2814_ENB|(it->trigvar<<5),
+               dev->iobase+DT2814_CSR);
+       
+       return 0;
+}
+
+static int dt2814_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       int i,irq;
+       int ret;
+       comedi_subdevice *s;
+
+       dev->iobase=it->options[0];
+       printk("comedi%d: dt2814: 0x%04x ",dev->minor,dev->iobase);
+       if(check_region(dev->iobase,DT2814_SIZE)<0){
+               printk("I/O port conflict\n");
+               return -EIO;
+       }
+       request_region(dev->iobase,DT2814_SIZE,"dt2814");
+       dev->iobase=dev->iobase;
+       dev->iosize=DT2814_SIZE;
+
+       outb(0,dev->iobase+DT2814_CSR);
+       udelay(100);
+       if(inb(dev->iobase+DT2814_CSR)&DT2814_ERR){
+               printk("reset error (fatal)\n");
+               return -EIO;
+       }
+       i=inb(dev->iobase+DT2814_DATA);
+       i=inb(dev->iobase+DT2814_DATA);
+
+       irq=it->options[1];
+#if 0
+       if(irq<0){
+               save_flags(flags);
+               sti();
+               irqs=probe_irq_on();
+       
+               outb(0,dev->iobase+DT2814_CSR);
+
+               udelay(100);
+
+               irq=probe_irq_off(irqs);
+               restore_flags(flags);
+               if(inb(dev->iobase+DT2814_CSR)&DT2814_ERR){
+                       printk("error probing irq (bad) \n");
+               }
+
+               i=inb(dev->iobase+DT2814_DATA);
+               i=inb(dev->iobase+DT2814_DATA);
+       }
+#endif
+       dev->irq=0;
+       if(irq>0){
+               printk("( irq = %d )\n",irq);
+               request_irq(irq,dt2814_interrupt,0*SA_INTERRUPT,"dt2814",dev);
+               dev->irq=irq;
+       }else if(irq==0){
+               printk("(no irq)\n");
+       }else{
+               printk("(probe returned multiple irqs--bad)\n");
+       }
+       
+       dev->n_subdevices=1;
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+       if((ret=alloc_private(dev,sizeof(dt2814_private)))<0)
+               return ret;
+
+       s=dev->subdevices+0;
+       s->type=COMEDI_SUBD_AI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=16;                   /* XXX */
+       s->len_chanlist=1;
+       s->trig[0]=dt2814_ai_mode0;
+       s->trig[1]=dt2814_ai_mode1;
+       s->maxdata=0xfff;
+       s->range_type=RANGE_unknown;    /* XXX */
+       s->timer_type=0;                /* XXX */
+
+       return 0;
+}
+
+
+static int dt2814_detach(comedi_device *dev)
+{
+       printk("comedi%d: dt2814: remove\n",dev->minor);
+       
+       if(dev->irq){
+               free_irq(dev->irq,dev);
+       }
+       release_region(dev->iobase,dev->iosize);
+
+       return 0;
+}
+
+
+static void dt2814_interrupt(int irq,void *d,struct pt_regs * regs)
+{
+        int lo,hi;
+        comedi_device *dev=d;
+       int data;
+
+        hi=inb(dev->iobase+DT2814_DATA);
+        lo=inb(dev->iobase+DT2814_DATA);
+
+        data=(hi<<4)|(lo>>4);
+
+       if(!(--devpriv->ntrig)){
+               int flags,i;
+
+               outb(0,dev->iobase+DT2814_CSR);
+               /* note: turning off timed mode triggers another
+                       sample. */
+
+               save_flags(flags);
+               cli();  /* FIXME */
+               for(i=0;i<DT2814_TIMEOUT;i++){
+                       if(inb(dev->iobase+DT2814_CSR)&DT2814_FINISH)
+                               break;
+               }
+               inb(dev->iobase+DT2814_DATA);
+               inb(dev->iobase+DT2814_DATA);
+               restore_flags(flags);
+       
+               comedi_done(dev,dev->subdevices);
+       }
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_dt2814);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_dt2814);
+}
+#endif
diff --git a/comedi/drivers/dt2815.c b/comedi/drivers/dt2815.c
new file mode 100644 (file)
index 0000000..39f7244
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+   module/dt2815.c
+   hardware driver for Data Translation DT2815
+
+   COMEDI - Linux Control and Measurement Device Interface
+   Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
+
+   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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+
+/*
+--BEGIN-RANGE-DEFS--
+RANGE_dt2815_ao_32_current
+        0         32       mA
+RANGE_dt2815_ao_20_current
+        4         20       mA
+---END-RANGE-DEFS---
+*/
+
+#define RANGE_dt2815_ao_5_unipolar     RANGE_unipolar5
+#define RANGE_dt2815_ao_5_bipolar      RANGE_bipolar5
+
+#define DT2815_SIZE 2
+
+#define DT2815_DATA 0
+#define DT2815_STATUS 1
+
+static int dt2815_attach(comedi_device *dev,comedi_devconfig *it);
+static int dt2815_detach(comedi_device *dev);
+comedi_driver driver_dt2815={
+       driver_name:    "dt2815",
+       module:         &__this_module,
+       attach:         dt2815_attach,
+       detach:         dt2815_detach,
+};
+
+static void dt2815_free_resources(comedi_device * dev);
+
+typedef struct {
+  unsigned int range_type_list[8];
+} dt2815_private;
+
+#define devpriv ((dt2815_private *)dev->private)
+
+static int dt2815_ao(comedi_device * dev, comedi_subdevice *s, comedi_trig * it)
+{
+  int i;
+  for(i=0 ; i < it->n_chan ; i++) {
+    int t;
+    int chan = CR_CHAN(it->chanlist[i]);
+    int data = it->data[i];
+    unsigned int status;
+    unsigned int lo, hi;
+
+    lo = ((data & 0x0f) << 4) | (chan << 1) | 0x01;
+    hi = (data & 0xff0) >> 4;
+    for (t = 0 ; t < 30 ; t++) {
+      status = inb(dev->iobase + DT2815_STATUS);
+      if (status == 0x00) {
+       outb(lo, dev->iobase + DT2815_DATA);
+       break;
+      }
+      udelay(10);
+    }
+    if (status != 0x00) {
+      rt_printk("dt2815: failed to write low byte on %d reason %x, %d\n",
+            chan, status, t);
+    }
+    for (t = 0 ; t < 30 ; t++) {
+      status = inb(dev->iobase + DT2815_STATUS);
+      if (status == 0x10) {
+       outb(hi, dev->iobase + DT2815_DATA);
+       break;
+      }
+      udelay(10);
+    }
+    if (status != 0x10) {
+      rt_printk("dt2815: failed to write high byte on %d reason %x, %d\n",
+            chan, status, t);
+    }
+  }
+  return i;
+}
+
+/*
+  options[0]   Board base address
+  options[1]   IRQ (not applicable)
+  options[2]   Voltage unipolar/bipolar configuration
+                 0 == unipolar 5V  (0V -- +5V)
+                1 == bipolar 5V  (-5V -- +5V)
+  options[3]   Current offset configuration
+                 0 == disabled  (0mA -- +32mAV)
+                 1 == enabled  (+4mA -- +20mAV)
+  options[4]   Firmware program configuration
+                 0 == program 1 (see manual table 5-4)
+                 1 == program 2 (see manual table 5-4)
+                 2 == program 3 (see manual table 5-4)
+                 3 == program 4 (see manual table 5-4)
+  options[5]   Analog output 0 range configuration
+                 0 == voltage
+                 1 == current
+  options[6]   Analog output 1 range configuration
+  ...
+  options[12]   Analog output 7 range configuration
+                 0 == voltage
+                 1 == current
+ */
+
+static int dt2815_attach(comedi_device * dev, comedi_devconfig * it)
+{
+  comedi_subdevice *s;
+  int i;
+  unsigned int current_range_type,voltage_range_type;
+
+  dev->iobase = it->options[0];
+  printk("comedi%d: dt2815: 0x%04x ", dev->minor, dev->iobase);
+  if (check_region(dev->iobase, DT2815_SIZE) < 0) {
+    printk("I/O port conflict\n");
+    return -EIO;
+  }
+  request_region(dev->iobase, DT2815_SIZE, "dt2815");
+
+  dev->board_name = "dt2815";
+
+  dev->n_subdevices = 1;
+  if(alloc_subdevices(dev)<0)
+    return -ENOMEM;
+  if(alloc_private(dev,sizeof(dt2815_private))<0)
+    return -ENOMEM;
+
+  s=dev->subdevices;
+  /* ao subdevice */
+  s->type=COMEDI_SUBD_AO;
+  s->subdev_flags=SDF_WRITEABLE;
+  s->maxdata=0xfff;
+  s->n_chan=8;
+  s->trig[0] = dt2815_ao;
+  s->range_type_list=devpriv->range_type_list;
+
+  current_range_type = (it->options[3])
+             ? RANGE_dt2815_ao_20_current
+             : RANGE_dt2815_ao_32_current;
+  voltage_range_type = (it->options[2])
+             ? RANGE_dt2815_ao_5_bipolar
+             : RANGE_dt2815_ao_5_unipolar;
+  for (i = 0; i < 8; i++) {
+    devpriv->range_type_list[i] = (it->options[5+i])
+           ? current_range_type
+           : voltage_range_type;
+  }
+
+  /* Init the 2815 */
+  outb(0x00, dev->iobase + DT2815_STATUS);
+  for (i = 0 ; i < 100 ; i++) {
+    /* This is incredibly slow (approx 20 ms) */
+    unsigned int status;
+
+    udelay(1000);
+    status = inb(dev->iobase + DT2815_STATUS);
+    if (status == 4) {
+      unsigned int program;
+      program = (it->options[4] & 0x3) << 3 | 0x7;
+      outb(program, dev->iobase + DT2815_DATA);
+      printk(", program: 0x%x (@t=%d)\n", program, i);
+      break;
+    } else if (status != 0x00) {
+      printk("dt2815: unexpected status 0x%x (@t=%d)\n", status, i);
+      if (status & 0x60) {
+       outb(0x00, dev->iobase + DT2815_STATUS);
+      }
+    }
+  }
+
+
+  printk("\n");
+
+  return 0;
+}
+
+static void dt2815_free_resources(comedi_device * dev)
+{
+  if(dev->iobase)
+    release_region(dev->iobase, DT2815_SIZE);
+}
+
+static int dt2815_detach(comedi_device * dev)
+{
+  printk("comedi%d: dt2815: remove\n", dev->minor);
+
+  dt2815_free_resources(dev);
+
+  return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_dt2815);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_dt2815);
+}
+#endif
diff --git a/comedi/drivers/dt2817.c b/comedi/drivers/dt2817.c
new file mode 100644 (file)
index 0000000..3e3a6ec
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+    module/dt2817.c
+    hardware driver for Data Translation DT2817
+
+    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.
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+
+
+#define DT2817_SIZE 5
+
+#define DT2817_CR 0
+#define DT2817_DATA 1
+
+
+static int dt2817_attach(comedi_device *dev,comedi_devconfig *it);
+static int dt2817_detach(comedi_device *dev);
+comedi_driver driver_dt2817={
+       driver_name:    "dt2817",
+       module:         &__this_module,
+       attach:         dt2817_attach,
+       detach:         dt2817_detach,
+};
+
+
+static int dt2817_dio(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       if(it->flags & TRIG_CONFIG){
+               int mask,i;
+               int chan;
+               int oe=0;
+
+               for(i=0;i<it->n_chan;i++){
+                       chan=CR_CHAN(it->chanlist[i]);
+                       if(chan<8){
+                               mask=0xff;
+                       }else if(chan<16){
+                               mask=0xff00;
+                       }else if(chan<24){
+                               mask=0xff0000;
+                       }else mask=0xff000000;
+                       if(it->data[i])s->io_bits|=mask;
+                       else s->io_bits&=~mask;
+               }
+               if(s->io_bits&0x000000ff)oe|=0x1;
+               if(s->io_bits&0x0000ff00)oe|=0x2;
+               if(s->io_bits&0x00ff0000)oe|=0x4;
+               if(s->io_bits&0xff000000)oe|=0x8;
+
+               outb(oe,dev->iobase + DT2817_CR);
+       }else{
+               int data;
+
+               if(it->flags & TRIG_WRITE){
+                       do_pack(&s->state,it);
+                       if(s->io_bits&0x000000ff)
+                               outb(s->state&0xff, dev->iobase + DT2817_DATA+0);
+                       if(s->io_bits&0x0000ff00)
+                               outb((s->state>>8)&0xff, dev->iobase + DT2817_DATA+1);
+                       if(s->io_bits&0x00ff0000)
+                               outb((s->state>>16)&0xff, dev->iobase + DT2817_DATA+2);
+                       if(s->io_bits&0xff000000)
+                               outb((s->state>>24)&0xff, dev->iobase + DT2817_DATA+3);
+               }else{
+                       data = inb(dev->iobase + DT2817_DATA + 0);
+                       data |= (inb(dev->iobase + DT2817_DATA + 1)<<8);
+                       data |= (inb(dev->iobase + DT2817_DATA + 2)<<16);
+                       data |= (inb(dev->iobase + DT2817_DATA + 3)<<24);
+                       di_unpack(data,it);
+               }
+       }
+       
+       return it->n_chan;
+}
+
+static int dt2817_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       int ret;
+       comedi_subdevice *s;
+
+       dev->iobase=it->options[0];
+       printk("comedi%d: dt2817: 0x%04x ",dev->minor,dev->iobase);
+       if(check_region(dev->iobase,DT2817_SIZE)<0){
+               printk("I/O port conflict\n");
+               return -EIO;
+       }
+       request_region(dev->iobase,DT2817_SIZE,"dt2817");
+       dev->board_name="dt2817";
+       dev->iosize=DT2817_SIZE;
+
+       dev->n_subdevices=1;
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+
+       s=dev->subdevices+0;
+
+       s->n_chan=32;
+       s->type=COMEDI_SUBD_DIO;
+       s->subdev_flags=SDF_READABLE|SDF_WRITEABLE;
+       s->range_type=RANGE_digital;
+       s->maxdata=1;
+       s->trig[0]=dt2817_dio;
+
+       s->state=0;
+       outb(0,dev->iobase+DT2817_CR);
+
+       printk("\n");
+
+       return 0;
+}
+
+
+static int dt2817_detach(comedi_device *dev)
+{
+       printk("comedi%d: dt2817: remove\n",dev->minor);
+       
+       release_region(dev->iobase,dev->iosize);
+
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_dt2817);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_dt2817);
+}
+#endif
diff --git a/comedi/drivers/dt282x.c b/comedi/drivers/dt282x.c
new file mode 100644 (file)
index 0000000..7da8671
--- /dev/null
@@ -0,0 +1,1256 @@
+/*
+   modules/dt282x.c
+   hardware driver for Data Translation DT2821 series
+
+   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 <comedi_module.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+#define DEBUG
+
+#define DT2821_TIMEOUT         100     /* 500 us */
+#define DT2821_SIZE 0x10
+
+/*
+ *    Registers in the DT282x
+ */
+
+#define DT2821_ADCSR   0x00    /* A/D Control/Status             */
+#define DT2821_CHANCSR 0x02    /* Channel Control/Status */
+#define DT2821_ADDAT   0x04    /* A/D data                       */
+#define DT2821_DACSR   0x06    /* D/A Control/Status             */
+#define DT2821_DADAT   0x08    /* D/A data                       */
+#define DT2821_DIODAT  0x0a    /* digital data                   */
+#define DT2821_SUPCSR  0x0c    /* Supervisor Control/Status      */
+#define DT2821_TMRCTR  0x0e    /* Timer/Counter          */
+
+/*
+ *  At power up, some registers are in a well-known state.  The
+ *  masks and values are as follows:
+ */
+
+#define DT2821_ADCSR_MASK 0xfff0
+#define DT2821_ADCSR_VAL 0x7c00
+
+#define DT2821_CHANCSR_MASK 0xf0f0
+#define DT2821_CHANCSR_VAL 0x70f0
+
+#define DT2821_DACSR_MASK 0x7c93
+#define DT2821_DACSR_VAL 0x7c90
+
+#define DT2821_SUPCSR_MASK 0xf8ff
+#define DT2821_SUPCSR_VAL 0x0000
+
+#define DT2821_TMRCTR_MASK 0xff00
+#define DT2821_TMRCTR_VAL 0xf000
+
+/*
+ *    Bit fields of each register
+ */
+
+/* ADCSR */
+
+#define DT2821_ADERR   0x8000  /* (R)   1 for A/D error  */
+#define DT2821_ADCLK   0x0200  /* (R/W) A/D clock enable */
+               /*      0x7c00           read as 1's            */
+#define DT2821_MUXBUSY 0x0100  /* (R)   multiplexer busy */
+#define DT2821_ADDONE  0x0080  /* (R)   A/D done         */
+#define DT2821_IADDONE 0x0040  /* (R/W) interrupt on A/D done    */
+               /*      0x0030           gain select            */
+               /*      0x000f           channel select         */
+
+/* CHANCSR */
+
+#define DT2821_LLE     0x8000  /* (R/W) Load List Enable */
+               /*      0x7000           read as 1's            */
+               /*      0x0f00     (R)   present address        */
+               /*      0x00f0           read as 1's            */
+               /*      0x000f     (R)   number of entries - 1  */
+
+/* DACSR */
+
+#define DT2821_DAERR   0x8000  /* (R)   D/A error                */
+#define DT2821_YSEL    0x0200  /* (R/W) DAC 1 select             */
+#define DT2821_SSEL    0x0100  /* (R/W) single channel select    */
+#define DT2821_DACRDY  0x0080  /* (R)   DAC ready                */
+#define DT2821_IDARDY  0x0040  /* (R/W) interrupt on DAC ready   */
+#define DT2821_DACLK   0x0020  /* (R/W) D/A clock enable */
+#define DT2821_HBOE    0x0002  /* (R/W) DIO high byte output enable      */
+#define DT2821_LBOE    0x0001  /* (R/W) DIO low byte output enable       */
+
+/* SUPCSR */
+
+#define DT2821_DMAD    0x8000  /* (R)   DMA done                 */
+#define DT2821_ERRINTEN        0x4000  /* (R/W) interrupt on error               */
+#define DT2821_CLRDMADNE 0x2000        /* (W)   clear DMA done                   */
+#define DT2821_DDMA    0x1000  /* (R/W) dual DMA                 */
+#define DT2821_DS1     0x0800  /* (R/W) DMA select 1                     */
+#define DT2821_DS0     0x0400  /* (R/W) DMA select 0                     */
+#define DT2821_BUFFB   0x0200  /* (R/W) buffer B selected                */
+#define DT2821_SCDN    0x0100  /* (R)   scan done                        */
+#define DT2821_DACON   0x0080  /* (W)   DAC single conversion            */
+#define DT2821_ADCINIT 0x0040  /* (W)   A/D initialize                   */
+#define DT2821_DACINIT 0x0020  /* (W)   D/A initialize                   */
+#define DT2821_PRLD    0x0010  /* (W)   preload multiplexer              */
+#define DT2821_STRIG   0x0008  /* (W)   software trigger         */
+#define DT2821_XTRIG   0x0004  /* (R/W) external trigger enable  */
+#define DT2821_XCLK    0x0002  /* (R/W) external clock enable            */
+#define DT2821_BDINIT  0x0001  /* (W)   initialize board         */
+
+/*
+--BEGIN-RANGE-DEFS--
+RANGE_dt282x_ai_lo_bipolar
+        -10     10
+        -5      5
+        -2.5    2.5
+        -1.25   1.25
+RANGE_dt282x_ai_lo_unipolar
+        0       10
+        0       5
+        0       2.5
+        0       1.25
+RANGE_dt282x_ai_5_bipolar
+       -5      5
+       -2.5    2.5
+       -1.25   1.25
+       -0.625  0.625
+RANGE_dt282x_ai_5_unipolar
+        0       5
+        0       2.5
+        0       1.25
+        0       0.625
+RANGE_dt282x_ai_hi_bipolar
+        -10     10
+        -1      1
+        -0.1    0.1
+        -0.02   0.02
+RANGE_dt282x_ai_hi_unipolar
+        0       10
+        0       1
+        0       0.1
+        0       0.02
+RANGE_bipolar2_5
+       -2.5    2.5
+---END-RANGE-DEFS---
+*/
+
+typedef struct {
+       char *name;
+       int adbits;
+       int adchan_se;
+       int adchan_di;
+       int ispgl;
+       int dachan;
+       int dabits;
+} boardtype_t;
+
+static boardtype_t boardtypes[] =
+{
+       {       name:           "dt2821",
+               adbits:         12,
+               adchan_se:      16,
+               adchan_di:      8,
+               ispgl:          0,
+               dachan:         2,
+               dabits:         12,
+       },
+       {       name:           "dt2823",
+               adbits:         16,
+               adchan_se:      0,
+               adchan_di:      4,
+               ispgl:          0,
+               dachan:         2,
+               dabits:         16,
+       },
+       {       name:           "dt2824-pgh",
+               adbits:         16,
+               adchan_se:      16,
+               adchan_di:      8,
+               ispgl:          0,
+               dachan:         0,
+               dabits:         0,
+       },
+       {       name:           "dt2824-pgl",
+               adbits:         16,
+               adchan_se:      16,
+               adchan_di:      8,
+               ispgl:          1,
+               dachan:         0,
+               dabits:         0,
+       },
+       {       name:           "dt2825",
+               adbits:         12,
+               adchan_se:      16,
+               adchan_di:      8,
+               ispgl:          0,
+               dachan:         2,
+               dabits:         12,
+       },
+       {       name:           "dt2827",
+               adbits:         16,
+               adchan_se:      0,
+               adchan_di:      4,
+               ispgl:          0,
+               dachan:         2,
+               dabits:         12,
+       },
+       {       name:           "dt2828",
+               adbits:         12,
+               adchan_se:      4,
+               adchan_di:      0,
+               ispgl:          0,
+               dachan:         2,
+               dabits:         12,
+       },
+       {       name:           "dt21-ez",
+               adbits:         12,
+               adchan_se:      16,
+               adchan_di:      8,
+               ispgl:          0,
+               dachan:         2,
+               dabits:         12,
+       },
+       {       name:           "dt23-ez",
+               adbits:         16,
+               adchan_se:      16,
+               adchan_di:      8,
+               ispgl:          0,
+               dachan:         0,
+               dabits:         0,
+       },
+       {       name:           "dt24-ez",
+               adbits:         12,
+               adchan_se:      16,
+               adchan_di:      8,
+               ispgl:          0,
+               dachan:         0,
+               dabits:         0,
+       },
+       {       name:           "dt24-ez-pgl",
+               adbits:         12,
+               adchan_se:      16,
+               adchan_di:      8,
+               ispgl:          1,
+               dachan:         0,
+               dabits:         0,
+       },
+};
+static int n_boardtypes = sizeof(boardtypes)/sizeof(boardtype_t);
+
+
+typedef struct {
+       int ad_2scomp;          /* we have 2's comp jumper set  */
+       int da0_2scomp;         /* same, for DAC0               */
+       int da1_2scomp;         /* same, for DAC1               */
+
+       int darangelist[2];
+
+       int dacsr;              /* software copies of registers */
+       int adcsr;
+       int supcsr;
+
+       int ntrig;
+       int nread;
+
+       struct{
+               int chan;
+               short *buf;     /* DMA buffer */
+               int size;       /* size of current transfer */
+       }dma[2];
+       int dma_maxsize;        /* max size of DMA transfer     */
+       int usedma;             /* driver uses DMA              */
+       int current_dma_chan;
+       int dma_dir;
+} dt282x_private;
+
+#define devpriv ((dt282x_private *)dev->private)
+#define boardtype (*(boardtype_t *)dev->board_ptr)
+
+/*
+ *    Some useless abstractions
+ */
+#define chan_to_DAC(a) ((a)&1)
+#define update_dacsr(a)        outw(devpriv->dacsr|(a),dev->iobase+DT2821_DACSR)
+#define update_adcsr(a)        outw(devpriv->adcsr|(a),dev->iobase+DT2821_ADCSR)
+#define mux_busy() (inw(dev->iobase+DT2821_ADCSR)&DT2821_MUXBUSY)
+#define ad_done() (inw(dev->iobase+DT2821_ADCSR)&DT2821_ADDONE)
+#define update_supcsr(a)       outw(devpriv->supcsr|(a),dev->iobase+DT2821_SUPCSR)
+
+/*
+ *    danger! macro abuse... a is the expression to wait on, and b is
+ *      the statement(s) to execute if it doesn't happen.
+ */
+#define wait_for(a,b)                                  \
+       do{                                             \
+               int _i;                                 \
+               for(_i=0;_i<DT2821_TIMEOUT;_i++){       \
+                       if(a){_i=0;break;}              \
+                       udelay(5);                      \
+               }                                       \
+               if(_i){b}                               \
+       }while(0)
+
+static int dt282x_attach(comedi_device * dev, comedi_devconfig * it);
+static int dt282x_detach(comedi_device * dev);
+static int dt282x_recognize(char *name);
+comedi_driver driver_dt282x={
+       driver_name:    "dt282x",
+       module:         &__this_module,
+       attach:         dt282x_attach,
+       detach:         dt282x_detach,
+       recognize:      dt282x_recognize,
+};
+
+static void free_resources(comedi_device *dev);
+static int prep_ai_dma(comedi_device * dev,int chan,int size);
+static int prep_ao_dma(comedi_device * dev,int chan,int size);
+static int dt282x_ai_cancel(comedi_device * dev, comedi_subdevice * s);
+static int dt282x_ao_cancel(comedi_device * dev, comedi_subdevice * s);
+static int dt282x_ns_to_timer(int *nanosec);
+
+
+static int dt282x_grab_dma(comedi_device *dev,int dma1,int dma2);
+
+static void dt282x_cleanup_buffer(comedi_device *dev,unsigned short *buf,unsigned int len)
+{
+       unsigned int i;
+       unsigned short mask=(1<<boardtype.adbits)-1;
+       unsigned short sign=1<<(boardtype.adbits-1);
+
+       if(devpriv->ad_2scomp){
+               for(i=0;i<len;i++){
+                       buf[i]&=mask;
+                       buf[i]^=sign;
+               }
+       }else{
+               for(i=0;i<len;i++){
+                       buf[i]&=mask;
+               }
+       }
+}
+
+static void copy_to_buf(comedi_device *dev,comedi_subdevice *s,void *buf,unsigned int n_bytes)
+{
+       unsigned int n;
+
+       n=n_bytes;
+       if(s->buf_int_ptr+n >= s->cur_trig.data_len){
+               n=s->cur_trig.data_len-s->buf_int_ptr;
+               memcpy(((void *)(s->cur_trig.data))+s->buf_int_ptr,buf,n);
+               buf+=n;
+               s->buf_int_count+=n;
+               s->buf_int_ptr=0;
+
+               n=n_bytes-n;
+       }
+       memcpy(((void *)(s->cur_trig.data))+s->buf_int_ptr,buf,n);
+       buf+=n;
+       s->buf_int_count+=n;
+       s->buf_int_ptr+=n;
+}
+
+static int copy_from_buf(comedi_device *dev,comedi_subdevice *s,void *buf,unsigned int n_bytes)
+{
+       unsigned int n,m;
+
+       n=s->buf_int_count-s->buf_user_count;
+       if(n==0)return 0;
+       if(n>n_bytes)
+               n=n_bytes;
+
+       n_bytes=n;
+       if(s->buf_int_ptr+n >= s->cur_trig.data_len){
+               m=s->cur_trig.data_len-s->buf_int_ptr;
+               memcpy(buf,((void *)(s->cur_trig.data))+s->buf_int_ptr,m);
+               buf+=m;
+               s->buf_int_count+=m;
+               s->buf_int_ptr=0;
+
+               n-=m;
+       }
+       memcpy(buf,((void *)(s->cur_trig.data))+s->buf_int_ptr,n);
+       s->buf_int_count+=n;
+       s->buf_int_ptr+=n;
+
+       return n_bytes;
+}
+
+
+static void dt282x_ao_dma_interrupt(comedi_device * dev)
+{
+       void *ptr;
+       int size;
+       int i;
+       comedi_subdevice *s=dev->subdevices+1;
+
+       update_supcsr(DT2821_CLRDMADNE);
+
+       if(!s->cur_trig.data){
+               printk("cur_trig.data disappeared.  dang!\n");
+               return;
+       }
+
+       i=devpriv->current_dma_chan;
+       ptr=devpriv->dma[i].buf;
+
+       disable_dma(devpriv->dma[i].chan);
+
+       devpriv->current_dma_chan=1-i;
+
+       size=copy_from_buf(dev,s,ptr,devpriv->dma_maxsize*2);
+       if(!size){
+               printk("dt282x: AO underrun\n");
+               dt282x_ao_cancel(dev,s);
+               comedi_done(dev,s);
+               return;
+       }
+       prep_ao_dma(dev,i,size/2);
+
+       enable_dma(devpriv->dma[i].chan);
+
+       comedi_bufcheck(dev,s);
+       return;
+}
+
+static void dt282x_ai_dma_interrupt(comedi_device * dev)
+{
+       void *ptr;
+       int size;
+       int i;
+       comedi_subdevice *s=dev->subdevices;
+
+       update_supcsr(DT2821_CLRDMADNE);
+
+       if(!s->cur_trig.data){
+               printk("cur_trig.data disappeared.  dang!\n");
+               return;
+       }
+
+       i=devpriv->current_dma_chan;
+       ptr=devpriv->dma[i].buf;
+       size=devpriv->dma[i].size;
+
+       disable_dma(devpriv->dma[i].chan);
+
+       devpriv->current_dma_chan=1-i;
+       dt282x_cleanup_buffer(dev,ptr,size);
+       copy_to_buf(dev,s,ptr,size*2);
+       devpriv->nread-=size;
+
+       if(devpriv->nread<0){
+               printk("dt282x: off by one\n");
+               devpriv->nread=0;
+       }
+       if (!devpriv->nread) {
+               devpriv->adcsr=0;
+               update_adcsr(0);
+
+               /* this eliminates "extra sample" issues */
+               devpriv->supcsr = 0;
+               update_supcsr(DT2821_ADCINIT);
+
+               comedi_done(dev,s);
+
+               return;
+       }else{
+               comedi_bufcheck(dev,s);
+       }
+
+#if 1
+       /* clear the dual dma flag, making this the last dma segment */
+       /* XXX probably wrong */
+       if(!devpriv->ntrig){
+               devpriv->supcsr &= ~(DT2821_DDMA);
+               update_supcsr(0);
+       }
+#endif
+       /* restart the channel */
+       prep_ai_dma(dev,i,0);
+
+       enable_dma(devpriv->dma[i].chan);
+
+       return;
+}
+
+static int prep_ai_dma(comedi_device * dev,int chan,int n)
+{
+       int dma_chan;
+       unsigned long dma_ptr;
+       unsigned long flags;
+
+       if(!devpriv->ntrig)
+               return 0;
+
+       if(n==0)
+               n = devpriv->dma_maxsize;
+       if (n >= devpriv->ntrig)
+               n = devpriv->ntrig;
+       devpriv->ntrig -= n;
+
+       devpriv->dma[chan].size = n;
+       dma_chan = devpriv->dma[chan].chan;
+       dma_ptr = virt_to_bus(devpriv->dma[chan].buf);
+
+       set_dma_mode(dma_chan, DMA_MODE_READ);
+       flags=claim_dma_lock();
+       set_dma_addr(dma_chan, dma_ptr);
+       set_dma_count(dma_chan, n << 1);
+       release_dma_lock(flags);
+
+       return n;
+}
+
+static int prep_ao_dma(comedi_device * dev,int chan,int n)
+{
+       int dma_chan;
+       unsigned long dma_ptr;
+       unsigned long flags;
+
+       devpriv->dma[chan].size = n;
+       dma_chan = devpriv->dma[chan].chan;
+       dma_ptr = virt_to_bus(devpriv->dma[chan].buf);
+
+       set_dma_mode(dma_chan, DMA_MODE_WRITE);
+       flags=claim_dma_lock();
+       set_dma_addr(dma_chan, dma_ptr);
+       set_dma_count(dma_chan, n*2 );
+       release_dma_lock(flags);
+
+       return n;
+}
+
+static void dt282x_interrupt(int irq, void *d, struct pt_regs *regs)
+{
+       comedi_device *dev = d;
+       comedi_subdevice *s = dev->subdevices+0;
+       unsigned int supcsr, adcsr, dacsr;
+       sampl_t data;
+
+       adcsr=inw(dev->iobase + DT2821_ADCSR);
+       if (adcsr & DT2821_ADERR) {
+               comedi_error(dev, "A/D error");
+               dt282x_ai_cancel(dev,s);
+               comedi_done(dev,s);
+               return;
+       }
+       supcsr = inw(dev->iobase + DT2821_SUPCSR);
+       /*printk("supcsr=%02x\n",supcsr);*/
+       if (supcsr & DT2821_DMAD) {
+               if(devpriv->dma_dir==DMA_MODE_READ)
+                       dt282x_ai_dma_interrupt(dev);
+               else
+                       dt282x_ao_dma_interrupt(dev);
+               return;
+       }
+       if ((dacsr = inw(dev->iobase + DT2821_DACSR)) & DT2821_DAERR) {
+#if 0
+               static int warn = 5;
+               if(--warn<=0){
+                       disable_irq(dev->irq);
+                       printk("disabling irq\n");
+               }
+#endif
+               comedi_error(dev, "D/A error");
+               dt282x_ao_cancel(dev,s);
+               comedi_done(dev,s);
+               return;
+       }
+       if (adcsr & DT2821_ADDONE) {
+               data = (sampl_t) inw(dev->iobase + DT2821_ADDAT);
+               data&=(1<<boardtype.adbits)-1;
+               if(devpriv->ad_2scomp){
+                       data^=1<<(boardtype.adbits-1);
+               }
+               s->cur_trig.data[s->buf_int_ptr++]=data;
+
+               devpriv->nread--;
+               if(!devpriv->nread){
+                       comedi_done(dev,s);
+               }else{
+                       if(supcsr&DT2821_SCDN)
+                               update_supcsr(DT2821_STRIG);
+               }
+
+               return;
+       }
+}
+
+
+static void dt282x_load_changain(comedi_device * dev, int n, unsigned int *chanlist)
+{
+       unsigned int i;
+       unsigned int chan, range;
+
+       outw(DT2821_LLE | (n - 1), dev->iobase + DT2821_CHANCSR);
+       for (i = 0; i < n; i++) {
+               chan = CR_CHAN(chanlist[i]);
+               range = CR_RANGE(chanlist[i]);
+               update_adcsr((range << 4) | (chan));
+       }
+       outw(n - 1, dev->iobase + DT2821_CHANCSR);
+}
+
+
+/*
+ *    Performs a single A/D conversion.
+ *      - Put channel/gain into channel-gain list
+ *      - preload multiplexer
+ *      - trigger conversion and wait for it to finish
+ */
+static int dt282x_ai_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       devpriv->adcsr = DT2821_ADCLK;
+       update_adcsr(0);
+
+       dt282x_load_changain(dev, 1, it->chanlist);
+
+       update_supcsr(DT2821_PRLD);
+       wait_for(!mux_busy(),
+                comedi_error(dev, "timeout\n");
+                return -ETIME;
+           );
+
+       update_supcsr(DT2821_STRIG);
+       wait_for(ad_done(),
+                comedi_error(dev, "timeout\n");
+                return -ETIME;
+           );
+
+       it->data[0] = inw(dev->iobase + DT2821_ADDAT) & ((1<<boardtype.adbits)-1);
+       if (devpriv->ad_2scomp)
+               it->data[0] ^= (1 << (boardtype.adbits - 1));
+
+       return 1;
+}
+
+#if 0
+static int dt282x_ai_cmd(comedi_device * dev, comedi_subdevice * s)
+{
+       int err=0;
+       comedi_cmd *cmd=&s->cur_cmd;
+
+       if(cmd->start_src!=TRIG_NOW ||
+          cmd->start_arg!=0 |||
+          cmd->scan_begin_arg!=0 ||
+          cmd->convert_src!=TRIG_TIMER ||
+          cmd->scan_end_src!=TRIG_COUNT ||
+          cmd->scan_end_arg!=cmd->chanlist_len){
+               err=1;
+               cmd->start_src=TRIG_NOW;
+               cmd->start_arg=0;
+               cmd->scan_begin_arg=0;
+               cmd->convert_src=TRIG_TIMER;
+               cmd->scan_end_src=TRIG_COUNT;
+               cmd->scan_end_arg=cmd->chanlist_len;
+       }
+       if(cmd->scan_begin_src!=TRIG_FOLLOW && cmd->scan_begin_src!=TRIG_EXT){
+               err=1;
+               cmd->scan_begin_src=TRIG_INVAL;
+       }
+       if(cmd->convert_arg<4000){
+               err=1;
+               cmd->convert_arg=4000;
+       }
+       if(cmd->stop_src!=TRIG_COUNT && cmd->stop_src!=TRIG_NONE){
+               err=1;
+
+       }
+
+       return -EINVAL;
+}
+#endif
+
+static int dt282x_ai_mode1(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       int timer;
+
+       if (!devpriv->usedma) {
+               dt282x_load_changain(dev,it->n_chan,it->chanlist);
+
+               devpriv->ntrig=it->n*it->n_chan;
+               devpriv->nread=devpriv->ntrig;
+
+               timer=dt282x_ns_to_timer(&it->trigvar);
+               outw(timer, dev->iobase + DT2821_TMRCTR);
+
+               devpriv->adcsr = DT2821_ADCLK | DT2821_IADDONE;
+               update_adcsr(0);
+
+               devpriv->supcsr = DT2821_ERRINTEN ;
+
+               update_supcsr(DT2821_PRLD);
+               wait_for(!mux_busy(),
+                        comedi_error(dev, "timeout\n");
+                        return -ETIME;
+                   );
+               update_supcsr(DT2821_STRIG);
+
+               return 0;
+       } else {
+               timer=dt282x_ns_to_timer(&it->trigvar);
+               outw(timer, dev->iobase + DT2821_TMRCTR);
+
+               devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS0;
+               update_supcsr(DT2821_CLRDMADNE | DT2821_BUFFB | DT2821_ADCINIT);
+               devpriv->adcsr = 0;
+
+               devpriv->ntrig=it->n*it->n_chan;
+               devpriv->nread=devpriv->ntrig;
+
+               devpriv->dma_dir=DMA_MODE_READ;
+               devpriv->current_dma_chan=0;
+               prep_ai_dma(dev,0,0);
+               enable_dma(devpriv->dma[0].chan);
+               if(devpriv->ntrig){
+                       prep_ai_dma(dev,1,0);
+                       enable_dma(devpriv->dma[1].chan);
+                       devpriv->supcsr |= DT2821_DDMA;
+                       update_supcsr(0);
+               }
+
+               devpriv->adcsr = DT2821_ADCLK | DT2821_IADDONE;
+               update_adcsr(0);
+
+               dt282x_load_changain(dev,it->n_chan,it->chanlist);
+
+               devpriv->adcsr = DT2821_ADCLK | DT2821_IADDONE;
+               update_adcsr(0);
+
+               update_supcsr(DT2821_PRLD);
+               wait_for(!mux_busy(),
+                        comedi_error(dev, "timeout\n");
+                        return -ETIME;
+                   );
+               update_supcsr(DT2821_STRIG);
+
+               return 0;
+       }
+}
+
+static int dt282x_ai_mode4(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       int timer;
+
+       if (!devpriv->usedma) {
+               dt282x_load_changain(dev,it->n_chan,it->chanlist);
+
+               devpriv->ntrig=it->n*it->n_chan;
+               devpriv->nread=devpriv->ntrig;
+
+               timer=dt282x_ns_to_timer(&it->trigvar1);
+               outw(timer, dev->iobase + DT2821_TMRCTR);
+
+               devpriv->adcsr = DT2821_ADCLK | DT2821_IADDONE;
+               update_adcsr(0);
+
+               devpriv->supcsr = DT2821_ERRINTEN ;
+
+               update_supcsr(DT2821_PRLD);
+               wait_for(!mux_busy(),
+                        comedi_error(dev, "timeout\n");
+                        return -ETIME;
+                   );
+               update_supcsr(DT2821_STRIG);
+
+               return 0;
+       } else {
+               timer=dt282x_ns_to_timer(&it->trigvar1);
+               outw(timer, dev->iobase + DT2821_TMRCTR);
+
+               devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS0 | DT2821_DS1;
+               update_supcsr(DT2821_CLRDMADNE | DT2821_BUFFB | DT2821_ADCINIT);
+               devpriv->adcsr = 0;
+
+               devpriv->ntrig=it->n*it->n_chan;
+               devpriv->nread=devpriv->ntrig;
+
+               devpriv->dma_dir=DMA_MODE_READ;
+               devpriv->current_dma_chan=0;
+               prep_ai_dma(dev,0,0);
+               enable_dma(devpriv->dma[0].chan);
+               if(devpriv->ntrig){
+                       prep_ai_dma(dev,1,0);
+                       enable_dma(devpriv->dma[1].chan);
+                       devpriv->supcsr |= DT2821_DDMA;
+                       update_supcsr(0);
+               }
+
+               devpriv->adcsr = DT2821_ADCLK | DT2821_IADDONE;
+               update_adcsr(0);
+
+               dt282x_load_changain(dev,it->n_chan,it->chanlist);
+
+               devpriv->adcsr = DT2821_ADCLK | DT2821_IADDONE;
+               update_adcsr(0);
+
+               update_supcsr(DT2821_PRLD);
+               wait_for(!mux_busy(),
+                        comedi_error(dev, "timeout\n");
+                        return -ETIME;
+                   );
+               devpriv->supcsr |= DT2821_XTRIG;
+               update_supcsr(0);
+
+               return 0;
+       }
+}
+
+static int dt282x_ai_cancel(comedi_device * dev, comedi_subdevice * s)
+{
+       devpriv->adcsr=0;
+       update_adcsr(0);
+
+       devpriv->supcsr = 0;
+       update_supcsr(DT2821_ADCINIT);
+
+       return 0;
+}
+
+
+static int dt282x_ns_to_timer(int *nanosec)
+{
+       int prescale,base,divider;
+
+       for(prescale=0;prescale<16;prescale++){
+               if(prescale==1)continue;
+               base=250*(1<<prescale);
+               divider=(*nanosec+base/2)/base;
+               if(divider<256){
+                       *nanosec=divider*base;
+                       return (prescale<<8)|(255-divider);
+               }
+       }
+       base=250*(1<<15);
+       divider=255;
+       *nanosec=divider*base;
+       return (15<<8)|(255-divider);
+}
+
+
+/*
+ *    Analog output routine.  Selects single channel conversion,
+ *      selects correct channel, converts from 2's compliment to
+ *      offset binary if necessary, loads the data into the DAC
+ *      data register, and performs the conversion.
+ */
+static int dt282x_ao(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       sampl_t data;
+       unsigned int chan;
+
+       data = it->data[0];
+       chan = CR_CHAN(it->chanlist[0]);
+
+       devpriv->dacsr |= DT2821_SSEL;
+
+       if (chan) {
+               /* select channel */
+               devpriv->dacsr |= DT2821_YSEL;
+               if (devpriv->da0_2scomp)
+                       data ^= (1<<(boardtype.dabits-1));
+       } else {
+               devpriv->dacsr &= ~DT2821_YSEL;
+               if (devpriv->da1_2scomp)
+                       data ^= (1<<(boardtype.dabits-1));
+       }
+
+       update_dacsr(0);
+
+       outw(data, dev->iobase + DT2821_DADAT);
+
+       update_supcsr(DT2821_DACON);
+
+       return 1;
+}
+
+static int dt282x_ao_mode2(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int size;
+       int timer;
+
+       devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS1 | DT2821_DDMA;
+       update_supcsr(DT2821_CLRDMADNE | DT2821_BUFFB | DT2821_DACINIT);
+
+       devpriv->ntrig=it->n*it->n_chan;
+       devpriv->nread=devpriv->ntrig;
+
+       devpriv->dma_dir=DMA_MODE_WRITE;
+       devpriv->current_dma_chan=0;
+
+       size=copy_from_buf(dev,s,devpriv->dma[0].buf,devpriv->dma_maxsize*2);
+       prep_ao_dma(dev,0,size/2);
+       enable_dma(devpriv->dma[0].chan);
+
+       size=copy_from_buf(dev,s,devpriv->dma[1].buf,devpriv->dma_maxsize*2);
+       prep_ao_dma(dev,1,size/2);
+       enable_dma(devpriv->dma[1].chan);
+       
+       timer=dt282x_ns_to_timer(&it->trigvar);
+       outw(timer, dev->iobase + DT2821_TMRCTR);
+
+       devpriv->dacsr = DT2821_SSEL| DT2821_DACLK | DT2821_IDARDY;
+       update_dacsr(0);
+
+       update_supcsr(DT2821_STRIG);
+
+       return 0;
+}
+
+static int dt282x_ao_cancel(comedi_device * dev, comedi_subdevice * s)
+{
+       devpriv->dacsr=0;
+       update_dacsr(0);
+
+       devpriv->supcsr = 0;
+       update_supcsr(DT2821_DACINIT);
+
+       return 0;
+}
+
+static int dt282x_dio(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       if(it->flags&TRIG_CONFIG){
+               int mask,i;
+
+               for(i=0;i<it->n_chan;i++){
+                       mask=(CR_CHAN(it->chanlist[i])<8)?0x00ff:0xff00;
+                       if(it->data[i])s->io_bits|=mask;
+                       else s->io_bits&=~mask;
+               }
+               if(s->io_bits&0x00ff)devpriv->dacsr|=DT2821_LBOE;
+               else devpriv->dacsr&=~DT2821_LBOE;
+               if(s->io_bits&0xff00)devpriv->dacsr|=DT2821_HBOE;
+               else devpriv->dacsr&=~DT2821_HBOE;
+
+               outw(devpriv->dacsr, dev->iobase + DT2821_DACSR);
+       }else{
+               unsigned int data;
+
+               if(it->flags&TRIG_WRITE){
+                       do_pack(&s->state,it);
+                       outw(s->state, dev->iobase + DT2821_DIODAT);
+               }else{
+                       data = inw(dev->iobase + DT2821_DIODAT);
+                       di_unpack(data,it);
+               }
+       }
+
+       return it->n_chan;
+}
+
+
+static int ai_range_table[]={ RANGE_dt282x_ai_lo_bipolar,
+       RANGE_dt282x_ai_lo_unipolar, RANGE_dt282x_ai_5_bipolar,
+       RANGE_dt282x_ai_5_unipolar };
+static int ai_range_pgl_table[]={ RANGE_dt282x_ai_hi_bipolar,
+       RANGE_dt282x_ai_hi_unipolar };
+static inline int opt_ai_range_lkup(int ispgl,int x)
+{
+       if(ispgl){
+               if(x<0 || x>=2)return RANGE_unknown;
+               return ai_range_pgl_table[x];
+       }else{
+               if(x<0 || x>=4)return RANGE_unknown;
+               return ai_range_table[x];
+       }
+}
+static int ao_range_table[]={ RANGE_bipolar10, RANGE_unipolar10, RANGE_bipolar5,
+       RANGE_unipolar5, RANGE_bipolar2_5 };
+static inline int opt_ao_range_lkup(int x)
+       { if(x<0)x=0; if(x>=5)x=0; return ao_range_table[x]; }
+
+enum{  opt_iobase=0, opt_irq, opt_dma1, opt_dma2,      /* i/o base, irq, dma channels */
+       opt_diff,                                       /* differential */
+       opt_ai_twos, opt_ao0_twos, opt_ao1_twos,        /* twos comp */
+       opt_ai_range, opt_ao0_range, opt_ao1_range,     /* range */
+};
+
+
+static int dt282x_recognize(char *name)
+{
+       int i;
+
+       for(i=0;i<n_boardtypes;i++){
+               if(!strcmp(boardtypes[i].name,name))
+                       return i;
+       }
+
+       return -1;
+}
+
+/*
+   options:
+   0   i/o base
+   1   irq
+   2   dma1
+   3   dma2
+   4   0=single ended, 1=differential
+   5   ai 0=straight binary, 1=2's comp
+   6   ao0 0=straight binary, 1=2's comp
+   7   ao1 0=straight binary, 1=2's comp
+   8   ai 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V
+   9   ao0 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V
+   10  ao1 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V
+ */
+static int dt282x_attach(comedi_device * dev, comedi_devconfig * it)
+{
+       int i, irqs, irq;
+       long flags;
+       int ret;
+       comedi_subdevice *s;
+
+       dev->board_ptr = boardtypes+dev->board;
+       dev->board_name = boardtypes[dev->board].name;
+
+       if (it->options[opt_iobase])
+               dev->iobase = it->options[opt_iobase];
+       else
+               dev->iobase = 0x240;
+
+       printk("comedi%d: dt282x: 0x%04x", dev->minor, dev->iobase);
+       if (check_region(dev->iobase, DT2821_SIZE) < 0) {
+               printk(" I/O port conflict\n");
+               return -EBUSY;
+       }
+       request_region(dev->iobase, DT2821_SIZE, "dt282x");
+       dev->iosize = DT2821_SIZE;
+
+       outw(DT2821_BDINIT, dev->iobase + DT2821_SUPCSR);
+       i = inw(dev->iobase + DT2821_ADCSR);
+#ifdef DEBUG
+       printk(" fingerprint=%x,%x,%x,%x,%x",
+              inw(dev->iobase + DT2821_ADCSR),
+              inw(dev->iobase + DT2821_CHANCSR),
+              inw(dev->iobase + DT2821_DACSR),
+              inw(dev->iobase + DT2821_SUPCSR),
+              inw(dev->iobase + DT2821_TMRCTR));
+#endif
+
+       if (
+                  ((inw(dev->iobase + DT2821_ADCSR) & DT2821_ADCSR_MASK)
+                   != DT2821_ADCSR_VAL) ||
+              ((inw(dev->iobase + DT2821_CHANCSR) & DT2821_CHANCSR_MASK)
+               != DT2821_CHANCSR_VAL) ||
+                  ((inw(dev->iobase + DT2821_DACSR) & DT2821_DACSR_MASK)
+                   != DT2821_DACSR_VAL) ||
+                ((inw(dev->iobase + DT2821_SUPCSR) & DT2821_SUPCSR_MASK)
+                 != DT2821_SUPCSR_VAL) ||
+                ((inw(dev->iobase + DT2821_TMRCTR) & DT2821_TMRCTR_MASK)
+                 != DT2821_TMRCTR_VAL)) {
+               printk(" board not found");
+               return -EIO;
+       }
+       /* should do board test */
+
+       irq = it->options[opt_irq];
+       if (irq < 0) {
+               save_flags(flags);
+               sti();
+               irqs = probe_irq_on();
+
+               /* trigger interrupt */
+
+               udelay(100);
+
+               irq = probe_irq_off(irqs);
+               restore_flags(flags);
+               if (0 /* error */ ) {
+                       printk(" error probing irq (bad)");
+               }
+       }
+       dev->irq = 0;
+       if (irq > 0) {
+               printk(" ( irq = %d )", irq);
+               request_irq(irq, dt282x_interrupt, SA_INTERRUPT, "dt282x", dev);
+               dev->irq = irq;
+       } else if (irq == 0) {
+               printk(" (no irq)");
+       } else {
+               printk(" (probe returned multiple irqs--bad)");
+       }
+
+       if((ret=alloc_private(dev,sizeof(dt282x_private)))<0)
+               return ret;
+
+       ret=dt282x_grab_dma(dev,it->options[opt_dma1],it->options[opt_dma2]);
+       if(ret<0)
+               return ret;
+
+       dev->n_subdevices = 3;
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+
+       s=dev->subdevices+0;
+
+       /* ai subdevice */
+       s->type=COMEDI_SUBD_AI;
+       s->subdev_flags=SDF_READABLE|((it->options[opt_diff])?SDF_DIFF:SDF_COMMON);
+       s->n_chan=(it->options[opt_diff])?boardtype.adchan_di:boardtype.adchan_se;
+       s->trig[0]=dt282x_ai_mode0;
+       s->trig[1]=dt282x_ai_mode1;
+       s->trig[4]=dt282x_ai_mode4;
+#if 0
+       s->do_cmd=dt282x_ai_cmd;
+#endif
+       s->cancel=dt282x_ai_cancel;
+       s->maxdata=(1<<boardtype.adbits)-1;
+       s->len_chanlist=16;
+       s->range_type = opt_ai_range_lkup(boardtype.ispgl,it->options[opt_ai_range]);
+       s->timer_type=TIMER_nanosec;
+       devpriv->ad_2scomp=it->options[opt_ai_twos];
+
+       s++;
+       if((s->n_chan=boardtype.dachan)){
+               /* ao subsystem */
+               s->type=COMEDI_SUBD_AO;
+               s->subdev_flags=SDF_WRITEABLE;
+               s->trig[0]=dt282x_ao;
+               s->trig[2]=dt282x_ao_mode2;
+               s->cancel=dt282x_ao_cancel;
+               s->maxdata=(1<<boardtype.dabits)-1;
+               s->len_chanlist=1;                      /* XXX could do 2 */
+               s->range_type_list=devpriv->darangelist;
+               s->timer_type=TIMER_nanosec;
+               devpriv->darangelist[0]=
+                       opt_ao_range_lkup(it->options[opt_ao0_range]);
+               devpriv->darangelist[1]=
+                       opt_ao_range_lkup(it->options[opt_ao1_range]);
+               devpriv->da0_2scomp=it->options[opt_ao0_twos];
+               devpriv->da1_2scomp=it->options[opt_ao1_twos];
+       }else{
+               s->type=COMEDI_SUBD_UNUSED;
+       }
+
+       s++;
+       /* dio subsystem */
+       s->type=COMEDI_SUBD_DIO;
+       s->subdev_flags=SDF_READABLE|SDF_WRITEABLE;
+       s->n_chan=16;
+       s->trig[0]=dt282x_dio;
+       s->maxdata=1;
+       s->range_type = RANGE_digital;
+
+       printk("\n");
+
+       return 0;
+}
+
+
+static void free_resources(comedi_device *dev)
+{
+       if (dev->irq) {
+               free_irq(dev->irq, dev);
+       }
+       if(dev->iobase)
+               release_region(dev->iobase, dev->iosize);
+       if(dev->private){
+               if (devpriv->dma[0].chan)
+                       free_dma(devpriv->dma[0].chan);
+               if (devpriv->dma[1].chan)
+                       free_dma(devpriv->dma[1].chan);
+               if (devpriv->dma[0].buf)
+                       free_page((unsigned long) devpriv->dma[0].buf);
+               if (devpriv->dma[1].buf)
+                       free_page((unsigned long) devpriv->dma[1].buf);
+       }
+}
+
+static int dt282x_detach(comedi_device * dev)
+{
+       printk("comedi%d: dt282x: remove\n", dev->minor);
+
+       free_resources(dev);
+
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_dt282x);
+
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_dt282x);
+}
+
+#endif
+
+static int dt282x_grab_dma(comedi_device *dev,int dma1,int dma2)
+{
+       int ret;
+
+       devpriv->usedma=0;
+
+       if(!dma1 && !dma2){
+               printk(" (no dma)");
+               return 0;
+       }
+
+       if(dma1==dma2 || dma1<5 || dma2<5 || dma1>7 || dma2>7)
+               return -EINVAL;
+
+       if(dma2<dma1){
+               int i;i=dma1;dma1=dma2;dma2=i;
+       }
+
+       ret = request_dma(dma1, "dt282x A");
+       if (ret)
+               return -EBUSY;
+       devpriv->dma[0].chan=dma1;
+
+       ret = request_dma(dma2, "dt282x B");
+       if (ret)
+               return -EBUSY;
+       devpriv->dma[1].chan=dma2;
+
+       devpriv->dma_maxsize = PAGE_SIZE >> 1;
+       devpriv->dma[0].buf = (void *) get_free_page(GFP_KERNEL | GFP_DMA);
+       devpriv->dma[1].buf = (void *) get_free_page(GFP_KERNEL | GFP_DMA);
+       if (!devpriv->dma[0].buf || !devpriv->dma[1].buf) {
+               printk(" can't get DMA memory");
+               return -ENOMEM;
+       }
+
+       printk(" (dma=%d,%d)",dma1,dma2);
+
+       devpriv->usedma=1;
+
+       return 0;
+}
+
diff --git a/comedi/drivers/dt3000.c b/comedi/drivers/dt3000.c
new file mode 100644 (file)
index 0000000..d234c09
--- /dev/null
@@ -0,0 +1,665 @@
+/*
+    module/dt3000.c
+    Data Translation DT3000 series driver
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1999 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.
+
+*/
+
+/*
+   The DT3000 series is Data Translation's attempt to make a PCI
+   data acquisition board.  The design of this series is very nice,
+   since each board has an on-board DSP (Texas Instruments TMS320C52).
+   However, a few details are a little annoying.  The boards lack
+   bus-mastering DMA, which eliminates them from serious work.
+   They also are not capable of autocalibration, which is a common
+   feature in modern hardware.  The default firmware is pretty bad,
+   making it nearly impossible to write an RT compatible driver.
+   It would make an interesting project to write a decent firmware
+   for these boards.
+
+   Data Translation originally wanted an NDA for the documentation
+   for the 3k series.  However, if you ask nicely, they might send
+   you the docs without one, also.
+*/
+
+#include <comedi_module.h>
+#include <linux/module.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#define PCI_VENDOR_ID_DT       0x1116
+
+typedef struct{
+       char *name;
+       unsigned int device_id;
+       int adchan;
+       int adbits;
+       int adrange;
+       int dachan;
+       int dabits;
+}dt3k_boardtype;
+
+static dt3k_boardtype dt3k_boardtypes[]={
+       {       name:           "dt3001",
+               device_id:      0x22,
+               adchan:         16,
+               adbits:         12,
+               adrange:        RANGE_dt3000_ai,
+               dachan:         2,
+               dabits:         12,
+       },
+       {       name:           "dt3001-pgl",
+               device_id:      0x27,
+               adchan:         16,
+               adbits:         12,
+               adrange:        RANGE_dt3000_ai_pgl,
+               dachan:         2,
+               dabits:         12,
+       },
+       {       name:           "dt3002",
+               device_id:      0x23,
+               adchan:         32,
+               adbits:         12,
+               adrange:        RANGE_dt3000_ai,
+               dachan:         0,
+               dabits:         0,
+       },
+       {       name:           "dt3003",
+               device_id:      0x24,
+               adchan:         64,
+               adbits:         12,
+               adrange:        RANGE_dt3000_ai,
+               dachan:         2,
+               dabits:         12,
+       },
+       {       name:           "dt3003-pgl",
+               device_id:      0x28,
+               adchan:         64,
+               adbits:         12,
+               adrange:        RANGE_dt3000_ai_pgl,
+               dachan:         2,
+               dabits:         12,
+       },
+       {       name:           "dt3004",
+               device_id:      0x25,
+               adchan:         16,
+               adbits:         16,
+               adrange:        RANGE_dt3000_ai,
+               dachan:         2,
+               dabits:         12,
+       },
+       {       name:           "dt3005",       /* a.k.a. 3004-200 */
+               device_id:      0x26,
+               adchan:         16,
+               adbits:         16,
+               adrange:        RANGE_dt3000_ai,
+               dachan:         2,
+               dabits:         12,
+       },
+};
+static int n_dt3k_boards=sizeof(dt3k_boardtypes)/sizeof(dt3k_boardtype);
+
+#define DT3000_SIZE            (2*0x1000)
+
+/* dual-ported RAM location definitions */
+
+#define DPR_DAC_buffer         (2*0x000)
+#define DPR_ADC_buffer         (2*0x800)
+#define DPR_Command            (2*0xfd3)
+#define DPR_SubSys             (2*0xfd3)
+#define DPR_Encode             (2*0xfd4)
+#define DPR_Params(a)          (2*(0xfd5+(a)))
+#define DPR_Tick_Reg_Lo                (2*0xff5)
+#define DPR_Tick_Reg_Hi                (2*0xff6)
+#define DPR_DA_Buf_Front       (2*0xff7)
+#define DPR_DA_Buf_Rear                (2*0xff8)
+#define DPR_AD_Buf_Front       (2*0xff9)
+#define DPR_AD_Buf_Rear                (2*0xffa)
+#define DPR_Int_Mask           (2*0xffb)
+#define DPR_Intr_Flag          (2*0xffc)
+#define DPR_Response_Mbx       (2*0xffe)
+#define DPR_Command_Mbx                (2*0xfff)
+
+/* command list */
+
+#define CMD_GETBRDINFO         0
+#define CMD_CONFIG             1
+#define CMD_GETCONFIG          2
+#define CMD_START              3
+#define CMD_STOP               4
+#define CMD_READSINGLE         5
+#define CMD_WRITESINGLE                6
+#define CMD_CALCCLOCK          7
+#define CMD_READEVENTS         8
+#define CMD_WRITECTCTRL                16
+#define CMD_READCTCTRL         17
+#define CMD_WRITECT            18
+#define CMD_READCT             19
+#define CMD_WRITEDATA          32
+#define CMD_READDATA           33
+#define CMD_WRITEIO            34
+#define CMD_READIO             35
+#define CMD_WRITECODE          36
+#define CMD_READCODE           37
+#define CMD_EXECUTE            38
+#define CMD_HALT               48
+
+#define SUBS_AI                0
+#define SUBS_AO                1
+#define SUBS_DIN       2
+#define SUBS_DOUT      3
+#define SUBS_MEM       4
+#define SUBS_CT                5
+
+/* interrupt flags */
+#define DT3000_CMDONE          0x80
+#define DT3000_CTDONE          0x40
+#define DT3000_DAHWERR         0x20
+#define DT3000_DASWERR         0x10
+#define DT3000_DAEMPTY         0x08
+#define DT3000_ADHWERR         0x04
+#define DT3000_ADSWERR         0x02
+#define DT3000_ADFULL          0x01
+
+#define DT3000_COMPLETION_MASK 0xff00
+#define DT3000_COMMAND_MASK    0x00ff
+#define DT3000_NOTPROCESSED    0x0000
+#define DT3000_NOERROR         0x5500
+#define DT3000_ERROR           0xaa00
+#define DT3000_NOTSUPPORTED    0xff00
+
+
+#define DT3000_EXTERNAL_CLOCK  1
+#define DT3000_RISING_EDGE     2
+
+#define TMODE_MASK             0x1c
+
+#define DT3000_AD_TRIG_INTERNAL                (0<<2)
+#define DT3000_AD_TRIG_EXTERNAL                (1<<2)
+#define DT3000_AD_RETRIG_INTERNAL      (2<<2)
+#define DT3000_AD_RETRIG_EXTERNAL      (3<<2)
+#define DT3000_AD_EXTRETRIG            (4<<2)
+
+#define DT3000_CHANNEL_MODE_SE         0
+#define DT3000_CHANNEL_MODE_DI         1
+
+/*
+--BEGIN-RANGE-DEFS--
+RANGE_dt3000_ai
+       -10     10
+       -5      5
+       -2.5    2.5
+       -1.25   1.25
+RANGE_dt3000_ai_pgl
+       -10     10
+       -1      1
+       -0.1    0.1
+       -0.02   0.02
+---END-RANGE-DEFS---
+*/
+
+typedef struct{
+       struct pci_dev *pci_dev;
+       unsigned long phys_addr;
+       void *io_addr;
+       unsigned int lock;
+}dt3k_private;
+#define devpriv ((dt3k_private *)dev->private)
+
+static int dt3000_attach(comedi_device *dev,comedi_devconfig *it);
+static int dt3000_detach(comedi_device *dev);
+comedi_driver driver_dt3000={
+       driver_name:    "dt3000",
+       module:         &__this_module,
+       attach:         dt3000_attach,
+       detach:         dt3000_detach,
+};
+
+
+#define TIMEOUT 100
+
+static int dt3k_send_cmd(comedi_device *dev,unsigned int cmd)
+{
+       int i;
+       unsigned int status;
+       
+       writew(cmd,dev->iobase+DPR_Command_Mbx);
+       
+       for(i=0;i<TIMEOUT;i++){
+               status=readw(dev->iobase+DPR_Command_Mbx);
+               if((status&DT3000_COMPLETION_MASK)!=DT3000_NOTPROCESSED)
+                       break;
+               udelay(1);
+       }
+       if((status&DT3000_COMPLETION_MASK)==DT3000_NOERROR){
+               return 0;
+       }
+
+       printk("dt3k_send_cmd() timeout/error status=0x%04x\n",status);
+       
+       return -ETIME;
+}
+
+static unsigned int dt3k_readsingle(comedi_device *dev,unsigned int subsys,
+       unsigned int chan,unsigned int gain)
+{
+       writew(subsys,dev->iobase+DPR_SubSys);
+       
+       writew(chan,dev->iobase+DPR_Params(0));
+       writew(gain,dev->iobase+DPR_Params(1));
+       
+       dt3k_send_cmd(dev,CMD_READSINGLE);
+
+       return readw(dev->iobase+DPR_Params(2));
+}
+
+static void dt3k_writesingle(comedi_device *dev,unsigned int subsys,
+       unsigned int chan,unsigned int data)
+{
+       writew(subsys,dev->iobase+DPR_SubSys);
+       
+       writew(chan,dev->iobase+DPR_Params(0));
+       writew(0,dev->iobase+DPR_Params(1));
+       writew(data,dev->iobase+DPR_Params(2));
+       
+       dt3k_send_cmd(dev,CMD_WRITESINGLE);
+}
+
+
+
+static int dt3k_ai_config(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int i;
+       unsigned int chan,range,aref;
+       
+       for(i=0;i<it->n_chan;i++){
+               chan=CR_CHAN(it->chanlist[i]);
+               range=CR_RANGE(it->chanlist[i]);
+               
+               writew((range<<6)|chan,dev->iobase+DPR_ADC_buffer+i);
+       }
+       aref=CR_AREF(it->chanlist[0]);
+       
+       writew(it->n_chan,dev->iobase+DPR_Params(0));
+#if 0
+       writew(clkprescale,dev->iobase+DPR_Params(1));
+       writew(clkdivider,dev->iobase+DPR_Params(2));
+       writew(tscanprescale,dev->iobase+DPR_Params(3));
+       writew(tscandiv,dev->iobase+DPR_Params(4));
+       writew(triggerclockmode,dev->iobase+DPR_Params(5));
+#endif
+       writew(aref==AREF_DIFF,dev->iobase+DPR_Params(6));
+       writew(0,dev->iobase+DPR_Params(7));
+       
+       return dt3k_send_cmd(dev,CMD_CONFIG);
+}
+       
+
+static int dt3k_ai_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       unsigned int chan,gain,aref;
+
+       chan=CR_CHAN(it->chanlist[0]);
+       gain=CR_RANGE(it->chanlist[0]);
+       /* docs don't explain how to select aref */
+       aref=CR_AREF(it->chanlist[0]);
+
+       it->data[0]=dt3k_readsingle(dev,SUBS_AI,chan,gain);
+       
+       return 1;
+}
+
+static int dt3k_ao_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       unsigned int chan,data;
+
+       chan=CR_CHAN(it->chanlist[0]);
+       data=it->data[0];
+
+       dt3k_writesingle(dev,SUBS_AO,chan,data);
+
+       return 1;
+}
+
+static void dt3k_dio_config(comedi_device *dev,int bits)
+{
+       /* XXX */
+       writew(SUBS_DOUT,dev->iobase+DPR_SubSys);
+       
+       writew(bits,dev->iobase+DPR_Params(0));
+#if 0
+       /* don't know */
+       writew(0,dev->iobase+DPR_Params(1));
+       writew(0,dev->iobase+DPR_Params(2));
+#endif
+       
+       dt3k_send_cmd(dev,CMD_CONFIG);
+}
+
+static int dt3k_dio(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       if(it->flags&TRIG_CONFIG){
+               int mask,i;
+
+               for(i=0;i<it->n_chan;i++){
+                       mask=(CR_CHAN(it->chanlist[i])<4)?0x0f:0xf0;
+                       if(it->data[i])s->io_bits|=mask;
+                       else s->io_bits&=~mask;
+               }
+
+               mask=(s->io_bits&0x01)|((s->io_bits&0x10)>>3);
+               dt3k_dio_config(dev,mask);
+       }else{
+               unsigned int data;
+
+               if(it->flags&TRIG_WRITE){
+                       do_pack(&s->state,it);
+                       dt3k_writesingle(dev,SUBS_DOUT,0,s->state);
+               }else{
+                       data=dt3k_readsingle(dev,SUBS_DIN,0,0);
+                       di_unpack(data,it);
+               }
+       }
+
+       return it->n_chan;
+}
+
+static int dt3k_readmem(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       unsigned int addr;
+       
+       addr=CR_CHAN(it->chanlist[0]);
+       
+       writew(SUBS_MEM,dev->iobase+DPR_SubSys);
+       writew(addr,dev->iobase+DPR_Params(0));
+       writew(1,dev->iobase+DPR_Params(1));
+
+       dt3k_send_cmd(dev,CMD_READCODE);
+
+       it->data[0]=readw(dev->iobase+DPR_Params(2));
+       
+       return 1;
+}
+
+static int dt_pci_probe(comedi_device *dev);
+
+static int dt3000_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       int ret=0;
+       
+       printk("dt3000:");
+#ifdef PCI_SUPPORT_VER1
+       if(!pcibios_present()){
+#else
+       if(!pci_present()){
+#endif
+               printk(" no PCI bus\n");
+               return -EINVAL;
+       }
+       
+       if((ret=alloc_private(dev,sizeof(dt3k_private)))<0)
+               return ret;
+
+       ret=dt_pci_probe(dev);
+       if(ret<0)return ret;
+       if(ret==0){
+               printk(" no DT board found\n");
+               return -ENODEV;
+       }
+
+       dev->board_name=dt3k_boardtypes[dev->board].name;
+
+       dev->n_subdevices=4;
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+
+       s=dev->subdevices;
+
+       /* ai subdevice */
+       s->type=COMEDI_SUBD_AI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=dt3k_boardtypes[dev->board].adchan;
+       s->trig[0]=dt3k_ai_mode0;
+       s->maxdata=(1<<dt3k_boardtypes[dev->board].adbits)-1;
+       s->len_chanlist=512;
+       s->range_type=RANGE_dt3000_ai;
+       s->timer_type=TIMER_nanosec;
+
+       s++;
+       /* ao subsystem */
+       s->type=COMEDI_SUBD_AO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->n_chan=2;
+       s->trig[0]=dt3k_ao_mode0;
+       s->maxdata=(1<<dt3k_boardtypes[dev->board].dabits)-1;
+       s->len_chanlist=1;
+       s->range_type=RANGE_bipolar10;
+
+       s++;
+       /* dio subsystem */
+       s->type=COMEDI_SUBD_DIO;
+       s->subdev_flags=SDF_READABLE|SDF_WRITEABLE;
+       s->n_chan=8;
+       s->trig[0]=dt3k_dio;
+       s->maxdata=1;
+       s->len_chanlist=8;
+       s->range_type=RANGE_digital;
+
+       s++;
+       /* mem subsystem */
+       s->type=COMEDI_SUBD_MEMORY;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=0x1000;
+       s->trig[0]=dt3k_readmem;
+       s->maxdata=0xff;
+       s->len_chanlist=1;
+       s->range_type=RANGE_unknown;
+
+#if 0
+       s++;
+       /* proc subsystem */
+       s->type=COMEDI_SUBD_PROC;
+#endif
+
+       return 0;
+}
+
+static int dt3000_detach(comedi_device *dev)
+{
+       if(dev->irq)free_irq(dev->irq,dev);
+
+       /* XXX */
+
+       return 0;
+}
+
+
+#ifdef PCI_SUPPORT_VER1
+static int dt_pci_find_device(comedi_device *dev);
+static int setup_pci(comedi_device *dev);
+
+static int dt_pci_probe(comedi_device *dev)
+{
+       int board;
+
+       ret=dt_pci_find_device(NULL,&board);
+       if(ret==0)
+               return 0;
+
+       setup_pci(dev);
+
+       return 1;
+}
+
+static int dt_pci_find_device(comedi_device *dev)
+{
+       int i;
+       unsigned char   pci_bus;
+       unsigned char   pci_dev_fn;
+       
+       for(i=0;i<n_dt3k_boards;i++){
+               if(pcibios_find_device(PCI_VENDOR_ID_DT,
+                       dt3k_boardtypes[i].device_id,
+                       0,
+                       &pci_bus,
+                       &pci_dev_fn) == PCIBIOS_SUCCESSFUL)
+               {
+                       devpriv->pci_bus=pci_bus;
+                       devpriv->pci_dev_fn=pci_dev_fn;
+                       dev->board=i;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static int setup_pci(comedi_device *dev)
+{
+       unsigned long           offset;
+       u32                     addr;
+
+       pcibios_read_config_dword(devpriv->pci_bus,devpriv->pci_device_fn,
+                       PCI_BASE_ADDRESS_0,&addr);
+       devpriv->phys_addr=addr;
+       offset=devpriv->phys_addr & ~PAGE_MASK;
+       devpriv->io_addr=ioremap(devpriv->phys_addr&PAGE_MASK,DT3000_SIZE+offset)
+               +offset;
+#if DEBUG
+       printk("0x%08lx mapped to %p, ",devpriv->phys_addr,devpriv->io_addr);
+#endif
+
+       dev->iobase = (int)devpriv->io_addr;
+
+       return 0;
+}
+
+#else
+
+static struct pci_dev *dt_pci_find_device(struct pci_dev *from,int *board);
+static int setup_pci(comedi_device *dev);
+
+static int dt_pci_probe(comedi_device *dev)
+{
+       int board;
+
+       devpriv->pci_dev=dt_pci_find_device(NULL,&board);
+       dev->board=board;
+
+       if(!devpriv->pci_dev)
+               return 0;
+
+       setup_pci(dev);
+
+       return 1;
+}
+
+static int setup_pci(comedi_device *dev)
+{
+       unsigned long           offset;
+       u32                     addr;
+
+#if LINUX_VERSION_CODE < 0x020300
+       addr=devpriv->pci_dev->base_address[0];
+#else
+       addr=devpriv->pci_dev->resource[0].start;
+#endif
+       devpriv->phys_addr=addr;
+       offset = devpriv->phys_addr & ~PAGE_MASK;
+       devpriv->io_addr = ioremap(devpriv->phys_addr & PAGE_MASK, DT3000_SIZE + offset )
+               + offset;
+#if DEBUG
+       printk("0x%08lx mapped to %p, ",devpriv->phys_addr,devpriv->io_addr);
+#endif
+
+       dev->iobase = (int)devpriv->io_addr;
+
+       return 0;
+}
+
+#if LINUX_VERSION_CODE < 0x020300
+static struct pci_dev *dt_pci_find_device(struct pci_dev *from,int *board)
+{
+       int i;
+       
+       if(!from){
+               from=pci_devices;
+       }else{
+               from=from->next;
+       }
+       while(from){
+               if(from->vendor == PCI_VENDOR_ID_DT){
+                       for(i=0;i<n_dt3k_boards;i++){
+                               if(from->device == dt3k_boardtypes[i].device_id){
+                                       *board=i;
+                                       return from;
+                               }
+                       }
+                       printk("unknown Data Translation PCI device found with device_id=0x%04x\n",from->device);
+               }
+               from=from->next;
+       }
+       *board=-1;
+       return from;
+}
+
+#else
+
+static struct pci_dev *dt_pci_find_device(struct pci_dev *from,int *board)
+{
+       int i;
+       
+       if(!from){
+               from=(struct pci_dev *)(pci_devices.next);
+       }else{
+               from=(struct pci_dev *)(from->global_list.next);
+       }
+       while(from){
+               if(from->vendor == PCI_VENDOR_ID_DT){
+                       for(i=0;i<n_dt3k_boards;i++){
+                               if(from->device == dt3k_boardtypes[i].device_id){
+                                       *board=i;
+                                       return from;
+                               }
+                       }
+                       printk("unknown Data Translation PCI device found with device_id=0x%04x\n",from->device);
+               }
+               from=(struct pci_dev *)(from->global_list.next);
+       }
+       *board=-1;
+       return from;
+}
+
+#endif
+#endif /* PCI_SUPPORT_VER1 */
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_dt3000);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_dt3000);
+}
+#endif
diff --git a/comedi/drivers/mite.c b/comedi/drivers/mite.c
new file mode 100644 (file)
index 0000000..5ff8a44
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+    module/mite.c
+    Hardware driver for NI Mite PCI interface chip
+
+    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.
+
+*/
+
+/*
+       The PCI-MIO E series driver was originally written by
+       Tomasz Motylewski <...>, and ported to comedi by ds.
+
+
+       References for specifications:
+       
+          321747b.pdf  Register Level Programmer Manual (obsolete)
+          321747c.pdf  Register Level Programmer Manual (new)
+          DAQ-STC reference manual
+
+       Other possibly relevant info:
+       
+          320517c.pdf  User manual (obsolete)
+          320517f.pdf  User manual (new)
+          320889a.pdf  delete
+          320906c.pdf  maximum signal ratings
+          321066a.pdf  about 16x
+          321791a.pdf  discontinuation of at-mio-16e-10 rev. c
+          321808a.pdf  about at-mio-16e-10 rev P
+          321837a.pdf  discontinuation of at-mio-16de-10 rev d
+          321838a.pdf  about at-mio-16de-10 rev N
+       
+       ISSUES:
+
+*/
+
+#include <comedi_module.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/malloc.h>
+#include <mite.h>
+#include <ni_stc.h>
+#include <kvmem.h>
+
+
+#define PCI_MITE_SIZE          4096
+#define PCI_DAQ_SIZE           4096
+
+
+struct mite_struct *mite_devices;
+
+
+
+
+#ifdef PCI_SUPPORT_VER1
+/* routines for the old PCI code (before 2.1.55) */
+
+void mite_init(void)
+{
+       struct mite_struct *mite;
+       int pci_index;
+       unsigned char pci_bus, pci_device_fn;
+       u16 vendor;
+       u16 device_id;
+
+       for(pci_index=0;pci_index<0xff;pci_index++){
+               if(pcibios_find_class(PCI_CLASS_OTHERS << 8,
+                       pci_index,&pci_bus,&pci_device_fn)!=PCIBIOS_SUCCESSFUL)
+                               break;
+
+               pcibios_read_config_word(pci_bus,pci_device_fn,PCI_VENDOR_ID,&vendor);
+               if(vendor==PCI_VENDOR_ID_NATINST){
+                       mite=kmalloc(sizeof(*mite),GFP_KERNEL);
+                       memset(mite,0,sizeof(*mite));
+
+                       mite->pci_bus=pci_bus;
+                       mite->pci_device_fn=pci_device_fn;
+
+                       pcibios_read_config_word(pci_bus,pci_device_fn,PCI_DEVICE_ID,&device_id);
+                       mite->device_id=device_id;
+
+                       mite->next=mite_devices;
+                       mite_devices=mite;
+               }
+       }
+}
+
+#else
+
+#if LINUX_VERSION_CODE < 0x020300
+
+/* functions for the new PCI code (after 2.1.55) */
+
+void mite_init(void)
+{
+       struct pci_dev *pcidev;
+       struct mite_struct *mite;
+
+       for(pcidev=pci_devices;pcidev;pcidev=pcidev->next){
+               if(pcidev->vendor==PCI_VENDOR_ID_NATINST){
+                       mite=kmalloc(sizeof(*mite),GFP_KERNEL);
+                       memset(mite,0,sizeof(*mite));
+
+                       mite->pcidev=pcidev;
+
+                       mite->next=mite_devices;
+                       mite_devices=mite;
+               }
+       }
+}
+
+#else
+
+/* And after the pci_devices change */
+
+void mite_init(void)
+{
+       struct pci_dev *pcidev;
+       struct mite_struct *mite;
+
+       pci_for_each_dev(pcidev){
+               if(pcidev->vendor==PCI_VENDOR_ID_NATINST){
+                       mite=kmalloc(sizeof(*mite),GFP_KERNEL);
+                       memset(mite,0,sizeof(*mite));
+
+                       mite->pcidev=pcidev;
+
+                       mite->next=mite_devices;
+                       mite_devices=mite;
+               }
+       }
+}
+
+
+#endif
+#endif
+
+int mite_setup(struct mite_struct *mite)
+{
+       unsigned long                   offset;
+       u32                             addr;
+       int i;
+
+#ifdef PCI_SUPPORT_VER1
+       pcibios_read_config_dword(mite->pci_bus,mite->pci_device_fn,PCI_BASE_ADDRESS_0,&addr);
+#else
+#if LINUX_VERSION_CODE < 0x020300
+       addr=mite->pcidev->base_address[0];
+#else
+       addr=mite->pcidev->resource[0].start;
+#endif
+#endif
+       mite->mite_phys_addr=addr;
+       offset = mite->mite_phys_addr & ~PAGE_MASK;
+       mite->mite_io_addr = ioremap(mite->mite_phys_addr & PAGE_MASK, PCI_MITE_SIZE + offset ) + offset;
+       printk("MITE:0x%08lx mapped to %p ",mite->mite_phys_addr,mite->mite_io_addr);
+       
+#ifdef PCI_SUPPORT_VER1
+       pcibios_read_config_dword(mite->pci_bus,mite->pci_device_fn,PCI_BASE_ADDRESS_1,&addr);
+#else
+#if LINUX_VERSION_CODE < 0x020300
+       addr=mite->pcidev->base_address[1];
+#else
+       addr=mite->pcidev->resource[1].start;
+#endif
+#endif
+       mite->daq_phys_addr=addr;
+       offset = mite->daq_phys_addr & ~PAGE_MASK;
+       mite->daq_io_addr = ioremap(mite->daq_phys_addr & PAGE_MASK, PCI_DAQ_SIZE + offset ) + offset;
+       printk("DAQ:0x%08lx mapped to %p, ",mite->daq_phys_addr,mite->daq_io_addr);
+
+       /* XXX don't know what the 0xc0 and 0x80 mean */
+       writel(mite->daq_phys_addr | 0x80 , mite->mite_io_addr + 0xc0 );
+       
+#ifdef PCI_SUPPORT_VER1
+       {
+               unsigned char irq;
+               pcibios_read_config_byte(mite->pci_bus,mite->pci_device_fn,PCI_INTERRUPT_LINE,&irq);
+               mite->irq=irq;
+       }
+#endif
+
+       /* DMA setup */
+       for(i=0;i<MITE_RING_SIZE;i++){
+               mite->ring[i].next=virt_to_bus(mite->ring+i+1);
+               mite->ring[i].unused=0x1c; /* eh? */
+       }
+
+       return (int) mite->daq_io_addr;
+}
+
+
+void mite_cleanup(void)
+{
+       struct mite_struct *mite,*next;
+
+       for(mite=mite_devices;mite;mite=next){
+               next=mite->next;
+               kfree(mite);
+       }
+}
+
+void mite_unsetup(struct mite_struct *mite)
+{
+       if(!mite)return;
+
+       if(mite->mite_io_addr){
+               iounmap(mite->mite_io_addr);
+               mite->mite_io_addr=NULL;
+       }
+       if(mite->daq_io_addr){
+               iounmap(mite->daq_io_addr);
+               mite->daq_io_addr=NULL;
+       }
+}
+
+
+int mite_kvmem_segment_load(struct mite_struct *mite,int i,char *kvmem,unsigned int len)
+{
+       int count,offset;
+
+       offset=((int)kvmem)&(PAGE_SIZE-1);
+
+       mite->ring[i].addr = kvirt_to_bus((int)kvmem);
+
+       count=PAGE_SIZE-offset;
+       if(count>len)count=len;
+       mite->ring[i].count = count;
+
+       return count;
+}
+
+void mite_dma_prep(struct mite_struct *mite,comedi_subdevice *s)
+{
+       int i,n;
+       int chor,chcr,mcr,dcr,lkcr;
+
+       for(i=0;i<MITE_RING_SIZE;i++){
+               n=s->prealloc_bufsz-s->buf_int_ptr;
+               n=mite_kvmem_segment_load(mite,i,((void *)s->cur_trig.data)+s->buf_int_ptr,n);
+               s->buf_int_ptr+=n;
+               if(s->buf_int_ptr>=s->cur_trig.data_len)
+                       s->buf_int_ptr=0;
+       }
+
+       writel(virt_to_bus(mite->ring),mite->mite_io_addr+MITE_LKAR+CHAN_OFFSET(0));
+
+       chor = CHOR_DMARESET | CHOR_FRESET;
+       writel(chor,mite->mite_io_addr+MITE_CHOR+CHAN_OFFSET(0));
+
+       chcr = CHCR_LINKLONG | CHCR_DEV_TO_MEM;
+       chcr = CHCR_LINKLONG | CHCR_MEM_TO_DEV;
+       writel(chcr,mite->mite_io_addr+MITE_CHCR+CHAN_OFFSET(0));
+
+       mcr = CR_RL64 | CR_ASEQxP1 | CR_PSIZEHALF;
+       writel(mcr,mite->mite_io_addr+MITE_MCR+CHAN_OFFSET(0));
+
+       dcr = CR_RL64 | CR_PSIZEHALF | CR_PORTIO | CR_AMDEVICE;
+       dcr |= CR_REQSDRQ0;
+       writel(dcr,mite->mite_io_addr+MITE_DCR+CHAN_OFFSET(0));
+
+       lkcr = CR_RL64 | CR_ASEQUP | CR_PSIZEWORD;
+       writel(lkcr,mite->mite_io_addr+MITE_LKCR+CHAN_OFFSET(0));
+
+       //lkar = 0;
+
+}
+
+void mite_dma_arm(struct mite_struct *mite)
+{
+       int chor;
+
+       /* arm */
+       chor = CHOR_START;
+       writel(chor,mite->mite_io_addr+CHAN_OFFSET(0)+MITE_CHOR);
+}
+
+void mite_dma_disarm(struct mite_struct *mite)
+{
+       int chor;
+
+       /* disarm */
+       chor = CHOR_ABORT;
+       writel(chor,mite->mite_io_addr+CHAN_OFFSET(0)+MITE_CHOR);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       mite_init();
+
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       mite_cleanup();
+}
+
+#ifdef LINUX_V20
+
+struct symbol_table mite_syms = {
+#include <linux/symtab_begin.h>
+       X(mite_dma_arm),
+       X(mite_dma_disarm),
+       X(mite_dma_prep),
+       X(mite_setup),
+       X(mite_unsetup),
+       X(mite_kvmem_segment_load),
+       X(mite_devices),
+#include <linux/symtab_end.h>
+};
+
+#endif
+
+#ifdef LINUX_V22
+
+EXPORT_SYMBOL(mite_dma_arm);
+EXPORT_SYMBOL(mite_dma_disarm);
+EXPORT_SYMBOL(mite_dma_prep);
+EXPORT_SYMBOL(mite_setup);
+EXPORT_SYMBOL(mite_unsetup);
+EXPORT_SYMBOL(mite_kvmem_segment_load);
+EXPORT_SYMBOL(mite_devices);
+
+#endif
+
+#endif
+
diff --git a/comedi/drivers/mite.h b/comedi/drivers/mite.h
new file mode 100644 (file)
index 0000000..e038a2d
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+    module/mite.h
+    Hardware driver for NI Mite PCI interface chip
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1999 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 _MITE_H_
+#define _MITE_H_
+
+#include <comedi_module.h>
+#include <linux/pci.h>
+#ifdef PCI_SUPPORT_VER1
+#include <linux/bios32.h>
+#endif
+
+
+#define PCI_VENDOR_ID_NATINST          0x1093
+
+
+#define MITE_RING_SIZE 32
+
+struct mite_dma_chain{
+       u32 count;
+       u32 addr;
+       u32 unused;
+       u32 next;
+};
+
+struct mite_struct{
+       struct mite_struct *next;
+       int used;
+
+#ifdef PCI_SUPPORT_VER1
+       unsigned char pci_bus;
+       unsigned char pci_device_fn;
+       int irq;
+       int device_id;
+#else
+       struct pci_dev *pcidev;
+#endif
+       unsigned long mite_phys_addr;
+       void *mite_io_addr;
+       unsigned long daq_phys_addr;
+       void *daq_io_addr;
+
+       struct mite_dma_chain ring[MITE_RING_SIZE];
+};
+
+extern struct mite_struct *mite_devices;
+
+#ifdef PCI_SUPPORT_VER1
+#define mite_irq(a)            ((a)->irq)
+#define mite_device_id(a)      ((a)->device_id)
+#else
+#define mite_irq(a)            ((a)->pcidev->irq)
+#define mite_device_id(a)      ((a)->pcidev->device)
+#endif
+
+#define mite_iobase(a)         ((a)->daq_io_addr)
+
+
+void mite_init(void);
+void mite_cleanup(void);
+int mite_setup(struct mite_struct *mite);
+void mite_unsetup(struct mite_struct *mite);
+
+void mite_dma_prep(struct mite_struct *mite,comedi_subdevice *s);
+
+
+#define CHAN_OFFSET(x)                 (0x100*(x))
+
+/* DMA base for chan 0 is 0x500, chan 1 is 0x600 */
+
+#define MITE_CHOR              0x500
+#define CHOR_DMARESET                  (1<<31)
+#define CHOR_SET_SEND_TC               (1<<11)
+#define CHOR_CLR_SEND_TC               (1<<10)
+#define CHOR_SET_LPAUSE                        (1<<9)
+#define CHOR_CLR_LPAUSE                        (1<<8)
+#define CHOR_CLRDONE                   (1<<7)
+#define CHOR_CLRRB                     (1<<6)
+#define CHOR_CLRLC                     (1<<5)
+#define CHOR_FRESET                    (1<<4)
+#define CHOR_ABORT                     (1<<3)
+#define CHOR_STOP                      (1<<2)
+#define CHOR_CONT                      (1<<1)
+#define CHOR_START                     (1<<0)
+#define CHOR_PON                       (CHOR_CLR_SEND_TC|CHOR_CLR_LPAUSE)
+
+#define MITE_CHCR              0x504
+#define CHCR_SET_DMA_IE                        (1<<31)
+#define CHCR_CLR_DMA_IE                        (1<<30)
+#define CHCR_SET_LINKP_IE              (1<<29)
+#define CHCR_CLR_LINKP_IE              (1<<28)
+#define CHCR_SET_SAR_IE                        (1<<27)
+#define CHCR_CLR_SAR_IE                        (1<<26)
+#define CHCR_SET_DONE_IE               (1<<25)
+#define CHCR_CLR_DONE_IE               (1<<24)
+#define CHCR_SET_MRDY_IE               (1<<23)
+#define CHCR_CLR_MRDY_IE               (1<<22)
+#define CHCR_SET_DRDY_IE               (1<<21)
+#define CHCR_CLR_DRDY_IE               (1<<20)
+#define CHCR_SET_LC_IE                 (1<<19)
+#define CHCR_CLR_LC_IE                 (1<<18)
+#define CHCR_SET_CONT_RB_IE            (1<<17)
+#define CHCR_CLR_CONT_RB_IE            (1<<16)
+#define CHCR_FIFODIS                   (1<<15)
+#define CHCR_FIFO_ON                   0
+#define CHCR_BURSTEN                   (1<<14)
+#define CHCR_NO_BURSTEN                        0
+#define CHCR_NFTP(x)                   ((x)<<11)
+#define CHCR_NFTP0                     CHCR_NFTP(0)
+#define CHCR_NFTP1                     CHCR_NFTP(1)
+#define CHCR_NFTP2                     CHCR_NFTP(2)
+#define CHCR_NFTP4                     CHCR_NFTP(3)
+#define CHCR_NFTP8                     CHCR_NFTP(4)
+#define CHCR_NFTP16                    CHCR_NFTP(5)
+#define CHCR_NETP(x)                   ((x)<<11)
+#define CHCR_NETP0                     CHCR_NETP(0)
+#define CHCR_NETP1                     CHCR_NETP(1)
+#define CHCR_NETP2                     CHCR_NETP(2)
+#define CHCR_NETP4                     CHCR_NETP(3)
+#define CHCR_NETP8                     CHCR_NETP(4)
+#define CHCR_CHEND1                    (1<<5)
+#define CHCR_CHEND0                    (1<<4)
+#define CHCR_DIR                       (1<<3)
+#define        CHCR_DEV_TO_MEM         CHCR_DIR
+#define        CHCR_MEM_TO_DEV         0
+#define CHCR_NORMAL                    ((0)<<0)
+#define CHCR_CONTINUE                  ((1)<<0)
+#define CHCR_RINGBUFF                  ((2)<<0)
+#define CHCR_LINKSHORT                 ((4)<<0)
+#define CHCR_LINKLONG                  ((5)<<0)
+#define CHCRPON                                (CHCR_CLR_DMA_IE | CHCR_CLR_LINKP_IE | CHCR_CLR_SAR_IE | CHCR_CLR_DONE_IE | CHCR_CLR_MRDY_IE | CHCR_CLR_DRDY_IE | CHCR_CLR_LC_IE | CHCR_CLR_CONT_IE)
+
+#define MITE_TCR               0x508
+
+/* CR bits */
+#define CR_RL(x)                       ((x)<<21)
+#define CR_RL0                         CR_RL(0)
+#define CR_RL1                         CR_RL(1)
+#define CR_RL2                         CR_RL(2)
+#define CR_RL4                         CR_RL(3)
+#define CR_RL8                         CR_RL(4)
+#define CR_RL16                                CR_RL(5)
+#define CR_RL32                                CR_RL(6)
+#define CR_RL64                                CR_RL(7)
+#define CR_RD(x)                       ((x)<<19)
+#define CR_RD0                         CR_RD(0)
+#define CR_RD32                                CR_RD(1)
+#define CR_RD512                       CR_RD(2)
+#define CR_RD8192                      CR_RD(3)
+#define CR_REQS(x)                     ((x)<<16)
+#define CR_REQSDRQ0                    CR_REQS(4)
+#define CR_REQSDRQ1                    CR_REQS(5)
+#define CR_REQSDRQ2                    CR_REQS(6)
+#define CR_REQSDRQ3                    CR_REQS(7)
+#define CR_ASEQx(x)                    ((x)<<10)
+#define CR_ASEQx0                      CR_ASEQx(0)
+#define        CR_ASEQDONT             CR_ASEQx0
+#define CR_ASEQxP1                     CR_ASEQx(1)
+#define        CR_ASEQUP               CR_ASEQxP1
+#define CR_ASEQxP2                     CR_ASEQx(2)
+#define        CR_ASEQDOWN             CR_ASEQxP2
+#define CR_ASEQxP4                     CR_ASEQx(3)
+#define CR_ASEQxP8                     CR_ASEQx(4)
+#define CR_ASEQxP16                    CR_ASEQx(5)
+#define CR_ASEQxP32                    CR_ASEQx(6)
+#define CR_ASEQxP64                    CR_ASEQx(7)
+#define CR_ASEQxM1                     CR_ASEQx(9)
+#define CR_ASEQxM2                     CR_ASEQx(10)
+#define CR_ASEQxM4                     CR_ASEQx(11)
+#define CR_ASEQxM8                     CR_ASEQx(12)
+#define CR_ASEQxM16                    CR_ASEQx(13)
+#define CR_ASEQxM32                    CR_ASEQx(14)
+#define CR_ASEQxM64                    CR_ASEQx(15)
+#define CR_PSIZEBYTE                   (1<<8)
+#define CR_PSIZEHALF                   (2<<8)
+#define CR_PSIZEWORD                   (3<<8)
+#define CR_PORTCPU                     (0<<6)
+#define CR_PORTIO                      (1<<6)
+#define CR_PORTVXI                     (2<<6)
+#define CR_PORTMXI                     (3<<6)
+#define CR_AMDEVICE                    (1<<0)
+
+#define MITE_MCR               0x50c
+#define        MCRPON                          0
+
+#define MITE_MAR               0x510
+
+#define MITE_DCR               0x514
+#define DCR_NORMAL                     (1<<29)
+#define DCRPON                         0
+
+#define MITE_DAR               0x518
+
+#define MITE_LKCR              0x51c
+
+#define MITE_LKAR              0x520
+#define MITE_LLKAR             0x524
+#define MITE_BAR               0x528
+#define MITE_BCR               0x52c
+#define MITE_SAR               0x530
+#define MITE_WSCR              0x534
+#define MITE_WSER              0x538
+#define MITE_CHSR              0x53c
+#define MITE_FCR               0x540
+
+#define MITE_FIFO              0x80
+#define MITE_FIFOEND           0xff
+
+#define MITE_AMRAM             0x00
+#define MITE_AMDEVICE          0x01
+#define MITE_AMHOST_A32_SINGLE 0x09
+#define MITE_AMHOST_A24_SINGLE 0x39
+#define MITE_AMHOST_A16_SINGLE 0x29
+#define MITE_AMHOST_A32_BLOCK  0x0b
+#define MITE_AMHOST_A32D64_BLOCK       0x08
+#define MITE_AMHOST_A24_BLOCK  0x3b
+
+
+
+#endif
+
diff --git a/comedi/drivers/multiq3.c b/comedi/drivers/multiq3.c
new file mode 100644 (file)
index 0000000..c8d7dfc
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+   module/multiq3.c
+   hardware driver for Quanser Consulting MultiQ-3 board
+
+   COMEDI - Linux Control and Measurement Device Interface
+   Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
+
+   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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+
+#define MULTIQ3_SIZE 16
+
+/*
+ * MULTIQ-3 port offsets
+ */
+#define MULTIQ3_DIGIN_PORT 0
+#define MULTIQ3_DIGOUT_PORT 0
+#define MULTIQ3_DAC_DATA 2
+#define MULTIQ3_AD_DATA 4
+#define MULTIQ3_AD_CS 4
+#define MULTIQ3_STATUS 6
+#define MULTIQ3_CONTROL 6
+#define MULTIQ3_CLK_DATA 8
+#define MULTIQ3_ENC_DATA 12
+#define MULTIQ3_ENC_CONTROL 14
+
+/*
+ * flags for CONTROL register
+ */
+#define MULTIQ3_AD_MUX_EN      0x0040
+#define MULTIQ3_AD_AUTOZ       0x0080
+#define MULTIQ3_AD_AUTOCAL     0x0100
+#define MULTIQ3_AD_SH          0x0200
+#define MULTIQ3_AD_CLOCK_4M    0x0400
+#define MULTIQ3_DA_LOAD                0x1800
+
+#define MULTIQ3_CONTROL_MUST    0x0600
+
+/*
+ * flags for STATUS register
+ */
+#define MULTIQ3_STATUS_EOC      0x008
+#define MULTIQ3_STATUS_EOC_I    0x010
+
+/*
+ * flags for encoder control
+ */
+#define MULTIQ3_CLOCK_DATA      0x00
+#define MULTIQ3_CLOCK_SETUP     0x18
+#define MULTIQ3_INPUT_SETUP     0x41
+#define MULTIQ3_QUAD_X4         0x38
+#define MULTIQ3_BP_RESET        0x01
+#define MULTIQ3_CNTR_RESET      0x02
+#define MULTIQ3_TRSFRPR_CTR     0x08
+#define MULTIQ3_TRSFRCNTR_OL    0x10
+#define MULTIQ3_EFLAG_RESET     0x06
+
+#define MULTIQ3_TIMEOUT 30
+
+static int multiq3_attach(comedi_device *dev,comedi_devconfig *it);
+static int multiq3_detach(comedi_device *dev);
+comedi_driver driver_multiq3={
+       driver_name:    "multiq3",
+       module:         &__this_module,
+       attach:         multiq3_attach,
+       detach:         multiq3_detach,
+};
+
+
+static int multiq3_ai(comedi_device *dev, comedi_subdevice *s, comedi_trig *it)
+{
+  int i, hi, lo;
+  int chan;
+  int data;
+  int status, control;
+
+  chan = CR_CHAN(it->chanlist[0]);
+  control = MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan<<3);
+  outw(control, dev->iobase+MULTIQ3_CONTROL);
+  for (i = 0; i < MULTIQ3_TIMEOUT; i++) {
+    status = inw(dev->iobase+MULTIQ3_STATUS);
+    if(status & MULTIQ3_STATUS_EOC) {
+      break;
+    }
+    udelay(10);
+  }
+  if(i == MULTIQ3_TIMEOUT){
+    rt_printk("multiq3: timeout\n");
+    return -ETIME;
+  }
+  outw(0, dev->iobase+MULTIQ3_AD_CS);
+  for (i = 0; i < MULTIQ3_TIMEOUT; i++) {
+    status = inw(dev->iobase+MULTIQ3_STATUS);
+    if(status & MULTIQ3_STATUS_EOC_I) {
+      break;
+    }
+    udelay(10);
+  }
+  hi = inb(dev->iobase + MULTIQ3_AD_CS) &0xff;
+  lo = inb(dev->iobase + MULTIQ3_AD_CS) &0xff;
+
+  data = (((hi << 8) | lo) + 0x1000) & 0x1fff;
+  it->data[0]=data;
+
+  return 1;
+}
+
+static int multiq3_ao(comedi_device *dev, comedi_subdevice *s, comedi_trig *it)
+{
+  int chan, control;
+
+  chan=CR_CHAN(it->chanlist[0]);
+  control = MULTIQ3_CONTROL_MUST | MULTIQ3_DA_LOAD | chan;
+  outw(control, dev->iobase+MULTIQ3_CONTROL);
+  outw(it->data[0], dev->iobase+MULTIQ3_DAC_DATA);
+  control = MULTIQ3_CONTROL_MUST;
+  outw(control, dev->iobase+MULTIQ3_CONTROL);
+  return 1;
+}
+
+static int multiq3_di(comedi_device *dev, comedi_subdevice *s, comedi_trig *it)
+{
+  unsigned int bits;
+
+  bits = inw(dev->iobase + MULTIQ3_DIGIN_PORT);
+
+  return di_unpack(bits,it);
+}
+
+static int multiq3_do(comedi_device *dev, comedi_subdevice *s, comedi_trig *it)
+{
+  do_pack(&s->state,it);
+
+  outw(s->state, dev->iobase + MULTIQ3_DIGOUT_PORT);
+
+  return it->n_chan;
+}
+
+static int multiq3_ei(comedi_device *dev, comedi_subdevice *s, comedi_trig *it)
+{
+  int b1, b2, b3;
+  int chan;
+  int data;
+  int control;
+
+  chan = CR_CHAN(it->chanlist[0]);
+  control = MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan<<3);
+  outw(control, dev->iobase+MULTIQ3_CONTROL);
+  outb(MULTIQ3_BP_RESET, dev->iobase+MULTIQ3_ENC_CONTROL);
+  outb(MULTIQ3_TRSFRCNTR_OL, dev->iobase+MULTIQ3_ENC_CONTROL);
+  b1 = inb(dev->iobase+MULTIQ3_ENC_DATA);
+  b2 = inb(dev->iobase+MULTIQ3_ENC_DATA);
+  b3 = inb(dev->iobase+MULTIQ3_ENC_DATA);
+
+  data =  (((b3<<16) | (b2 << 8) | (b1)) + 0x800000) & 0xffffff;
+  ((lsampl_t*)(it->data))[0] = data;
+
+  return 1;
+}
+
+static void encoder_reset(comedi_device *dev) {
+  int chan;
+  for (chan = 0 ; chan < dev->subdevices[4].n_chan ; chan++) {
+    int control = MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan<<3);
+    outw(control, dev->iobase+MULTIQ3_CONTROL);
+    outb(MULTIQ3_EFLAG_RESET, dev->iobase+MULTIQ3_ENC_CONTROL);
+    outb(MULTIQ3_BP_RESET, dev->iobase+MULTIQ3_ENC_CONTROL);
+    outb(MULTIQ3_CLOCK_DATA, dev->iobase+MULTIQ3_ENC_DATA);
+    outb(MULTIQ3_CLOCK_SETUP, dev->iobase+MULTIQ3_ENC_CONTROL);
+    outb(MULTIQ3_INPUT_SETUP, dev->iobase+MULTIQ3_ENC_CONTROL);
+    outb(MULTIQ3_QUAD_X4, dev->iobase+MULTIQ3_ENC_CONTROL);
+    outb(MULTIQ3_CNTR_RESET, dev->iobase+MULTIQ3_ENC_CONTROL);
+  }
+}
+
+/*
+   options[0] - I/O port
+   options[1] - irq
+   options[2] - number of encoder chips installed
+ */
+
+static int multiq3_attach(comedi_device * dev, comedi_devconfig * it)
+{
+  int result = 0;
+  int iobase;
+      int irq;
+      comedi_subdevice *s;
+
+    iobase = it->options[0];
+    printk("comedi%d: multiq3: 0x%04x ", dev->minor, iobase);
+    if (check_region(iobase, MULTIQ3_SIZE) < 0) {
+      printk("comedi%d: I/O port conflict\n", dev->minor);
+      return -EIO;
+    }
+
+      request_region(dev->iobase, MULTIQ3_SIZE, "multiq3");
+      dev->iobase = iobase;
+      dev->iosize = MULTIQ3_SIZE;
+
+      irq = it->options[1];
+      if (irq > 0) {
+       printk("comedi%d: irq = %d ignored\n", dev->minor, irq);
+      } else if(irq == 0) {
+       printk("comedi%d: no irq\n", dev->minor);
+      }
+      dev->board_name = "multiq3";
+      dev->n_subdevices = 5;
+      result = alloc_subdevices(dev);
+      if(result<0)return result;
+
+      s = dev->subdevices + 0;
+      /* ai subdevice */
+      s->type = COMEDI_SUBD_AI;
+      s->subdev_flags = SDF_READABLE;
+      s->n_chan = 8;
+      s->trig[0] = multiq3_ai;
+      s->maxdata = 0x1fff;
+      s->range_type = RANGE_bipolar5;
+
+      s = dev->subdevices + 1;
+      /* ao subdevice */
+      s->type = COMEDI_SUBD_AO;
+      s->subdev_flags = SDF_WRITEABLE;
+      s->n_chan = 8;
+      s->trig[0] = multiq3_ao;
+      s->maxdata = 0xfff;
+      s->range_type = RANGE_bipolar5;
+
+      s = dev->subdevices + 2;
+      /* di subdevice */
+      s->type = COMEDI_SUBD_DI;
+      s->subdev_flags = SDF_READABLE;
+      s->n_chan = 16;
+      s->trig[0] = multiq3_di;
+      s->maxdata = 1;
+      s->range_type = RANGE_digital;
+
+      s = dev->subdevices + 3;
+      /* do subdevice */
+      s->type = COMEDI_SUBD_DO;
+      s->subdev_flags = SDF_WRITEABLE;
+      s->n_chan = 16;
+      s->trig[0] = multiq3_do;
+      s->maxdata = 1;
+      s->range_type = RANGE_digital;
+      s->state = 0;
+
+      s = dev->subdevices + 4;
+      /* encoder (counter) subdevice */
+      s->type = COMEDI_SUBD_COUNTER;
+      s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
+      s->n_chan = it->options[2] * 2;
+      s->trig[0] = multiq3_ei;
+      s->maxdata = 0xffffff;
+      s->range_type = RANGE_unknown;
+
+      encoder_reset(dev);
+
+  return 0;
+}
+
+
+static int multiq3_detach(comedi_device * dev)
+{
+  printk("comedi%d: multiq3: remove\n", dev->minor);
+
+  if (dev->iobase) { release_region(dev->iobase, dev->iosize); }
+  if (dev->irq) { free_irq(dev->irq,dev); }
+
+  return 0;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_multiq3);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_multiq3);
+}
+#endif
diff --git a/comedi/drivers/ni_atmio.c b/comedi/drivers/ni_atmio.c
new file mode 100644 (file)
index 0000000..46a339e
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+    module/atmio-E.c
+    Hardware driver for NI AT-MIO E series cards
+
+    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.
+
+*/
+
+/*
+       The real guts of the driver is in ni-E.c, which is included
+       both here and in pcimio-E.c
+
+       
+       Interrupt support added by Truxton Fulton <trux@truxton.com>
+
+       References for specifications:
+       
+          321747b.pdf  Register Level Programmer Manual (obsolete)
+          321747c.pdf  Register Level Programmer Manual (new)
+          DAQ-STC reference manual
+
+       Other possibly relevant info:
+       
+          320517c.pdf  User manual (obsolete)
+          320517f.pdf  User manual (new)
+          320889a.pdf  delete
+          320906c.pdf  maximum signal ratings
+          321066a.pdf  about 16x
+          321791a.pdf  discontinuation of at-mio-16e-10 rev. c
+          321808a.pdf  about at-mio-16e-10 rev P
+          321837a.pdf  discontinuation of at-mio-16de-10 rev d
+          321838a.pdf  about at-mio-16de-10 rev N
+       
+       ISSUES:
+
+       need to deal with external reference for DAC, and other DAC
+       properties in board properties
+       
+       deal with at-mio-16de-10 revision D to N changes, etc.
+       
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <linux/malloc.h>
+#ifdef CONFIG_COMEDI_RT
+#include <linux/rtl.h>
+#endif
+#include <comedi_module.h>
+#include <ni_stc.h>
+#include <8255.h>
+
+#undef DEBUG
+
+#define ATMIO 1
+#undef PCIMIO
+
+/*
+ *  AT specific setup
+ */
+
+#define NI_SIZE 0x20
+
+static struct caldac_struct *type1[]={&caldac_mb88341,NULL,NULL};
+static struct caldac_struct *type2[]={&caldac_dac8800,&caldac_dac8043,NULL};
+
+static ni_board ni_boards[]={
+       {       device_id:      44,
+               name:           "at-mio-16e-1",
+               n_adchan:       16,
+               adbits:         12,
+               ai_fifo_depth:  8192,
+               alwaysdither:   0,
+               gainlkup:       ai_gain_16,
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  2048,
+               ao_unipolar:    1,
+               has_8255:       0,
+               caldac:         type1,
+       },
+       {       device_id:      25,
+               name:           "at-mio-16e-2",
+               n_adchan:       16,
+               adbits:         12,
+               ai_fifo_depth:  2048,
+               alwaysdither:   0,
+               gainlkup:       ai_gain_16,
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  2048,
+               ao_unipolar:    1,
+               has_8255:       0,
+               caldac:         type1,
+       },
+       {       device_id:      36,
+               name:           "at-mio-16e-10",
+               n_adchan:       16,
+               adbits:         12,
+               ai_fifo_depth:  512,
+               alwaysdither:   0,
+               gainlkup:       ai_gain_16,
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  0,
+               ao_unipolar:    1,
+               caldac:         type1,
+               has_8255:       0,
+       },
+       {       device_id:      37,
+               name:           "at-mio-16de-10",
+               n_adchan:       16,
+               adbits:         12,
+               ai_fifo_depth:  512,
+               alwaysdither:   0,
+               gainlkup:       ai_gain_16,
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  0,
+               ao_unipolar:    1,
+               caldac:         type1,
+               has_8255:       1,
+       },
+       {       device_id:      38,
+               name:           "at-mio-64e-3",
+               n_adchan:       64,
+               adbits:         12,
+               ai_fifo_depth:  2048,
+               alwaysdither:   0,
+               gainlkup:       ai_gain_16,
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  2048,
+               ao_unipolar:    1,
+               has_8255:       0,
+               caldac:         type1,
+       },
+       {       device_id:      39,
+               name:           "at-mio-16xe-50",
+               n_adchan:       16,
+               adbits:         16,
+               ai_fifo_depth:  512,
+               alwaysdither:   1,
+               gainlkup:       ai_gain_8,
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  0,
+               ao_unipolar:    0,
+               caldac:         type2,
+               has_8255:       0,
+       },
+       {       device_id:      50,
+               name:           "at-mio-16xe-10",
+               n_adchan:       16,
+               adbits:         16,
+               ai_fifo_depth:  512,
+               alwaysdither:   1,      /* unknown */
+               gainlkup:       ai_gain_8,      /* unknown */
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  0,      /* unknown */
+               ao_unipolar:    0,      /* unknown */
+               caldac:         type2,
+               has_8255:       0,
+       },
+       {       device_id:      51,
+               name:           "at-ai-16xe-10",
+               n_adchan:       16,
+               adbits:         16,
+               ai_fifo_depth:  512,
+               alwaysdither:   1,      /* unknown */
+               gainlkup:       ai_gain_8,
+               n_aochan:       0,
+               aobits:         0,
+               ao_fifo_depth:  0,
+               aorangelkup:    0,
+               ao_unipolar:    0,
+               caldac:         type2,
+               has_8255:       0,
+       }
+};
+
+
+static int ni_irqpin[]={-1,-1,-1,0,1,2,-1,3,-1,-1,4,5,6,-1,-1,7};
+
+#define interrupt_pin(a)       (ni_irqpin[(a)])
+
+#define IRQ_POLARITY 0
+
+
+/* How we access registers */
+
+#define ni_writew(a,b)         (outw((a),(b)+dev->iobase))
+#define ni_readw(a)            (inw((a)+dev->iobase))
+#define ni_writeb(a,b)         (outb((a),(b)+dev->iobase))
+#define ni_readb(a)            (inb((a)+dev->iobase))
+#define ni_writeb_p(a,b)       (outb_p((a),(b)+dev->iobase))
+#define ni_readb_p(a)          (inb_p((a)+dev->iobase))
+
+
+/*
+ * this is how we access windowed registers
+ */
+
+#define win_out(a,b) (ni_writew((b),Window_Address),ni_writew((a),Window_Data))
+#define win_in(b) (ni_writew((b),Window_Address),ni_readw(Window_Data))
+#define win_save() (ni_readw(Window_Address))
+#define win_restore(a) (ni_writew((a),Window_Address))
+
+
+typedef struct{
+       int dio;
+       int ao0p,ao1p;
+       int lastchan;
+       int last_do;
+       int rt_irq;
+       int irqmask;
+       int aimode;
+
+       unsigned short ao_mode1;
+       unsigned short ao_mode2;
+       unsigned short ao_mode3;
+       unsigned short ao_cmd1;
+       unsigned short ao_cmd2;
+       unsigned short ao_cmd3;
+       unsigned short ao_trigger_select;
+}ni_private;
+#define devpriv ((ni_private *)dev->private)
+
+static int atmio_attach(comedi_device *dev,comedi_devconfig *it);
+static int atmio_detach(comedi_device *dev);
+comedi_driver driver_atmio={
+       driver_name:    "atmio-E",
+       module:         &__this_module,
+       attach:         atmio_attach,
+       detach:         atmio_detach,
+};
+
+
+#include "ni_mio_common.c"
+
+
+static int init_stage2(comedi_device *dev,comedi_devconfig *it);
+static int ni_getboardtype(comedi_device *dev);
+
+/* clean up allocated resources */
+int atmio_E_free(comedi_device *dev)
+{
+       if(dev->iobase)
+               release_region(dev->iobase,NI_SIZE);
+       if(dev->irq){
+               comedi_free_irq(dev->irq,dev);
+       }
+
+       return 0;
+}
+
+/* called when driver is removed */
+static int atmio_detach(comedi_device *dev)
+{
+       return atmio_E_free(dev);
+}
+
+static int atmio_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       if(!strcmp("ni_E",it->board_name)){
+               printk("comedi: 'ni_E' deprecated.  Use 'atmio-E'\n");
+       }else if(!strcmp("atmio-E",it->board_name)){
+               ;
+       }else{
+               return 0;
+       }
+
+       return init_stage2(dev,it);
+}
+
+static int init_stage2(comedi_device *dev,comedi_devconfig *it)
+{
+       int             ret;
+       int             iobase;
+       int             board;
+       int             irq;
+
+       
+       /* reserve our I/O region */
+
+       iobase=0x200;
+       if(it->options[0])iobase=it->options[0];
+       
+       printk("comedi%d: ni_E: 0x%04x",dev->minor,iobase);
+       if(check_region(iobase,NI_SIZE)<0){
+               printk(" I/O port conflict\n");
+               return -EIO;
+       }
+       request_region(iobase,NI_SIZE,"ni_E");
+
+       dev->iobase=iobase;
+       dev->iosize=NI_SIZE;
+       
+       
+       /* board existence sanity check */
+       
+#ifdef DEBUG
+       {
+               int i;
+
+               printk(" board fingerprint:");
+              for(i=0;i<16;i+=2){
+                       printk(" %04x %02x",inw(dev->iobase+i),inb(dev->iobase+i+1));
+               }
+       }
+#endif
+       
+       /* get board type */
+
+       board=ni_getboardtype(dev);
+       if(board<0)return -EIO;
+       
+       printk(" %s",ni_boards[board].name);
+       dev->board_name=ni_boards[board].name;
+
+       /* irq stuff */
+
+       irq=it->options[1];
+       if(irq!=0){
+               if(irq<0 || irq>15 || ni_irqpin[irq]==-1){
+                       printk(" invalid irq\n");
+                       return -EINVAL;
+               }
+               printk(" ( irq = %d )",irq);
+               if( (ret=comedi_request_irq(irq,ni_E_interrupt,NI_E_IRQ_FLAGS,"atmio-E",dev))<0 ){
+                       printk(" irq not available\n");
+                       return -EINVAL;
+               }
+               dev->irq=irq;
+       }
+       
+       /* allocate private area */
+       
+       if((ret=alloc_private(dev,sizeof(ni_private)))<0)
+               return ret;
+       
+       dev->board=board;
+
+       /* generic E series stuff in ni-E.c */
+
+       if( (ret=ni_E_init(dev,it))<0 ){
+               return ret;
+       }
+       
+       return 0;
+}
+
+
+static int ni_getboardtype(comedi_device *dev)
+{
+       int device_id=ni_read_eeprom(dev,511);
+       int i;
+       
+       for(i=0;i<n_ni_boards;i++){
+               if(ni_boards[i].device_id==device_id){
+                       return i;
+               }
+       }
+       if(device_id==255){
+               printk(" can't find board\n");
+       }else if(device_id == 0){
+               printk(" EEPROM read error (?) or device not found\n");
+       }else{
+               printk(" unknown device ID %d -- contact author\n",device_id);
+       }
+       return -1;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_atmio);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_atmio);
+}
+#endif
diff --git a/comedi/drivers/ni_mio_common.c b/comedi/drivers/ni_mio_common.c
new file mode 100644 (file)
index 0000000..301a885
--- /dev/null
@@ -0,0 +1,1505 @@
+/*
+    module/ni-E.c
+    Hardware driver for NI E series cards
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-9 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.
+
+*/
+
+/*
+       This file needs to be included by another file, e.g.,
+       atmio-E.c.
+
+       Interrupt support added by Truxton Fulton <trux@truxton.com>
+
+       References for specifications:
+       
+          321747b.pdf  Register Level Programmer Manual (obsolete)
+          321747c.pdf  Register Level Programmer Manual (new)
+          DAQ-STC reference manual
+
+       Other possibly relevant info:
+       
+          320517c.pdf  User manual (obsolete)
+          320517f.pdf  User manual (new)
+          320889a.pdf  delete
+          320906c.pdf  maximum signal ratings
+          321066a.pdf  about 16x
+          321791a.pdf  discontinuation of at-mio-16e-10 rev. c
+          321808a.pdf  about at-mio-16e-10 rev P
+          321837a.pdf  discontinuation of at-mio-16de-10 rev d
+          321838a.pdf  about at-mio-16de-10 rev N
+       
+       ISSUES:
+
+       deal with at-mio-16de-10 revision D to N changes, etc.
+       
+*/
+
+#include <8255.h>
+
+
+/* reference: ground, common, differential, other */
+static int ni_modebits1[4]={ 0x3000, 0x2000, 0x1000, 0 };
+static int ni_modebits2[4]={ 0x3f, 0x3f, 0x37, 0x37 };
+
+static int ni_gainlkup[][16]={
+       { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
+       { 1, 2, 4, 7, 9, 10, 12, 15, 0,0,0,0,0,0,0,0 },
+       { 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 0,0 },
+       { 0, 1, 4, 7, 8, 9, 12, 15, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static int ni_range_lkup[]={
+       RANGE_ni_E_ai,
+       RANGE_ni_E_ai_limited,
+       RANGE_ni_E_ai_limited14,
+       RANGE_ni_E_ai_limited_602x
+};
+
+/*
+--BEGIN-RANGE-DEFS--
+RANGE_ni_E_ai
+        -10     10
+        -5      5
+        -2.5    2.5
+        -1      1
+        -0.5    0.5
+        -0.25   0.25
+        -0.1    0.1
+        -0.05   0.05
+        0       20
+        0       10
+        0       5
+        0       2
+        0       1
+        0       0.5
+        0       0.2
+        0       0.1
+RANGE_ni_E_ai_limited
+       -10     10
+       -5      5
+       -1      1
+       -0.1    0.1
+       0       10
+       0       5
+       0       1
+       0       0.1
+RANGE_ni_E_ai_limited14
+        -5      5
+        -2.5    2.5
+        -1      1
+        -0.5    0.5
+        -0.25   0.25
+        -0.1    0.1
+        -0.05   0.05
+        0       10
+        0       5
+        0       2
+        0       1
+        0       0.5
+        0       0.2
+        0       0.1
+RANGE_ni_E_ai_limited_602x
+        -10     10
+        -5      5
+        -0.5    0.5
+        -0.05   0.05
+        0       20
+        0       10
+        0       1
+        0       0.1
+RANGE_ni_E_ao
+        -10     10
+        0       10
+RANGE_ni_E_ao_ext
+        -10     10
+        0       10
+        -1      1       ext
+        0       1       ext
+---END-RANGE-DEFS---
+*/
+
+
+
+static int ni_dio(comedi_device *dev,comedi_subdevice *s,comedi_trig *it);
+static int ni_read_eeprom(comedi_device *dev,int addr);
+
+static int ni_eeprom(comedi_device *dev,comedi_subdevice *s,comedi_trig *it);
+static int ni_calib(comedi_device *dev,comedi_subdevice *s,comedi_trig *it);
+static void caldac_setup(comedi_device *dev,comedi_subdevice *s);
+
+
+static void ni_handle_fifo_half_full(comedi_device *dev);
+static void ni_handle_fifo_dregs(comedi_device *dev);
+
+static int ni_ao_fifo_half_empty(comedi_device *dev,comedi_subdevice *s);
+
+static int ni_8255_callback(int dir,int port,int data,void *arg);
+
+#undef DEBUG
+
+#define AIMODE_NONE            0
+#define AIMODE_HALF_FULL       1
+#define AIMODE_SCAN            2
+#define AIMODE_SAMPLE          3
+
+#ifdef CONFIG_COMEDI_RT
+
+#define RTirqmask_AI 1
+#define RTirqmask_AO 2
+
+static int ni_ai_lock(comedi_device *dev,comedi_subdevice *s)
+{
+       comedi_change_irq_flags(dev->irq,dev,NI_E_IRQ_FLAGS | SA_PRIORITY );
+
+       return 0;
+}
+
+static int ni_ai_unlock(comedi_device *dev,comedi_subdevice *s)
+{
+       comedi_change_irq_flags(dev->irq,dev,NI_E_IRQ_FLAGS );
+
+       return 0;
+}
+
+#else
+
+#define ni_ai_lock NULL
+#define ni_ai_unlock NULL
+
+#endif
+
+static void ni_E_interrupt(int irq,void *d,struct pt_regs * regs)
+{
+       comedi_device *dev=d;
+       comedi_subdevice *s=dev->subdevices;
+       int status;
+       unsigned short b_status;
+       int ack=0;
+       int wsave;
+
+/*
+    If you want to use windowed registers in an interrupt, it is
+    important that you restore the window address register.  If
+    you change certain modes, e.g., AI_Configuration_Start/End,
+    you need to set up software flags for non-interrupt routines.
+*/
+       wsave=win_save();
+       
+       b_status=ni_readw(AO_Status_1);
+#if 0
+printk("B status=0x%04x\n",b_status);
+#endif
+       status=ni_readw(AI_Status_1);
+#if 0
+printk("A status=0x%04x\n",status);
+#endif
+       if(status&(AI_Overrun_St|AI_Overflow_St)){
+               rt_printk("ni_E: overrun/overflow status=0x%04x\n",status);
+               comedi_done(dev,s);
+               return;
+       }
+
+#if 0
+       /* this is wrong, since we do AO now  */
+       if(!dev->subdevices->cur_trig.data){
+               rt_printk("aiee! cur_trig.data got zapped!\n");
+               comedi_done(dev,s);
+               return;
+       }
+#endif
+
+       if(status&AI_SC_TC_St){
+#ifdef DEBUG
+rt_printk("ni-E: SC_TC interrupt\n");
+#endif
+               ni_handle_fifo_dregs(dev);
+               win_out(0x0000,Interrupt_A_Enable_Register);
+               comedi_done(dev,s);
+
+               ack|=AI_SC_TC_Interrupt_Ack;
+       }
+       if(status&AI_FIFO_Half_Full_St){
+               ni_handle_fifo_half_full(dev);
+       }
+       if(dev->subdevices->cur_trig.flags&TRIG_WAKE_EOS){
+#if 0
+               if(status&AI_START_St){
+                       /* just ack it */
+                       ack|=AI_START_Interrupt_Ack;
+               }
+#endif
+               if(status&AI_STOP_St){
+                       ni_handle_fifo_dregs(dev);
+       
+                       comedi_eos(dev,dev->subdevices+0);
+       
+                       /* we need to ack the START, also */
+                       ack|=AI_STOP_Interrupt_Ack|AI_START_Interrupt_Ack;
+               }
+       }
+#if 0
+       switch(devpriv->aimode){
+       deafult:
+               break;
+       case AIMODE_HALF_FULL:
+               if(status&AI_FIFO_Half_Full_St){
+                       ni_handle_fifo_half_full(dev);
+               }
+               break;
+       case AIMODE_SCAN:
+#if 0
+               if(status&AI_START_St){
+                       /* just ack it */
+                       ack|=AI_START_Interrupt_Ack;
+               }
+#endif
+               if(status&AI_STOP_St){
+                       ni_handle_fifo_dregs(dev);
+       
+                       comedi_eos(dev,dev->subdevices+0);
+       
+                       /* we need to ack the START, also */
+                       ack|=AI_STOP_Interrupt_Ack|AI_START_Interrupt_Ack;
+               }
+               break;
+       case AIMODE_SAMPLE:
+               ni_handle_fifo_dregs(dev);
+               if(s->buf_int_count>=s->cur_chan){
+                       while(s->buf_int_count>=s->cur_chan)
+                               s->cur_chan+=s->cur_trig.n_chan*sizeof(sampl_t);
+                       comedi_eos(dev,dev->subdevices+0);
+               }
+               break;
+       }
+#endif
+
+       if(b_status&AO_Overrun_St){
+               printk("ni-E: AO FIFO underrun status=0x%04x status2=0x%04x\n",b_status,ni_readw(AO_Status_2));
+       }
+
+       if(b_status&AO_BC_TC_St){
+               printk("ni-E: AO BC_TC status=0x%04x status2=0x%04x\n",b_status,ni_readw(AO_Status_2));
+       }
+
+       if(b_status&AO_FIFO_Request_St)
+               ni_ao_fifo_half_empty(dev,dev->subdevices+1);
+
+       b_status=ni_readw(AO_Status_1);
+       if(b_status&Interrupt_B_St){
+               if(b_status&AO_FIFO_Request_St){
+                       printk("AO buffer underrun\n");
+               }
+               printk("Ack! didn't clear AO interrupt. b_status=0x%04x\n",b_status);
+               win_out(0,Interrupt_B_Enable_Register);
+       }
+
+       if(ack){
+               ack|=AI_START1_Interrupt_Ack;
+               ni_writew(ack,Interrupt_A_Ack);
+       }
+
+       win_restore(wsave);
+}
+
+static void ni_ai_fifo_read(comedi_device *dev,comedi_subdevice *s,
+               sampl_t *data,int n)
+{
+       int i;
+
+       for(i=0;i<n;i++){
+               data[i]=ni_readw(ADC_FIFO_Data_Register);
+       }
+}
+
+
+static void ni_handle_fifo_half_full(comedi_device *dev)
+{
+       int n,m;
+       comedi_subdevice *s=dev->subdevices+0;
+
+       /*
+          if we got a fifo_half_full interrupt, we can transfer fifo/2
+          samples without checking the empty flag.  It doesn't matter if
+          we transfer the rest of the samples, the performance trade-off
+          is minimal (checking empty flag for a few samples vs. having
+          1% more interrupts.)  At really high speeds, it's better to
+          ignore them.
+
+       */
+       
+       n=boardtype.ai_fifo_depth/2;
+
+       /* this makes the assumption that the buffer length is
+          greater than the half-fifo depth. */
+
+       if(s->buf_int_ptr+n*sizeof(sampl_t)>=s->cur_trig.data_len){
+               m=(s->cur_trig.data_len-s->buf_int_ptr)/sizeof(sampl_t);
+               ni_ai_fifo_read(dev,s,((void *)(s->cur_trig.data))+s->buf_int_ptr,m);
+               s->buf_int_count+=m*sizeof(sampl_t);
+               n-=m;
+               s->buf_int_ptr=0;
+
+               comedi_eobuf(dev,s);
+       }
+       ni_ai_fifo_read(dev,s,((void *)(s->cur_trig.data))+s->buf_int_ptr,n);
+       s->buf_int_count+=n*sizeof(sampl_t);
+       s->buf_int_ptr+=n*sizeof(sampl_t);
+
+       comedi_bufcheck(dev,s);
+}
+
+/*
+   Empties the AI fifo
+*/
+static void ni_handle_fifo_dregs(comedi_device *dev)
+{
+       comedi_subdevice *s=dev->subdevices+0;
+       sampl_t *data;
+       int i,n;
+
+       /*
+          Too bad NI didn't have the foresight to return a
+          "fifo empty" value if we read past the end of the
+          FIFO.  It would have made this procedure twice
+          as fast.
+
+          We can calculate how many samples to transfer.
+          This would save a lot of time.
+       */
+
+       data=((void *)s->cur_trig.data)+s->buf_int_ptr;
+       while(1){
+               n=(s->cur_trig.data_len-s->buf_int_ptr)/sizeof(sampl_t);
+               for(i=0;i<n;i++){
+                       if(ni_readw(AI_Status_1)&AI_FIFO_Empty_St){
+                               return;
+                       }
+                       *data=ni_readw(ADC_FIFO_Data_Register);
+                       data++;
+                       s->buf_int_ptr+=sizeof(sampl_t);
+                       s->buf_int_count+=sizeof(sampl_t);
+               }
+               s->buf_int_ptr=0;
+               data=s->cur_trig.data;
+               comedi_eobuf(dev,s);
+       }
+}
+
+/*
+   used for both cancel ioctl and board initialization
+
+   this is pretty harsh for a cancel, but it works...
+ */
+static int ni_ai_reset(comedi_device *dev,comedi_subdevice *s)
+{
+       win_out(0x0000,Interrupt_A_Enable_Register);
+
+       win_out(AI_Reset,Joint_Reset_Register);
+
+       win_out(1,ADC_FIFO_Clear);
+
+       /* ai configuration */
+
+       win_out(AI_Configuration_Start,Joint_Reset_Register);
+
+       win_out(0x0000,AI_Command_1_Register); /* reset pulses */
+       win_out(0x000d,AI_Mode_1_Register);
+       win_out(0x0000,AI_Mode_2_Register);
+#if 0
+       win_out((1<<6)|0x0000,AI_Mode_3_Register); /* generate FIFO interrupts on half full */
+#else
+       win_out((0<<6)|0x0000,AI_Mode_3_Register); /* generate FIFO interrupts on non-empty */
+#endif
+       win_out(0xa4a0,AI_Personal_Register); /* ? */
+       win_out(0x032e,AI_Output_Control_Register);
+       win_out(0x0060,AI_Trigger_Select_Register); /* trigger source */
+
+       /* this should be done in _ai_modeX() */
+       win_out(0x29e0,AI_START_STOP_Select_Register);
+
+       /* the following registers should not be changed:
+               Clock_and_FOUT_Register
+               AI_Mode_1_Register
+               AI_Mode_3_Register
+               AI_Personal_Register
+               AI_Output_Control_Register
+               AI_Trigger_Select_Register
+       */
+
+       win_out(0x3f80,Interrupt_A_Ack_Register); /* clear interrupts */
+
+       win_out(AI_Configuration_End,Joint_Reset_Register);
+
+       return 0;
+}
+
+static void ni_load_channelgain_list(comedi_device *dev,unsigned int n_chan,unsigned int *list,int dither);
+
+
+/*
+       Mode 0 is immediate
+*/
+static int ni_ai_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int i;
+       int chan;
+       int wsave;
+       
+       wsave=win_save();
+
+       win_out(1,ADC_FIFO_Clear);
+
+       /* interrupt on errors */
+       win_out(0x0020,Interrupt_A_Enable_Register) ;
+       
+       for(chan=0;chan<it->n_chan;chan++){
+               ni_load_channelgain_list(dev,1,it->chanlist+chan,(it->flags&TRIG_DITHER)==TRIG_DITHER);
+#if 0
+       /* needs start configuration */
+       win_out(AI_START_Edge|AI_START_Sync|
+               AI_STOP_Select(19)|AI_STOP_Sync,
+               AI_START_STOP_Select_Register);
+#endif
+
+
+               win_out(1,AI_Command_1_Register);
+       
+               /* I don't know how long it takes to access the bus,
+                  so shorter loops might cause timeouts */
+#define NI_TIMEOUT 1000
+               for(i=0;i<NI_TIMEOUT;i++){
+                       if(!(ni_readw(AI_Status_1)&AI_FIFO_Empty_St)){
+                               it->data[chan]=ni_readw(ADC_FIFO_Data_Register);
+                               if(boardtype.adbits==12){
+                                       if(CR_RANGE(it->chanlist[chan])<8)
+                                               it->data[chan]^=0x800;
+                                       it->data[chan]&=0xfff;
+                               }else{
+                                       if(CR_RANGE(it->chanlist[chan])<8)
+                                               it->data[chan]^=0x8000;
+                                       it->data[chan]&=0xffff;
+                               }
+                               break;
+                       }
+                       /*udelay(25);*/
+               }
+               if(i==NI_TIMEOUT)goto timeout;
+       }
+       win_restore(wsave);
+       return chan;
+
+timeout:
+       rt_printk("ni_E: timeout 2\n");
+       win_restore(wsave);
+       return -ETIME;
+}
+
+
+static void ni_load_channelgain_list(comedi_device *dev,unsigned int n_chan,unsigned int *list,int dither)
+{
+       unsigned int chan,range,aref;
+       unsigned int i;
+       unsigned int hi,lo;
+
+       win_out(1,Configuration_Memory_Clear);
+
+       for(i=0;i<n_chan;i++){
+               chan=CR_CHAN(list[i]);
+               range=CR_RANGE(list[i]);
+               aref=CR_AREF(list[i]);
+               
+               /* fix the external/internal range differences */
+               range=ni_gainlkup[boardtype.gainlkup][range];
+               list[i]=CR_PACK(chan,range,aref);
+
+               hi=ni_modebits1[aref]|(chan&ni_modebits2[aref]);
+               ni_writew(hi,Configuration_Memory_High);
+
+               lo=((i==n_chan-1)?0x8000:0) | ((range&0x8)<<5) | (range&0x7) | (dither<<9);
+               ni_writew(lo,Configuration_Memory_Low);
+       }
+
+       /* prime the channel/gain list */
+
+       win_out(1,AI_Command_1_Register);
+       for(i=0;i<40;i++){
+               if(!(ni_readw(AI_Status_1)&AI_FIFO_Empty_St)){
+                       win_out(1,ADC_FIFO_Clear);
+                       return;
+               }
+               udelay(25);
+       }
+       rt_printk("ni_E: timeout 1\n");
+}
+
+
+/*
+       mode 2 is timed, multi-channel
+*/
+static int ni_ai_mode2(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int wsave;
+
+       wsave=win_save();
+
+       win_out(1,ADC_FIFO_Clear);
+
+       ni_load_channelgain_list(dev,it->n_chan,it->chanlist,(it->flags&TRIG_DITHER)==TRIG_DITHER);
+       
+       /* start configuration */
+       win_out(AI_Configuration_Start,Joint_Reset_Register);
+
+       /* stage number of scans */
+       win_out((it->n-1)>>16,AI_SC_Load_A_Registers);
+       win_out((it->n-1)&0xffff,AI_SC_Load_A_Registers+1);
+       
+       /* load SC (Scan Count) */
+       win_out(0x20,AI_Command_1_Register);
+       
+       /*
+           AI_SI_Special_Trigger_Delay=0
+           AI_Pre_Trigger=0
+         AI_START_STOP_Select_Register:
+           AI_START_Polarity=0 (?)     rising edge
+           AI_START_Edge=1             edge triggered
+           AI_START_Sync=1 (?)         
+           AI_START_Select=0           SI_TC
+           AI_STOP_Polarity=0          rising edge
+           AI_STOP_Edge=0              level
+           AI_STOP_Sync=1              
+           AI_STOP_Select=19           external pin (configuration mem)
+        */
+       win_out(AI_START_Edge|AI_START_Sync|
+               AI_STOP_Select(19)|AI_STOP_Sync,
+               AI_START_STOP_Select_Register);
+
+       win_out((it->trigvar>>16),AI_SI_Load_A_Registers);
+       win_out((it->trigvar&0xffff),AI_SI_Load_A_Registers+1);
+       /* AI_SI_Initial_Load_Source=A */
+       win_out(0,AI_Mode_2_Register);
+       /* load SI */
+       win_out(0x200,AI_Command_1_Register);
+
+       /* stage freq. counter into SI B */
+       win_out((it->trigvar>>16),AI_SI_Load_B_Registers);
+       win_out((it->trigvar&0xffff),AI_SI_Load_B_Registers+1);
+
+       win_out(it->trigvar1,AI_SI2_Load_A_Register); /* 0,0 does not work. */
+       win_out(it->trigvar1,AI_SI2_Load_B_Register);
+
+       /* AI_SI2_Reload_Mode = alternate */
+       /* AI_SI2_Initial_Load_Source = A */
+       win_out(0x0100,AI_Mode_2_Register);
+
+       /* AI_SI2_Load */
+       win_out(0x0800,AI_Command_1_Register);
+
+       /* AI_SI_Initial_Load_Source=0
+          AI_SI_Reload_Mode(0)
+          AI_SI2_Reload_Mode = alternate, AI_SI2_Initial_Load_Source = B */
+       win_out(0x0300,AI_Mode_2_Register);
+
+       if(dev->irq){
+               int bits;
+
+               /* interrupt on FIFO, errors, SC_TC */
+               bits=0x00a1;
+
+               if(it->flags&TRIG_WAKE_EOS){
+                       /* wake on end-of-scan */
+                       devpriv->aimode=AIMODE_SCAN;
+               }else if(s->cb_mask&COMEDI_CB_EOS){
+                       devpriv->aimode=AIMODE_SAMPLE;
+               }else{
+                       devpriv->aimode=AIMODE_HALF_FULL;
+               }
+               switch(devpriv->aimode){
+               case AIMODE_HALF_FULL:
+                       /*generate FIFO interrupts on half-full */
+                       win_out(AI_FIFO_Mode_HF|0x0000,AI_Mode_3_Register);
+                       break;
+               case AIMODE_SAMPLE:
+                       /*generate FIFO interrupts on non-empty */
+                       win_out(AI_FIFO_Mode_NE|0x0000,AI_Mode_3_Register);
+                       break;
+               case AIMODE_SCAN:
+                       /*generate FIFO interrupts on half-full */
+                       win_out(AI_FIFO_Mode_HF|0x0000,AI_Mode_3_Register);
+                       bits|=AI_STOP_Interrupt_Enable;
+                       break;
+               default:
+                       break;
+               }
+
+               win_out(0x3f80,Interrupt_A_Ack_Register); /* clear interrupts */
+
+               win_out(bits,Interrupt_A_Enable_Register) ;
+       }else{
+               /* interrupt on nothing */
+               win_out(0x0000,Interrupt_A_Enable_Register) ;
+
+               /* XXX start polling if necessary */
+       }
+
+#ifdef DEBUG
+rt_printk("end config\n");
+#endif
+       /* end configuration */
+       win_out(AI_Configuration_End,Joint_Reset_Register);
+       
+       /* AI_SI2_Arm, AI_SI_Arm, AI_DIV_Arm, AI_SC_Arm */
+       win_out(0x1540,AI_Command_1_Register);
+
+       /* AI_START1_Pulse */
+       win_out(AI_START1_Pulse,AI_Command_2_Register);
+#ifdef DEBUG
+rt_printk("START1 pulse\n");
+#endif
+
+       /* XXX hack alert */
+       s->cur_chan=s->cur_trig.n_chan*sizeof(sampl_t);
+
+       win_restore(wsave);
+       return 0;
+}
+
+/*
+       mode 4 is external trigger for scans, timer for samples
+       in a scan
+*/
+static int ni_ai_mode4(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int wsave;
+
+       wsave=win_save();
+
+       win_out(1,ADC_FIFO_Clear);
+
+       ni_load_channelgain_list(dev,it->n_chan,it->chanlist,(it->flags&TRIG_DITHER)==TRIG_DITHER);
+       
+       /* start configuration */
+       win_out(AI_Configuration_Start,Joint_Reset_Register);
+
+       /* stage number of scans */
+       win_out((it->n-1)>>16,AI_SC_Load_A_Registers);
+       win_out((it->n-1)&0xffff,AI_SC_Load_A_Registers+1);
+       
+       /* load SC (Scan Count) */
+       win_out(0x20,AI_Command_1_Register);
+       
+       /*
+           AI_SI_Special_Trigger_Delay=0
+           AI_Pre_Trigger=0
+         AI_START_STOP_Select_Register:
+           AI_START_Polarity=0 (?)     rising edge
+           AI_START_Edge=1             edge triggered
+           AI_START_Sync=1 (?)         
+           AI_START_Select=1           PFI0
+           AI_STOP_Polarity=0          rising edge
+           AI_STOP_Edge=0              level
+           AI_STOP_Sync=1              
+           AI_STOP_Select=19           external pin (configuration mem)
+       */
+       win_out(AI_START_Edge|AI_START_Sync|AI_START_Select(1)|
+               AI_STOP_Select(19)|AI_STOP_Sync,
+               AI_START_STOP_Select_Register);
+       
+#if 0
+       win_out((it->trigvar>>16),AI_SI_Load_A_Registers);
+       win_out((it->trigvar&0xffff),AI_SI_Load_A_Registers+1);
+       /* AI_SI_Initial_Load_Source=A */
+       win_out(0,AI_Mode_2_Register);
+       /* load SI */
+       win_out(0x200,AI_Command_1_Register);
+
+       /* stage freq. counter into SI B */
+       win_out((it->trigvar>>16),AI_SI_Load_B_Registers);
+       win_out((it->trigvar&0xffff),AI_SI_Load_B_Registers+1);
+#endif
+
+       win_out(it->trigvar1,AI_SI2_Load_A_Register); /* 0,0 does not work. */
+       win_out(it->trigvar1,AI_SI2_Load_B_Register);
+
+       /* AI_SI2_Reload_Mode = alternate */
+       /* AI_SI2_Initial_Load_Source = A */
+       win_out(0x0100,AI_Mode_2_Register);
+
+       /* AI_SI2_Load */
+       win_out(0x0800,AI_Command_1_Register);
+
+       /* AI_SI_Initial_Load_Source=0
+          AI_SI_Reload_Mode(0)
+          AI_SI2_Reload_Mode = alternate, AI_SI2_Initial_Load_Source = B */
+       win_out(0x0300,AI_Mode_2_Register);
+
+       if(dev->irq){
+               int bits;
+
+               /* interrupt on FIFO, errors, SC_TC */
+               bits=0x00a1;
+
+               if(it->flags&TRIG_WAKE_EOS){
+                       /* wake on end-of-scan */
+                       devpriv->aimode=AIMODE_SCAN;
+               }else if(s->cb_mask&COMEDI_CB_EOS){
+                       devpriv->aimode=AIMODE_SAMPLE;
+               }else{
+                       devpriv->aimode=AIMODE_HALF_FULL;
+               }
+               switch(devpriv->aimode){
+               case AIMODE_HALF_FULL:
+                       /*generate FIFO interrupts on half-full */
+                       win_out(AI_SI2_Source_Select|AI_FIFO_Mode_HF|0x0000,AI_Mode_3_Register);
+                       break;
+               case AIMODE_SAMPLE:
+                       /*generate FIFO interrupts on non-empty */
+                       win_out(AI_SI2_Source_Select|AI_FIFO_Mode_NE|0x0000,AI_Mode_3_Register);
+                       break;
+               case AIMODE_SCAN:
+                       /*generate FIFO interrupts on half-full */
+                       win_out(AI_SI2_Source_Select|AI_FIFO_Mode_HF|0x0000,AI_Mode_3_Register);
+                       bits|=AI_STOP_Interrupt_Enable;
+                       break;
+               default:
+                       break;
+               }
+
+               /* clear interrupts */
+               win_out(0x3f80,Interrupt_A_Ack_Register);
+
+               win_out(bits,Interrupt_A_Enable_Register) ;
+       }else{
+               /* interrupt on nothing */
+               win_out(0x0000,Interrupt_A_Enable_Register) ;
+
+               /* XXX start polling if necessary */
+       }
+       
+       /* end configuration */
+       win_out(AI_Configuration_End,Joint_Reset_Register);
+
+       /* AI_SI2_Arm, AI_DIV_Arm, AI_SC_Arm */
+       win_out(0x1500,AI_Command_1_Register);
+
+       /* AI_START1_Pulse */
+       win_out(AI_START1_Pulse,AI_Command_2_Register);
+
+       /* XXX hack alert */
+       s->cur_chan=s->cur_trig.n_chan*sizeof(sampl_t);
+
+       win_restore(wsave);
+       return 0;
+}
+
+
+static void ni_ao_fifo_load(comedi_device *dev,comedi_subdevice *s,
+               sampl_t *data,int n)
+{
+       int i;
+
+       for(i=0;i<n;i++){
+               ni_writew(data[i],DAC_FIFO_Data);
+       }
+}
+
+
+/*
+ *  There's a small problem if the FIFO gets really low and we
+ *  don't have the data to fill it.  Basically, if after we fill
+ *  the FIFO with all the data available, the FIFO is _still_
+ *  less than half full, we never clear the interrupt.  If the
+ *  IRQ is in edge mode, we never get another interrupt, because
+ *  this one wasn't cleared.  If in level mode, we get flooded
+ *  with interrupts that we can't fulfill, because nothing ever
+ *  gets put into the buffer.
+ *
+ *  This kind of situation is recoverable, but it is easier to
+ *  just pretend we had a FIFO underrun, since there is a good
+ *  chance it will happen anyway.  This is _not_ the case for
+ *  RT code, as RT code might purposely be running close to the
+ *  metal.  Needs to be fixed eventually.
+ */
+static int ni_ao_fifo_half_empty(comedi_device *dev,comedi_subdevice *s)
+{
+       int n,m;
+
+       n=(s->buf_int_count-s->buf_user_count)/sizeof(sampl_t);
+       if(n==0)return 0;
+       if(n>boardtype.ao_fifo_depth/2)
+               n=boardtype.ao_fifo_depth/2;
+
+       if(s->buf_int_ptr+n*sizeof(sampl_t)>s->cur_trig.data_len){
+               m=(s->cur_trig.data_len-s->buf_int_ptr)/sizeof(sampl_t);
+               ni_ao_fifo_load(dev,s,((void *)(s->cur_trig.data))+s->buf_int_ptr,m);
+               s->buf_int_count+=m*sizeof(sampl_t);
+               s->buf_int_ptr=0;
+               n-=m;
+       }
+       ni_ao_fifo_load(dev,s,((void *)(s->cur_trig.data))+s->buf_int_ptr,n);
+       s->buf_int_count+=n*sizeof(sampl_t);
+       s->buf_int_ptr+=n*sizeof(sampl_t);
+
+       comedi_bufcheck(dev,s);
+
+       return 1;
+}
+
+static int ni_ao_prep_fifo(comedi_device *dev,comedi_subdevice *s)
+{
+       int n;
+
+       /* reset fifo */
+       win_out(0,DAC_FIFO_Clear);
+
+       /* load some data */
+       n=(s->buf_int_count-s->buf_user_count)/sizeof(sampl_t);
+       if(n==0)return 0;
+       if(n>boardtype.ao_fifo_depth)
+               n=boardtype.ao_fifo_depth;
+
+       ni_ao_fifo_load(dev,s,((void *)(s->cur_trig.data))+s->buf_int_ptr,n);
+       s->buf_int_count+=n*sizeof(sampl_t);
+       s->buf_int_ptr+=n*sizeof(sampl_t);
+
+       return n;
+}
+
+
+
+
+
+
+static int ni_ao_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       unsigned int data;
+       unsigned int conf;
+       unsigned int chan;
+       unsigned int range;
+       
+       data=it->data[0];
+       chan=CR_CHAN(it->chanlist[0]);
+
+       conf=chan<<8;
+       
+       /* XXX check range with current range in flaglist[chan] */
+       /* should update calibration if range changes (ick) */
+
+       range = CR_RANGE(it->chanlist[0]);
+       conf |= (range&1);
+       conf |= (range&2)<<1;
+       
+       /* not all boards can deglitch, but this shouldn't hurt */
+       if(it->flags & TRIG_DEGLITCH)
+               conf |= 2;
+
+       /* analog reference */
+       /* AREF_OTHER connects AO ground to AI ground, i think */
+       conf |= (CR_AREF(it->chanlist[0])==AREF_OTHER)? 8 : 0;
+
+       ni_writew(conf,AO_Configuration);
+
+       if(range&1)data^=0x800;
+       
+       ni_writew(data,(chan)? DAC1_Direct_Data : DAC0_Direct_Data);
+       
+       return 1;
+}
+
+static int ni_ao_mode2(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       unsigned int conf;
+       unsigned int chan;
+       unsigned int range;
+       
+       chan=CR_CHAN(it->chanlist[0]);
+
+       conf=chan<<8;
+       
+       /* XXX check range with current range in flaglist[chan] */
+       /* should update calibration if range changes (ick) */
+
+       range = CR_RANGE(it->chanlist[0]);
+       conf |= (range&1);
+       conf |= (range&2)<<1;
+       
+       /* not all boards can deglitch, but this shouldn't hurt */
+       if(it->flags & TRIG_DEGLITCH)
+               conf |= 2;
+
+       /* analog reference */
+       /* AREF_OTHER connects AO ground to AI ground, i think */
+       conf |= (CR_AREF(it->chanlist[0])==AREF_OTHER)? 8 : 0;
+
+       win_out(AO_Disarm,AO_Command_1_Register);
+
+       ni_writew(conf,AO_Configuration);
+
+       /* user is supposed to write() to buffer before triggering */
+       if(ni_ao_prep_fifo(dev,s)==0)
+               return -EIO;
+
+       win_out(AO_Configuration_Start,Joint_Reset_Register);
+
+       devpriv->ao_mode1|=AO_Trigger_Once;
+       win_out(devpriv->ao_mode1,AO_Mode_1_Register);
+       devpriv->ao_trigger_select&=~(AO_START1_Polarity|AO_START1_Select(-1));
+       devpriv->ao_trigger_select|=AO_START1_Edge|AO_START1_Sync;
+       win_out(devpriv->ao_trigger_select,AO_Trigger_Select_Register);
+       devpriv->ao_mode3&=~AO_Trigger_Length;
+       win_out(devpriv->ao_mode3,AO_Mode_3_Register);
+
+       if(it->n==0){
+               devpriv->ao_mode1|=AO_Continuous;
+       }else{
+               devpriv->ao_mode1&=~AO_Continuous;
+       }
+       win_out(devpriv->ao_mode1,AO_Mode_1_Register);
+       devpriv->ao_mode2&=~AO_BC_Initial_Load_Source;
+       win_out(devpriv->ao_mode2,AO_Mode_2_Register);
+       if(it->n==0){
+               win_out(0xff,AO_BC_Load_A_Register_High);
+               win_out(0xffff,AO_BC_Load_A_Register_Low);
+       }else{
+               win_out(0,AO_BC_Load_A_Register_High);
+               win_out(0,AO_BC_Load_A_Register_Low);
+       }
+       win_out(AO_BC_Load,AO_Command_1_Register);
+       devpriv->ao_mode2&=~AO_UC_Initial_Load_Source;
+       win_out(devpriv->ao_mode2,AO_Mode_2_Register);
+       if(it->n==0){
+               win_out(0xff,AO_UC_Load_A_Register_High);
+               win_out(0xffff,AO_UC_Load_A_Register_Low);
+               win_out(AO_UC_Load,AO_Command_1_Register);
+               win_out(0xff,AO_UC_Load_A_Register_High);
+               win_out(0xffff,AO_UC_Load_A_Register_Low);
+       }else{
+               win_out(0,AO_UC_Load_A_Register_High);
+               win_out(0,AO_UC_Load_A_Register_Low);
+               win_out(AO_UC_Load,AO_Command_1_Register);
+               win_out((it->n-1)>>16,AO_UC_Load_A_Register_High);
+               win_out((it->n-1)&0xffff,AO_UC_Load_A_Register_Low);
+       }
+
+       devpriv->ao_cmd2&=~AO_BC_Gate_Enable;
+       ni_writew(devpriv->ao_cmd2,AO_Command_2);
+       devpriv->ao_mode1&=~(AO_UI_Source_Select(0x1f)|AO_UI_Source_Polarity);
+       win_out(devpriv->ao_mode1,AO_Mode_1_Register);
+       devpriv->ao_mode2&=~(AO_UI_Reload_Mode(3)|AO_UI_Initial_Load_Source);
+       win_out(devpriv->ao_mode2,AO_Mode_2_Register);
+       win_out(0,AO_UI_Load_A_Register_High);
+       win_out(1,AO_UI_Load_A_Register_Low);
+       win_out(AO_UI_Load,AO_Command_1_Register);
+       win_out((it->trigvar>>16),AO_UI_Load_A_Register_High);
+       win_out((it->trigvar&0xffff),AO_UI_Load_A_Register_Low);
+
+       devpriv->ao_mode1&=~AO_Multiple_Channels;
+       win_out(devpriv->ao_mode1,AO_Mode_1_Register);
+       win_out(AO_UPDATE_Output_Select(1),AO_Output_Control_Register);
+
+       win_out(AO_DAC0_Update_Mode|AO_DAC1_Update_Mode,AO_Command_1_Register);
+
+       devpriv->ao_mode3|=AO_Stop_On_Overrun_Error;
+       win_out(devpriv->ao_mode3,AO_Mode_3_Register);
+
+devpriv->ao_mode2|=AO_FIFO_Mode(1);
+       devpriv->ao_mode2&=~AO_FIFO_Retransmit_Enable;
+       win_out(devpriv->ao_mode2,AO_Mode_2_Register);
+
+       win_out(AO_Configuration_End,Joint_Reset_Register);
+
+       win_out(devpriv->ao_mode3|AO_Not_An_UPDATE,AO_Mode_3_Register);
+       win_out(devpriv->ao_mode3,AO_Mode_3_Register);
+
+       /* wait for DACs to be loaded */
+       udelay(100);
+
+       win_out(devpriv->ao_cmd1|AO_UI_Arm|AO_UC_Arm|AO_BC_Arm|AO_DAC1_Update_Mode|AO_DAC0_Update_Mode,
+               AO_Command_1_Register);
+
+       win_out(AO_FIFO_Interrupt_Enable|AO_Error_Interrupt_Enable,Interrupt_B_Enable_Register);
+
+
+       ni_writew(devpriv->ao_cmd2|AO_START1_Pulse,AO_Command_2);
+       
+       return 0;
+}
+
+
+
+/*
+       digital i/o
+
+*/
+static int ni_dio(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int data,mask;
+       int i;
+       int temp;
+
+       /* rt stuff */
+       temp=win_save();
+
+       if(it->flags & TRIG_CONFIG){
+               data=s->io_bits;
+               for(i=0;i<it->n_chan;i++){
+                       mask=1<<CR_CHAN(it->chanlist[i]);
+                       data&= ~mask;
+                       if(it->data[i])
+                               data|=mask;
+               }
+               s->io_bits=data;
+               win_out(s->io_bits,DIO_Control_Register);
+       }else{
+               if(it->flags & TRIG_WRITE){
+                       do_pack(&s->state,it);
+                       win_out(s->state,DIO_Output_Register);
+               }else{
+                       data=win_in(DIO_Input_Register);
+                       di_unpack(data,it);
+               }
+       }
+
+       win_restore(temp);
+
+       return it->n_chan;
+}
+
+
+
+
+static int ni_E_init(comedi_device *dev,comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       
+       dev->n_subdevices=7;
+       
+       if(alloc_subdevices(dev)<0)
+               return -ENOMEM;
+       
+       /* analog input subdevice */
+
+       s=dev->subdevices+0;
+       s->type=COMEDI_SUBD_AI;
+       s->subdev_flags=SDF_READABLE|SDF_RT|SDF_GROUND|SDF_COMMON|SDF_DIFF|SDF_OTHER;
+       s->subdev_flags|=SDF_DITHER;
+       s->n_chan=boardtype.n_adchan;
+       s->len_chanlist=512;    /* XXX is this the same for PCI-MIO ? */
+       s->maxdata=(1<<boardtype.adbits)-1;
+       s->timer_type=TIMER_atmio;
+       s->range_type=ni_range_lkup[boardtype.gainlkup];
+       s->trig[0]=ni_ai_mode0;
+       s->trig[2]=ni_ai_mode2;
+       s->trig[4]=ni_ai_mode4;
+       s->cancel=ni_ai_reset;
+       s->do_lock=ni_ai_lock;
+       s->do_unlock=ni_ai_unlock;
+       
+       /* analog output subdevice */
+       /* XXX what about boards without ao? */
+
+       s=dev->subdevices+1;
+       if(boardtype.n_aochan){
+               s->type=COMEDI_SUBD_AO;
+               s->subdev_flags=SDF_WRITEABLE|SDF_RT|SDF_DEGLITCH|SDF_GROUND|SDF_OTHER;
+               s->n_chan=boardtype.n_aochan;
+               s->maxdata=(1<<boardtype.aobits)-1;
+               s->timer_type=TIMER_atmio;
+               s->range_type=RANGE_ni_E_ao_ext;        /* XXX wrong for some boards */
+               s->trig[0]=ni_ao_mode0;
+               if(boardtype.ao_fifo_depth)
+                       s->trig[2]=ni_ao_mode2;
+       }else{
+               s->type=COMEDI_SUBD_UNUSED;
+       }
+       
+       /* digital i/o subdevice */
+       
+       s=dev->subdevices+2;
+       s->type=COMEDI_SUBD_DIO;
+       s->subdev_flags=SDF_WRITEABLE|SDF_READABLE|SDF_RT;
+       s->n_chan=8;
+       s->maxdata=1;
+       s->range_type=RANGE_digital;
+       s->io_bits=0;           /* all bits input */
+       s->trig[0]=ni_dio;
+
+       /* dio setup */
+       win_out(s->io_bits,DIO_Control_Register);
+       
+       /* 8255 device */
+       s=dev->subdevices+3;
+       if(boardtype.has_8255){
+               subdev_8255_init(dev,s,ni_8255_callback,dev);
+       }else{
+               s->type=COMEDI_SUBD_UNUSED;
+               s->trig[0]=NULL;
+       }
+       /* XXX */
+       
+       /* timer/counter device */
+       s=dev->subdevices+4;
+       s->type=COMEDI_SUBD_UNUSED;
+       s->trig[0]=NULL;
+       /* XXX */
+       
+       /* calibration subdevice -- ai and ao */
+       s=dev->subdevices+5;
+       s->type=COMEDI_SUBD_CALIB;
+       s->subdev_flags=SDF_WRITEABLE|SDF_INTERNAL;
+       caldac_setup(dev,s);
+       s->trig[0]=ni_calib;
+       
+       /* EEPROM */
+       s=dev->subdevices+6;
+       s->type=COMEDI_SUBD_MEMORY;
+       s->subdev_flags=SDF_READABLE|SDF_INTERNAL;
+       s->n_chan=512;
+       s->maxdata=0xff;
+       s->trig[0]=ni_eeprom;
+       
+       /* ai configuration */
+       ni_ai_reset(dev,dev->subdevices+0);
+       win_out(0x1ba0,Clock_and_FOUT_Register);
+
+
+       /* analog output configuration */
+       
+       devpriv->ao0p=0x0000;
+       ni_writew(devpriv->ao0p,AO_Configuration);
+       devpriv->ao1p=0x0100;
+       ni_writew(devpriv->ao1p,AO_Configuration);
+       win_out(AO_Configuration_Start,Joint_Reset_Register);
+       win_out(AO_Disarm,AO_Command_1_Register);
+       win_out(0,Interrupt_B_Enable_Register);
+       win_out(0x0010,AO_Personal_Register);
+       ni_writew(0x3f98,Interrupt_B_Ack);
+       win_out(0x1430,AO_Personal_Register);
+       win_out(0,AO_Output_Control_Register);
+       win_out(0,AO_Start_Select_Register);
+       devpriv->ao_cmd1=0;
+       win_out(devpriv->ao_cmd1,AO_Command_1_Register);
+       devpriv->ao_cmd2=0;
+       devpriv->ao_mode1=0;
+       devpriv->ao_mode2=0;
+       devpriv->ao_mode3=0;
+       devpriv->ao_trigger_select=0;
+
+        if(dev->irq){
+                win_out((IRQ_POLARITY<<0) |  /* polarity : active high */
+                        (0<<1) |  /* no interrupt on 3 pins */
+                        (1<<11) |  /* enable int A */
+                        (1<<15) |  /* enable int B */
+                        (interrupt_pin(dev->irq)<<8) |  /* interrupt output pin A */
+                        (interrupt_pin(dev->irq)<<12) ,  /* interrupt output pin B */
+                        Interrupt_Control_Register
+                );
+        }
+
+       printk("\n");
+
+       return 0;
+}
+
+
+
+
+/*
+       presents the EEPROM as a subdevice
+*/
+
+static int ni_eeprom(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int i;
+       
+       for(i=0;i<it->n_chan;i++)
+               it->data[i]=ni_read_eeprom(dev,CR_CHAN(it->chanlist[i]));
+
+       return i;
+}
+
+/*
+       reads bytes out of eeprom
+*/
+
+static int ni_read_eeprom(comedi_device *dev,int addr)
+{
+       int bit;
+       int bitstring;
+
+       bitstring=0x0300|((addr&0x100)<<3)|(addr&0xff);
+       ni_writeb_p(0x04,Serial_Command);
+       for(bit=0x8000;bit;bit>>=1){
+               ni_writeb_p(0x04|((bit&bitstring)?0x02:0),Serial_Command);
+               ni_writeb_p(0x05|((bit&bitstring)?0x02:0),Serial_Command);
+       }
+       bitstring=0;
+       for(bit=0x80;bit;bit>>=1){
+               ni_writeb_p(0x04,Serial_Command);
+               ni_writeb_p(0x05,Serial_Command);
+               bitstring|=((ni_readb_p(Status)&0x01)?bit:0);
+       }
+       ni_writeb_p(0x00,Serial_Command);
+       
+       return bitstring;
+}
+
+static void ni_write_caldac(comedi_device *dev,int addr,int val);
+/*
+       calibration subdevice
+*/
+static int ni_calib(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       ni_write_caldac(dev,CR_CHAN(it->chanlist[0]),it->data[0]);
+
+       return 1;
+}
+
+static int pack_mb88341(int addr,int val,int *bitstring);
+static int pack_dac8800(int addr,int val,int *bitstring);
+static int pack_dac8043(int addr,int val,int *bitstring);
+static int pack_ad8522(int addr,int val,int *bitstring);
+
+struct caldac_struct{
+       int n_chans;
+       int n_bits;
+       int (*packbits)(int,int,int *);
+};
+
+static struct caldac_struct caldac_mb88341={ 12, 8, pack_mb88341 };
+static struct caldac_struct caldac_dac8800={ 8, 8, pack_dac8800 };
+static struct caldac_struct caldac_dac8043={ 1, 12, pack_dac8043 };
+static struct caldac_struct caldac_ad8522={ 2, 12, pack_ad8522 };
+
+
+static void caldac_setup(comedi_device *dev,comedi_subdevice *s)
+{
+       int i,j;
+       int n_dacs;
+       int n_chans=0;
+       int n_bits;
+       int diffbits=0;
+       
+       if(!boardtype.caldac[0])return;
+       n_bits=boardtype.caldac[0]->n_bits;
+       for(i=0;i<3;i++){
+               if(!boardtype.caldac[i])break;
+               if(boardtype.caldac[i]->n_bits!=n_bits)diffbits=1;
+               n_chans+=boardtype.caldac[i]->n_chans;
+       }
+       n_dacs=i;
+       s->n_chan=n_chans;
+
+       if(diffbits){
+               int chan;
+
+               s->maxdata_list=kmalloc(sizeof(int)*n_chans,GFP_KERNEL);
+               chan=0;
+               for(i=0;i<n_dacs;i++){
+                       for(j=0;j<boardtype.caldac[i]->n_chans;j++){
+                               s->maxdata_list[chan]=
+                                       (1<<boardtype.caldac[i]->n_bits)-1;
+                               chan++;
+                       }
+               }
+       }else{
+               s->maxdata=(1<<boardtype.caldac[0]->n_bits)-1;
+       }
+}
+
+static void ni_write_caldac(comedi_device *dev,int addr,int val)
+{
+       int loadbit=0,bits=0,bit,bitstring=0;
+       int i;
+       
+       for(i=0;i<3;i++){
+               if(!boardtype.caldac[i])return;
+               if(addr<boardtype.caldac[i]->n_chans){
+                       bits=boardtype.caldac[i]->packbits(addr,val,&bitstring);
+                       loadbit=SerDacLd(i);
+                       break;
+               }
+               addr-=boardtype.caldac[i]->n_chans;
+       }
+
+       for(bit=1<<(bits-1);bit;bit>>=1){
+               ni_writeb(((bit&bitstring)?0x02:0),Serial_Command);
+               ni_writeb(1|((bit&bitstring)?0x02:0),Serial_Command);
+       }
+       ni_writeb(loadbit,Serial_Command);
+       ni_writeb(0,Serial_Command);
+}
+
+
+
+static int pack_mb88341(int addr,int val,int *bitstring)
+{
+       /*
+          Fujitsu MB 88341
+          Note that address bits are reversed.  Thanks to
+          Ingo Keen for noticing this.
+
+          Note also that the 88341 expects address values from
+          1-12, whereas we use channel numbers 0-11.  The NI
+          docs use 1-12, also, so be careful here.
+       */
+       addr++;
+       *bitstring=((addr&0x1)<<11) |
+                 ((addr&0x2)<<9)  |
+                 ((addr&0x4)<<7)  |
+                 ((addr&0x8)<<5)  |
+                 (val&0xff);
+       return 12;
+}
+
+static int pack_dac8800(int addr,int val,int *bitstring)
+{
+       *bitstring=((addr&0x7)<<8)|(val&0xff);
+       return 11;
+}
+
+static int pack_dac8043(int addr,int val,int *bitstring)
+{
+       *bitstring=val&0xfff;
+       return 12;
+}
+       
+static int pack_ad8522(int addr,int val,int *bitstring)
+{
+       *bitstring=(val&0xfff)|(addr ? 0xc000:0xa000);
+       return 16;
+}
+
+
+
+/*
+ *
+ *  Programmable Function Inputs
+ *
+ */
+
+#if 0
+
+static void pfi_setup(comedi_device *dev)
+{
+       /* currently, we don't output any signals, thus, all
+          the PFI's are input */
+       
+       win_out(IO_Bidirection_Pin_Register,0);
+}
+
+
+
+/*
+ *
+ *  General Purpose Counter/Timer section
+ *
+ */
+
+
+/* event counting */
+
+int gpct_start(comedi_device *dev,int chan)
+{
+       /* select load source */
+       Gi_Load_Source_Select = 0;
+       
+       /* load initial counter value */
+       Gi_Load_A = 0;
+       
+       /* strobe */
+       Gi_Load=1;
+
+/* command reg */
+       Gi_Up_Down = 1; /* up counting */
+       Gi_Bank_Switch_Enable = 0;
+       Gi_Bank_Switch_Mode = 0;
+
+/* input select reg */
+       Gi_Source_Select = PFIn;
+       Gi_Source_Polarity = 0; /* rising edges */
+       Gi_Gate_Select = 31; /* logic low */
+       Gi_OR_Gate = 0;
+       Gi_Output_Polarity = 1; /* active low */
+       Gi_Gate_Select_Load_Source = 0;
+
+/* mode reg */
+       Gi_Gate_Polarity = 0; /* disable inversion */
+       Gi_Loading_On_Gate = 0;
+       Gi_Output_Mode = 1; /* one clock cycle output */
+       Gi_Loading_On_TC = 0;
+       Gi_Gating_Mode = 1;
+       Gi_Gate_On_Both_Edges = 0;
+       Gi_Trigger_Mode_For_Edge_Gate = 2;
+       Gi_Stop_Mode = 0;
+       Gi_Counting_Once = 0;
+       
+       
+/* int enable reg -- ignore */
+       
+/*
+       Gi_TC_Interrupt_Enable = 0;
+       Gi_Gate_Interrupt_Enable = 0;
+*/
+}
+
+static int gpct_sp(comedi_device *dev,comedi_param *it)
+{
+       switch(it->pnum){
+       case COMEDI_CTR_INPUT:
+       {
+               /* which input source? */
+               int pval=it->pval;
+               
+               if(pval<0 || pval>=10)return -EINVAL;
+               changeparam(&dev->chinfo[it->chan].paramlist,COMEDI_CTR_INPUT,
+                       0,pval);
+               /* XXX set it */
+               return 0;
+       }
+       case COMEDI_CTR_POLARITY:
+               /* count rising or falling edges? */
+               if(it->pval<0 || it->pval>=4)return -EINVAL;
+               changeparam(&dev->chinfo[it->chan].paramlist,COMEDI_AREF,
+                       0,it->pval);
+               /* XXX set it */
+               return 0;
+       case COMEDI_COUNT:
+               changeparam(&dev->chinfo[it->chan].paramlist,COMEDI_COUNT,
+                       0,it->pval);  /* irrelevant... */
+               devpriv->gpct[it->chan-boardtype->counteroffset]=it->pval;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+#endif
+
+static int ni_8255_callback(int dir,int port,int data,void *arg)
+{
+       comedi_device *dev=arg;
+
+       if(dir){
+               ni_writeb(25+2*port,data);
+               return 0;
+       }else{
+               return ni_readb(25+2*port);
+       }
+}
+
diff --git a/comedi/drivers/ni_pcidio.c b/comedi/drivers/ni_pcidio.c
new file mode 100644 (file)
index 0000000..3edb64a
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+    module/ni-dio.c
+    driver for National Instruments PCI-DIO-96
+               National Instruments PCI-DIO-32HS
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1999 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.
+
+*/
+
+/*
+   This driver is for both the NI PCI-DIO-32HS and the PCI-DIO-96,
+   which have very different architectures.  But, since the '96 is
+   so simple, it is included here.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <comedi_module.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <asm/irq.h>
+#include <mite.h>
+#include <8255.h>
+
+/* general debugging messages */
+#define DEBUG
+
+
+#define PCI_VENDOR_ID_NATINST  0x1093
+
+#define PCI_DIO_SIZE 4096
+#define PCI_MITE_SIZE 4096
+
+/* defines for the PCI-DIO-96 */
+
+#define NIDIO_A 0
+#define NIDIO_B 4
+#define NIDIO_C 8
+#define NIDIO_D 12
+
+/* defines for the PCI-DIO-32HS */
+
+#define Chip_ID_D                      24
+#define Chip_ID_I                      25
+#define Chip_ID_O                      26
+#define Chip_Version                   27
+
+#define Port_IO(x)                     (28+(x))
+#define Port_Pin_Directions(x)         (32+(x))
+#define Port_Pin_Mask(x)               (36+(x))
+#define Port_Pin_Polarities(x)         (40+(x))
+#define Port_Pattern(x)                        (48+(x))
+
+#define Master_DMA_And_Interrupt_Control 5
+#define InterruptLine                          3
+#define OpenInt                                        4
+
+#define Interrupt_And_Window_Status    4
+#define Group_Status                   5
+#define DataLeft                               (1<<0)
+#define Req                                    (1<<2)
+#define StopTrig                               (1<<3)
+
+#define Group_Flags                    6
+#define TransferReady                          (1<<0)
+#define CountExpired                           (1<<1)
+#define Waited                                 (1<<5)
+#define PrimaryTC                              (1<<6)
+#define SecondaryTC                            (1<<7)
+
+#define Group_First_Clear              6
+#define ClearWaited                            (1<<3)
+#define ClearPrimaryTC                         (1<<4)
+#define ClearSecondaryTC                       (1<<5)
+#define DMAReset                               (1<<6)
+#define FIFOReset                              (1<<7)
+#define ClearAll                               0xf8
+
+#define Group_FIFO                     8
+#define Transfer_Count                 20
+#define Data_Path                      64
+#define FIFO_Control                   72
+#define Interrupt_Enable               75
+#define DMA_Line_Control               76
+#define Transfer_Size_Control          77
+
+#define Protocol_Register_1            65
+#define Protocol_Register_2            66
+#define Protocol_Register_3            67
+#define Protocol_Register_4            70
+#define Protocol_Register_5            71
+#define Protocol_Register_6            73
+#define Protocol_Register_7            74
+#define Protocol_Register_8            88
+#define Protocol_Register_9            82
+#define Protocol_Register_10           83
+#define Protocol_Register_11           84
+#define Protocol_Register_12           85
+#define Protocol_Register_13           86
+#define Protocol_Register_14           68
+#define Protocol_Register_15           79
+
+#define OpMode                         Protocol_Register_1
+#define ClockReg                       Protocol_Register_2
+#define Sequence                       Protocol_Register_3
+#define ReqReg                         Protocol_Register_4
+#define BlockMode                      Protocol_Register_5
+#define LinePolarities                 Protocol_Register_6
+#define AckSer                         Protocol_Register_7
+#define StartDelay                     Protocol_Register_8
+#define ReqDelay                       Protocol_Register_9
+#define ReqNotDelay                    Protocol_Register_10
+#define AckDelay                       Protocol_Register_11
+#define AckNotDelay                    Protocol_Register_12
+#define Data1Delay                     Protocol_Register_13
+#define ClockSpeed                     Protocol_Register_14
+#define DAQOptions                     Protocol_Register_15
+
+static int nidio_attach(comedi_device *dev,comedi_devconfig *it);
+static int nidio_detach(comedi_device *dev);
+comedi_driver driver_pcidio={
+       driver_name:    "nidio",
+       module:         &__this_module,
+       attach:         nidio_attach,
+       detach:         nidio_detach,
+};
+
+typedef struct{
+       int dev_id;
+       char *name;
+       int n_8255;
+       int is_diodaq;
+}nidio_board;
+static nidio_board nidio_boards[]={
+       {
+       dev_id:         0x1150,
+       name:           "pci-dio-32hs",
+       n_8255:         0,
+       is_diodaq:      1,
+       },
+       {
+       dev_id:         0x0160,
+       name:           "pci-dio-96",
+       n_8255:         4,
+       is_diodaq:      0,
+       },
+};
+#define n_nidio_boards 2
+
+typedef struct{
+       struct mite_struct *mite;
+       int boardtype;
+       int dio;
+       int aip[16];
+}nidio96_private;
+#define devpriv ((nidio96_private *)dev->private)
+
+static int nidio_find_device(comedi_device *dev,comedi_devconfig *it);
+#if 0
+static int setup_mite(comedi_device *dev);
+#endif
+
+
+static int nidio96_8255_cb(int dir,int port,int data,void *arg)
+{
+       int iobase=(int)arg;
+
+       if(dir){
+               writeb(data,iobase+port);
+               return 0;
+       }else{
+               return readb(iobase+port);
+       }
+}
+
+static void nidio_interrupt(int irq, void *d, struct pt_regs *regs)
+{
+       comedi_device *dev=d;
+       //comedi_subdevice *s=dev->subdevices;
+       int a,b;
+       static int n_int=0;
+       //int i;
+       struct timeval tv;
+
+       do_gettimeofday(&tv);
+       a=readb(dev->iobase+Group_Status);
+       b=readb(dev->iobase+Group_Flags);
+       printk("status 0x%02x flags 0x%02x time %06d\n",a,b,(int)tv.tv_usec);
+
+       while(b&1){
+               writew(0xff,dev->iobase+Group_FIFO);
+               b=readb(dev->iobase+Group_Flags);
+       }
+
+       b=readb(dev->iobase+Group_Flags);
+       printk("new status 0x%02x\n",b);
+
+       n_int++;
+       if(n_int==10)
+               disable_irq(dev->irq);
+}
+
+static int nidio_dio(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       if(it->flags & TRIG_CONFIG){
+               do_pack(&s->io_bits,it);
+               writel(s->io_bits,dev->iobase+Port_Pin_Directions(0));
+       }else{
+               if(it->flags&TRIG_WRITE){
+                       do_pack(&s->state,it);
+                       writel(s->state,dev->iobase+Port_IO(0));
+               }else{
+                       unsigned int data;
+
+                       data=readl(dev->iobase+Port_IO(0));
+                       di_unpack(data,it);
+               }
+       }
+
+       return it->n_chan;
+}
+
+static int nidio_dio_mode2(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int i;
+
+       writeb(  0 ,dev->iobase+OpMode);
+
+       writel(0xffff,dev->iobase+Port_Pin_Directions(0));
+
+       /* choose chan A,B */
+       writeb(0x83,dev->iobase+Data_Path);
+
+       /* set transfer width */
+       writeb(0x03,dev->iobase+Transfer_Size_Control);
+
+#if 0
+       /* protocol configuration */
+       writeb(0x10,dev->iobase+ClockReg);
+       writeb(  0 ,dev->iobase+Sequence);
+       writeb(0x20,dev->iobase+ReqReg);
+       writeb(  4 ,dev->iobase+BlockMode);
+       writeb(  0 ,dev->iobase+LinePolarities);
+       writeb(0x60,dev->iobase+AckSer);
+       writeb(  1 ,dev->iobase+StartDelay);
+       writeb(  1 ,dev->iobase+ReqDelay);
+       writeb(  1 ,dev->iobase+ReqNotDelay);
+       writeb(  1 ,dev->iobase+AckDelay);
+       writeb(  1 ,dev->iobase+AckNotDelay);
+       writeb(  1 ,dev->iobase+Data1Delay);
+       writew(  0 ,dev->iobase+ClockSpeed);
+       writeb(  0 ,dev->iobase+DAQOptions);
+#else
+       /* protocol configuration */
+       writeb(  0 ,dev->iobase+ClockReg);
+       writeb(  1 ,dev->iobase+Sequence);
+       writeb(  4 ,dev->iobase+ReqReg);
+       writeb(  0 ,dev->iobase+BlockMode);
+       writeb(  3 ,dev->iobase+LinePolarities);
+       writeb(224 ,dev->iobase+AckSer);
+       writeb(100 ,dev->iobase+StartDelay);
+       writeb(  1 ,dev->iobase+ReqDelay);
+       writeb(  1 ,dev->iobase+ReqNotDelay);
+       writeb(  1 ,dev->iobase+AckDelay);
+       writeb( 11 ,dev->iobase+AckNotDelay);
+       writeb(  1 ,dev->iobase+Data1Delay);
+       writew(1000,dev->iobase+ClockSpeed);
+       writeb(100 ,dev->iobase+DAQOptions);
+#endif
+
+       /* ReadyLevel */
+       writeb(  0 ,dev->iobase+FIFO_Control);
+
+       for(i=0;i<16;i++){
+               writew(i,dev->iobase+Group_FIFO);
+       }
+
+       writel(10000 ,dev->iobase+Transfer_Count);
+
+#define USEDMA 0
+       if(USEDMA){
+               writeb(0x05,dev->iobase+DMA_Line_Control);
+               writeb(0x30,dev->iobase+Group_First_Clear);
+
+               mite_dma_prep(devpriv->mite,s);
+       }else{
+               writeb(0x00,dev->iobase+DMA_Line_Control);
+       }
+
+       /* clear and enable interrupts */
+       writeb(0xff,dev->iobase+Group_First_Clear);
+       writeb(0xc3,dev->iobase+Interrupt_Enable);
+       writeb(0x03,dev->iobase+Master_DMA_And_Interrupt_Control);
+
+       /* start */
+
+       writeb(0x0f,dev->iobase+OpMode);
+
+       return 0;
+}
+
+static int nidio_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       int ret;
+       
+       printk("comedi%d: nidio:",dev->minor);
+
+       if((ret=alloc_private(dev,sizeof(nidio96_private)))<0)
+               return ret;
+       
+       ret=nidio_find_device(dev,it);
+       if(ret<0)return ret;
+
+       dev->iobase=mite_setup(devpriv->mite);
+
+       dev->board_name=nidio_boards[dev->board].name;
+       dev->irq=mite_irq(devpriv->mite);
+       printk(" %s",dev->board_name);
+
+       if(!nidio_boards[dev->board].is_diodaq){
+               dev->n_subdevices=4;
+       }else{
+               dev->n_subdevices=1;
+       }
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+
+       if(!nidio_boards[dev->board].is_diodaq){
+               subdev_8255_init(dev,dev->subdevices+0,nidio96_8255_cb,(void *)(dev->iobase+NIDIO_A));
+               subdev_8255_init(dev,dev->subdevices+1,nidio96_8255_cb,(void *)(dev->iobase+NIDIO_B));
+               subdev_8255_init(dev,dev->subdevices+2,nidio96_8255_cb,(void *)(dev->iobase+NIDIO_C));
+               subdev_8255_init(dev,dev->subdevices+3,nidio96_8255_cb,(void *)(dev->iobase+NIDIO_D));
+       }else{
+
+               printk(" rev=%d",readb(dev->iobase+Chip_Version));
+       
+               s=dev->subdevices+0;
+
+               s->type=COMEDI_SUBD_DIO;
+               s->subdev_flags=SDF_READABLE|SDF_WRITEABLE|SDF_RT;
+               s->n_chan=32;
+               s->range_type=RANGE_digital;
+               s->maxdata=1;
+               s->trig[0]=nidio_dio;
+               s->trig[2]=nidio_dio_mode2;
+               s->timer_type=TIMER_nanosec;
+               s->len_chanlist=32;             /* XXX */
+
+               writel(0,dev->iobase+Port_IO(0));
+               writel(0,dev->iobase+Port_Pin_Directions(0));
+               writel(0,dev->iobase+Port_Pin_Mask(0));
+
+               /* disable interrupts on board */
+               writeb(0x00,dev->iobase+Master_DMA_And_Interrupt_Control);
+
+               ret=comedi_request_irq(dev->irq,nidio_interrupt,0,"nidio",dev);
+               if(ret<0){
+                       dev->irq=0;
+                       printk(" irq not available");
+               }
+       }
+
+       printk("\n");
+
+       return 0;
+}
+
+static int nidio_detach(comedi_device *dev)
+{
+       if(dev->irq)
+               comedi_free_irq(dev->irq,dev);
+
+       if(devpriv && devpriv->mite)
+               mite_unsetup(devpriv->mite);
+
+       return 0;
+}
+
+
+static int nidio_find_device(comedi_device *dev,comedi_devconfig *it)
+{
+       struct mite_struct *mite;
+       int i;
+       
+       for(mite=mite_devices;mite;mite=mite->next){
+               for(i=0;i<n_nidio_boards;i++){
+                       if(mite_device_id(mite)==nidio_boards[i].dev_id){
+                               dev->board=i;
+                               devpriv->mite=mite;
+
+                               return 0;
+                       }
+               }
+       }
+       printk("no device found\n");
+       return -EIO;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_pcidio);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_pcidio);
+}
+#endif
diff --git a/comedi/drivers/ni_pcimio.c b/comedi/drivers/ni_pcimio.c
new file mode 100644 (file)
index 0000000..12f1c56
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+    module/pcimio-E.c
+    Hardware driver for NI PCI-MIO E series cards
+
+    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.
+
+*/
+
+/*
+       The PCI-MIO E series driver was originally written by
+       Tomasz Motylewski <...>, and ported to comedi by ds.
+
+
+       References for specifications:
+       
+          321747b.pdf  Register Level Programmer Manual (obsolete)
+          321747c.pdf  Register Level Programmer Manual (new)
+          DAQ-STC reference manual
+
+       Other possibly relevant info:
+       
+          320517c.pdf  User manual (obsolete)
+          320517f.pdf  User manual (new)
+          320889a.pdf  delete
+          320906c.pdf  maximum signal ratings
+          321066a.pdf  about 16x
+          321791a.pdf  discontinuation of at-mio-16e-10 rev. c
+          321808a.pdf  about at-mio-16e-10 rev P
+          321837a.pdf  discontinuation of at-mio-16de-10 rev d
+          321838a.pdf  about at-mio-16de-10 rev N
+       
+       ISSUES:
+
+       need to deal with external reference for DAC, and other DAC
+       properties in board properties
+       
+       deal with at-mio-16de-10 revision D to N changes, etc.
+       
+       need to add other CALDAC type
+       
+       need to slow down DAC loading.  I don't trust NI's claim that
+       two writes to the PCI bus slows IO enough.  I would prefer to
+       use udelay().  Timing specs: (clock)
+               AD8522          30ns
+               DAC8043         120ns
+               DAC8800         60ns
+               MB88341         ?
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <linux/malloc.h>
+#include <comedi_module.h>
+#include <ni_stc.h>
+#include <mite.h>
+
+#undef DEBUG
+#define PCI_DEBUG
+
+
+#define PCIMIO 1
+#undef ATMIO
+
+static struct caldac_struct *type1[]={&caldac_dac8800,&caldac_dac8043,NULL};
+static struct caldac_struct *type2[]={&caldac_dac8800,&caldac_dac8043,&caldac_ad8522};
+static struct caldac_struct *type3[]={&caldac_mb88341,NULL,NULL};
+static struct caldac_struct *type4[]={&caldac_mb88341,&caldac_mb88341,&caldac_ad8522};
+
+
+static ni_board ni_boards[]={
+       {       device_id:      0x0162,
+               name:           "pci-mio-16xe-50",
+               n_adchan:       16,
+               adbits:         16,
+               ai_fifo_depth:  2048,
+               alwaysdither:   1,
+               gainlkup:       ai_gain_8,
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  0,
+               ao_unipolar:    0,
+               caldac:         type1,
+               has_8255:       0,
+       },
+       {       device_id:      0x1170,
+               name:           "pci-mio-16xe-10",
+               n_adchan:       16,
+               adbits:         16,
+               ai_fifo_depth:  512,
+               alwaysdither:   1,
+               gainlkup:       ai_gain_14,
+               n_aochan:       2,
+               aobits:         16,
+               ao_fifo_depth:  2048,
+               ao_unipolar:    1,
+               caldac:         type2,
+               has_8255:       0,
+       },
+       {       device_id:      0x1180,
+               name:           "pci-mio-16e-1",
+               n_adchan:       16,
+               adbits:         12,
+               ai_fifo_depth:  512,
+               alwaysdither:   0,
+               gainlkup:       ai_gain_16,
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  2048,
+               ao_unipolar:    1,
+               caldac:         type3,
+               has_8255:       0,
+       },
+       {       device_id:      0x1190,
+               name:           "pci-mio-16e-4",
+               n_adchan:       16,
+               adbits:         12,
+               ai_fifo_depth:  512,
+               alwaysdither:   0,
+               gainlkup:       ai_gain_16,
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  512,
+               ao_unipolar:    1,
+               caldac:         type3,
+               has_8255:       0,
+       },
+       {       device_id:      0x1330,
+               name:           "pci-6031e",
+               n_adchan:       64,
+               adbits:         16,
+               ai_fifo_depth:  512,
+               alwaysdither:   1,
+               gainlkup:       ai_gain_14,
+               n_aochan:       2,
+               aobits:         16,
+               ao_fifo_depth:  2048,
+               ao_unipolar:    1,
+               caldac:         type2,
+               has_8255:       0,
+       },
+#if 0
+       {       device_id:      0x0000,         /* unknown */
+               name:           "pci-6032e",
+               n_adchan:       16,
+               adbits:         16,
+               ai_fifo_depth:  512,
+               alwaysdither:   1,
+               gainlkup:       ai_gain_14,
+               n_aochan:       0,
+               aobits:         0,
+               ao_fifo_depth:  0,
+               ao_unipolar:    1,
+               caldac:         type2,
+               has_8255:       0,
+       },
+#endif
+#if 0
+       {       device_id:      0x0000,         /* unknown */
+               name:           "pci-6033e",
+               n_adchan:       64,
+               adbits:         16,
+               ai_fifo_depth:  512,
+               alwaysdither:   1,
+               gainlkup:       ai_gain_14,
+               n_aochan:       0,
+               aobits:         0,
+               ao_fifo_depth:  0,
+               ao_unipolar:    1,
+               caldac:         type2,
+               has_8255:       0,
+       },
+#endif
+       {       device_id:      0x1350,
+               name:           "pci-6071e",
+               n_adchan:       64,
+               adbits:         12,
+               ai_fifo_depth:  512,
+               alwaysdither:   1,
+               gainlkup:       ai_gain_16,
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  2048,
+               ao_unipolar:    1,
+               caldac:         type3,
+               has_8255:       0,
+       },
+       {       device_id:      0x2a60,
+               name:           "pci-6023e",
+               n_adchan:       16,
+               adbits:         12,
+               ai_fifo_depth:  512,
+               alwaysdither:   0,
+               n_aochan:       0,
+               aobits:         0,
+               ao_unipolar:    0,
+               gainlkup:       ai_gain_8_602x,
+               caldac:         type3,
+               has_8255:       0,
+       },
+       {       device_id:      0x2a70,
+               name:           "pci-6024e",
+               n_adchan:       16,
+               adbits:         12,
+               ai_fifo_depth:  512,
+               alwaysdither:   0,
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  0,
+               ao_unipolar:    1,
+               gainlkup:       ai_gain_8_602x,
+               caldac:         type3,
+               has_8255:       0,
+       },
+#if 0
+       {       device_id:      0x0000,         /* unknown */
+               name:           "pci-6025e",
+               n_adchan:       16,
+               adbits:         12,
+               ai_fifo_depth:  512,
+               alwaysdither:   0,
+               n_aochan:       2,
+               aobits:         12,
+               ao_fifo_depth:  0,
+               ao_unipolar:    1,
+               gainlkup:       ai_gain_8_602x,
+               caldac:         type3,
+               has_8255:       1,
+       },
+       {       device_id:      0x0000,         /* unknown */
+               name:           "pci-6052e",
+               n_adchan:       16,
+               adbits:         16,
+               ai_fifo_depth:  512,
+               alwaysdither:   1,
+               aobits:         16,
+               ao_unipolar:    1,
+               ao_fifo_depth:  2048,
+               caldac:         type4,
+               gainlkup:       ai_gain_16,
+       },
+       {       device_id:      0x0000,         /* unknown */
+               name:           "pci-6110e",
+       },
+       {       device_id:      0x0000,         /* unknown */
+               name:           "pci-6111e",
+       },
+       {       device_id:      0x0000,         /* unknown */
+               name:           "pci-6040e",
+       },
+       {       device_id:      0x0000,         /* unknown */
+               name:           "pci-6041e",
+       },
+#endif
+};
+#define n_pcimio_boards ((sizeof(ni_boards)/sizeof(ni_boards[0])))
+
+static int pcimio_attach(comedi_device *dev,comedi_devconfig *it);
+static int pcimio_detach(comedi_device *dev);
+comedi_driver driver_pcimio={
+       driver_name:    "pcimio-E",
+       module:         &__this_module,
+       attach:         pcimio_attach,
+       detach:         pcimio_detach,
+};
+
+
+/* How we access registers */
+
+
+#define ni_writew(a,b)         (writew((a),dev->iobase+(b)))
+#define ni_readw(a)            (readw(dev->iobase+(a)))
+#define ni_writeb(a,b)         (writeb((a),dev->iobase+(b)))
+#define ni_readb(a)            (readb(dev->iobase+(a)))
+#define ni_writeb_p(a,b)       (ni_writeb(a,b),ni_writeb(a,b))
+#define ni_readb_p(a)          (ni_readb(a),ni_readb(a))
+
+
+/*
+ * this is how we access windowed registers
+ * 
+ * from a driver perspective, using windowed registers
+ * on the PCI-MIO is really dumb.  I'd bet that the
+ * registers can actually be acessed without windowing...
+ * must try this sometime...
+ */
+
+#define win_out(a,b) (ni_writew((b),Window_Address),ni_writew((a),Window_Data))
+#define win_in(b) (ni_writew((b),Window_Address),ni_readw(Window_Data))
+#define win_save() (ni_readw(Window_Address))
+#define win_restore(a) (ni_writew((a),Window_Address))
+
+/*
+   If interrupts _still_ don't work, play with the
+   following two values.
+ */
+#define interrupt_pin(a)       0
+#define IRQ_POLARITY 1
+
+
+typedef struct{
+       int dio;
+       int ao0p,ao1p;
+       int aip[64];
+       int lastchan;
+       int last_do;
+       struct mite_struct *mite;
+       int rt_irq;
+       int irqmask;
+       int aimode;
+
+       unsigned short ao_mode1;
+       unsigned short ao_mode2;
+       unsigned short ao_mode3;
+       unsigned short ao_cmd1;
+       unsigned short ao_cmd2;
+       unsigned short ao_cmd3;
+       unsigned short ao_trigger_select;
+}ni_private;
+#define devpriv ((ni_private *)dev->private)
+
+
+#include "ni_mio_common.c"
+
+
+static int pcimio_find_device(comedi_device *dev);
+
+
+/* cleans up allocated resources */
+static int pcimio_detach(comedi_device *dev)
+{
+       if(dev->private && devpriv->mite)
+               mite_unsetup(devpriv->mite);
+       
+       if(dev->irq){
+               comedi_free_irq(dev->irq,dev);
+       }
+
+       return 0;
+}
+
+static int pcimio_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       int             ret;
+       
+       if(strcmp("pcimio-E",it->board_name))
+               return 0;
+       
+       printk("comedi%d: pcimio-E:",dev->minor);
+       
+       ret=alloc_private(dev,sizeof(ni_private));
+       if(ret<0)return ret;
+
+       ret=pcimio_find_device(dev);
+       if(ret<0)return ret;
+
+       printk(" %s",ni_boards[dev->board].name);
+       dev->board_name=ni_boards[dev->board].name;
+       
+       dev->iobase=mite_setup(devpriv->mite);
+
+       dev->irq=mite_irq(devpriv->mite);
+
+        if(dev->irq==0){
+               printk(" unknown irq (bad)\n");
+       }else{
+               printk(" ( irq = %d )",dev->irq);
+               if( (ret=comedi_request_irq(dev->irq,ni_E_interrupt,NI_E_IRQ_FLAGS,"pcimio-E",dev))<0 ){
+                       printk(" irq not available\n");
+                       dev->irq=0;
+               }
+       }
+       return ni_E_init(dev,it);
+}
+
+
+static int pcimio_find_device(comedi_device *dev)
+{
+       struct mite_struct *mite;
+       int i;
+
+       for(mite=mite_devices;mite;mite=mite->next){
+               for(i=0;i<n_pcimio_boards;i++){
+                       if(mite_device_id(mite)==ni_boards[i].device_id){
+                               dev->board=i;
+                               devpriv->mite=mite;
+
+                               return 0;
+                       }
+               }
+       }
+       printk("no device found\n");
+       return -EIO;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_pcimio);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_pcimio);
+}
+#endif
diff --git a/comedi/drivers/ni_stc.h b/comedi/drivers/ni_stc.h
new file mode 100644 (file)
index 0000000..ea2ae48
--- /dev/null
@@ -0,0 +1,472 @@
+/*
+    module/ni_stc.h
+    Register descriptions for NI DAQ-STC chip
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1998-9 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.
+
+*/
+
+/*
+       References:
+           DAQ-STC Technical Reference Manual
+*/
+
+#ifndef _COMEDI_NI_STC_H
+#define _COMEDI_NI_STC_H
+
+#define _bit15         0x8000
+#define _bit14         0x4000
+#define _bit13         0x2000
+#define _bit12         0x1000
+#define _bit11         0x0800
+#define _bit10         0x0400
+#define _bit9          0x0200
+#define _bit8          0x0100
+#define _bit7          0x0080
+#define _bit6          0x0040
+#define _bit5          0x0020
+#define _bit4          0x0010
+#define _bit3          0x0008
+#define _bit2          0x0004
+#define _bit1          0x0002
+#define _bit0          0x0001
+
+/* Registers in the National Instruments DAQ-STC chip */
+
+#define Interrupt_A_Ack_Register       2
+#define G0_Gate_Interrupt_Ack                  _bit15
+#define G0_TC_Interrupt_Ack                    _bit14
+#define AI_Error_Interrupt_Ack                 _bit13
+#define AI_STOP_Interrupt_Ack                  _bit12
+#define AI_START_Interrupt_Ack                 _bit11
+#define AI_START2_Interrupt_Ack                        _bit10
+#define AI_START1_Interrupt_Ack                        _bit9
+#define AI_SC_TC_Interrupt_Ack                 _bit8
+#define AI_SC_TC_Error_Confirm                 _bit7
+#define G0_TC_Error_Confirm                    _bit6
+#define G0_Gate_Error_Confirm                  _bit5
+
+#define AI_Status_1_Register           2
+#define Interrupt_A_St                         0x8000
+#define AI_FIFO_Full_St                                0x4000
+#define AI_FIFO_Half_Full_St                   0x2000
+#define AI_FIFO_Empty_St                       0x1000
+#define AI_Overrun_St                          0x0800
+#define AI_Overflow_St                         0x0400
+#define AI_SC_TC_Error_St                      0x0200
+#define AI_START2_St                           0x0100
+#define AI_START1_St                           0x0080
+#define AI_SC_TC_St                            0x0040
+#define AI_START_St                            0x0020
+#define AI_STOP_St                             0x0010
+#define G0_TC_St                               0x0008
+#define G0_Gate_Interrupt_St                   0x0004
+#define AI_FIFO_Request_St                     0x0002
+#define Pass_Thru_0_Interrupt_St               0x0001
+
+#define Interrupt_B_Ack_Register       3
+#define AO_Status_1_Register           3
+#define Interrupt_B_St                         _bit15
+#define AO_FIFO_Full_St                                _bit14
+#define AO_FIFO_Half_Full_St                   _bit13
+#define AO_FIFO_Empty_St                       _bit12
+#define AO_BC_TC_Error_St                      _bit11
+#define AO_START_St                            _bit10
+#define AO_Overrun_St                          _bit9
+#define AO_START1_St                           _bit8
+#define AO_BC_TC_St                            _bit7
+#define AO_UC_TC_St                            _bit6
+#define AO_UPDATE_St                           _bit5
+#define AO_UI2_TC_St                           _bit4
+#define G1_TC_St                               _bit3
+#define G1_Gate_Interrupt_St                   _bit2
+#define AO_FIFO_Request_St                     _bit1
+#define Pass_Thru_1_Interrupt_St               _bit0
+
+
+#define AI_Command_2_Register          4
+#define AI_End_On_SC_TC                                _bit15
+#define AI_End_On_End_Of_Scan                  _bit14
+#define AI_START1_Disable                      _bit11
+#define AI_SC_Save_Trace                       _bit10
+#define AI_SI_Switch_Load_On_SC_TC             _bit9
+#define AI_SI_Switch_Load_On_STOP              _bit8
+#define AI_SI_Switch_Load_On_TC                        _bit7
+#define AI_SC_Switch_Load_On_TC                        _bit4
+#define AI_STOP_Pulse                          _bit3
+#define AI_START_Pulse                         _bit2
+#define AI_START2_Pulse                                _bit1
+#define AI_START1_Pulse                                _bit0
+
+#define AO_Command_2_Register          5
+#define AO_End_On_BC_TC(x)                     ((x)<<14)
+#define AO_Start_Stop_Gate_Enable              _bit13
+#define AO_UC_Save_Trace                       _bit12
+#define AO_BC_Gate_Enable                      _bit11
+#define AO_BC_Save_Trace                       _bit10
+#define AO_UI_Switch_Load_On_BC_TC             _bit9
+#define AO_UI_Switch_Load_On_Stop              _bit8
+#define AO_UI_Switch_Load_On_TC                        _bit7
+#define AO_UC_Switch_Load_On_BC_TC             _bit6
+#define AO_UC_Switch_Load_On_TC                        _bit5
+#define AO_BC_Switch_Load_On_TC                        _bit4
+#define AO_Mute_B                              _bit3
+#define AO_Mute_A                              _bit2
+#define AO_UPDATE2_Pulse                       _bit1
+#define AO_START1_Pulse                                _bit0
+
+#define DIO_Input_Register             7
+#define AI_Command_1_Register          8
+
+#define AO_Command_1_Register          9
+#define AO_Analog_Trigger_Reset                        _bit15
+#define AO_START_Pulse                         _bit14
+#define AO_Disarm                              _bit13
+#define AO_UI2_Arm_Disarm                      _bit12
+#define AO_UI2_Load                            _bit11
+#define AO_UI_Arm                              _bit10
+#define AO_UI_Load                             _bit9
+#define AO_UC_Arm                              _bit8
+#define AO_UC_Load                             _bit7
+#define AO_BC_Arm                              _bit6
+#define AO_BC_Load                             _bit5
+#define AO_DAC1_Update_Mode                    _bit4
+#define AO_LDAC1_Source_Select                 _bit3
+#define AO_DAC0_Update_Mode                    _bit2
+#define AO_LDAC0_Source_Select                 _bit1
+#define AO_UPDATE_Pulse                                _bit0
+
+
+#define DIO_Output_Register            10
+#define DIO_Control_Register           11
+#define AI_Mode_1_Register             12
+
+#define AI_Mode_2_Register             13
+#define AI_SC_Gate_Enable                      _bit15
+#define AI_Start_Stop_Gate_Enable              _bit14
+#define AI_Pre_Trigger                         _bit13
+#define AI_External_MUX_Present                        _bit12
+#define AI_SI2_Ininial_Load_Source             _bit9
+#define AI_SI2_Reload_Mode                     _bit8
+#define AI_SI_Initial_Load_Source              _bit7
+#define AI_SI_Reload_Mode(a)                   ((a)<<4)
+#define AI_SI_Write_Switch                     _bit3
+#define AI_SC_Initial_Load_Source              _bit2
+#define AI_SC_Reload_Mode                      _bit1
+#define AI_SC_Write_Switch                     _bit0
+
+#define AI_SI_Load_A_Registers         14
+#define AI_SI_Load_B_Registers         16
+#define AI_SC_Load_A_Registers         18
+#define AI_SC_Load_B_Registers         20
+#define AI_SI2_Load_A_Register         23
+#define AI_SI2_Load_B_Register         25
+
+#define AO_Mode_1_Register             39
+#define AO_UPDATE_Source_Select(x)             (((x)&0x1f)<<11)
+#define AO_UI_Source_Select(x)                 (((x)&0x1f)<<6)
+#define AO_Multiple_Channels                   _bit5
+#define AO_UPDATE_Source_Polarity              _bit4
+#define AO_UI_Source_Polarity                  _bit3
+#define AO_UC_Switch_Load_Every_TC             _bit2
+#define AO_Continuous                          _bit1
+#define AO_Trigger_Once                                _bit0
+
+#define AO_Mode_2_Register             39
+#define AO_FIFO_Mode(x)                                ((x)<<14)
+#define AO_FIFO_Retransmit_Enable              _bit13
+#define AO_START1_Disable                      _bit12
+#define AO_UC_Initial_Load_Source              _bit11
+#define AO_UC_Write_Switch                     _bit10
+#define AO_UI2_Initial_Load_Source             _bit9
+#define AO_UI2_Reload_Mode                     _bit8
+#define AO_UI_Initial_Load_Source              _bit7
+#define AO_UI_Reload_Mode(x)                   ((x)<<4)
+#define AO_UI_Write_Switch                     _bit3
+#define AO_BC_Initial_Load_Source              _bit2
+#define AO_BC_Reload_Mode                      _bit1
+#define AO_BC_Write_Switch                     _bit0
+
+#define AO_UI_Load_A_Register_High     40
+#define AO_UI_Load_A_Register_Low      41
+#define AO_BC_Load_A_Register_High     44
+#define AO_BC_Load_A_Register_Low      45
+#define AO_BC_Load_B_Register_High     46
+#define AO_BC_Load_B_Register_Low      47
+#define AO_UC_Load_A_Register_High     48
+#define AO_UC_Load_A_Register_Low      49
+
+#define Clock_and_FOUT_Register                56
+#define Interrupt_Control_Register     59
+#define AI_Output_Control_Register     60
+
+#define AI_START_STOP_Select_Register  62
+#define AI_START_Polarity                      _bit15
+#define AI_STOP_Polarity                       _bit14
+#define AI_STOP_Sync                           _bit13
+#define AI_STOP_Edge                           _bit12
+#define AI_STOP_Select(a)                      ((a)<<7)
+#define AI_START_Sync                          _bit6
+#define AI_START_Edge                          _bit5
+#define AI_START_Select(a)                     (a)
+
+#define AI_Trigger_Select_Register     63
+#define AO_Start_Select_Register       66
+
+#define AO_Trigger_Select_Register     67
+#define AO_UI2_External_Gate_Enable            _bit15
+#define AO_Delayed_START1                      _bit14
+#define AO_START1_Polarity                     _bit13
+#define AO_UI2_Source_Polarity                 _bit12
+#define AO_UI2_Source_Select(x)                        (((x)&0x1f)<<7)
+#define AO_START1_Sync                         _bit6
+#define AO_START1_Edge                         _bit5
+#define AO_START1_Select(x)                    (((x)&0x1f)<<0)
+
+#define AO_Mode_3_Register             70
+#define AO_UI2_Switch_Load_Next_TC             _bit13
+#define AO_UC_Switch_Load_Every_BC_TC          _bit12
+#define AO_Trigger_Length                      _bit11
+#define AO_Stop_On_Overrun_Error               _bit5
+#define AO_Stop_On_BC_TC_Trigger_Error         _bit4
+#define AO_Stop_On_BC_TC_Error                 _bit3
+#define AO_Not_An_UPDATE                       _bit2
+#define AO_Software_Gate                       _bit1
+
+#define Joint_Reset_Register           72
+#define AO_Configuration_End                   _bit9
+#define AI_Configuration_End                   _bit8
+#define AO_Configuration_Start                 _bit5
+#define AI_Configuration_Start                 _bit4
+#define AO_Reset                               _bit1
+#define AI_Reset                               _bit0
+
+#define Interrupt_A_Enable_Register    73
+#define Pass_Thru_0_Interrupt_Enable           _bit9
+#define G0_Gate_Interrupt_Enable               _bit8
+#define AI_FIFO_Interrupt_Enable               _bit7
+#define G0_TC_Interrupt_Enable                 _bit6
+#define AI_Error_Interrupt_Enable              _bit5
+#define AI_STOP_Interrupt_Enable               _bit4
+#define AI_START_Interrupt_Enable              _bit3
+#define AI_START2_Interrupt_Enable             _bit2
+#define AI_START1_Interrupt_Enable             _bit1
+#define AI_SC_TC_Interrupt_Enable              _bit0
+
+#define Interrupt_B_Enable_Register    75
+#define Pass_Thru_1_Interrupt_Enable           _bit11
+#define G1_Gate_Interrupt_Enable               _bit10
+#define G1_TC_Interrupt_Enable                 _bit9
+#define AO_FIFO_Interrupt_Enable               _bit8
+#define AO_UI2_TC_Interrupt_Enable             _bit7
+#define AO_UC_TC_Interrupt_Enable              _bit6
+#define AO_Error_Interrupt_Enable              _bit5
+#define AO_STOP_Interrupt_Enable               _bit4
+#define AO_START_Interrupt_Enable              _bit3
+#define AO_UPDATE_Interrupt_Enable             _bit2
+#define AO_START1_Interrupt_Enable             _bit1
+#define AO_BC_TC_Interrupt_Enable              _bit0
+
+#define Second_IRQ_B_Enable_Register   76
+#define AI_Personal_Register           77
+#define AO_Personal_Register           78
+#define Write_Strobe_0_Register                82
+#define Write_Strobe_1_Register                83
+#define Write_Strobe_2_Register                84
+#define Write_Strobe_3_Register                85
+
+#define AO_Output_Control_Register     86
+#define AO_External_Gate_Enable                        _bit15
+#define AO_External_Gate_Select(x)             (((x)&0x1f)<<10)
+#define AO_Number_Of_Channels(x)               (((x)&0xf)<<6)
+#define AO_UPDATE2_Output_Select(x)            (((x)&0x3)<<4)
+#define AO_External_Gate_Polarity              _bit3
+#define AO_UPDATE2_Output_Toggle               _bit2
+#define AO_UPDATE_Output_Select(x)             (((x)&0x3)<<0)
+
+#define AI_Mode_3_Register             87
+#define AI_Trigger_Length                      _bit15
+#define AI_Delay_START                         _bit14
+#define AI_Software_Gate                       _bit13
+#define AI_SI_Special_Trigger_Delay            _bit12
+#define AI_SI2_Source_Select                   _bit11
+#define AI_Delayed_START2                      _bit10
+#define AI_Delayed_START1                      _bit9
+#define AI_External_Gate_Mode                  _bit8
+#define AI_FIFO_Mode_HF_to_E                   (3<<6)
+#define AI_FIFO_Mode_F                         (2<<6)
+#define AI_FIFO_Mode_HF                                (1<<6)
+#define AI_FIFO_Mode_NE                                (0<<6)
+#define AI_External_Gate_Polarity              _bit5
+#define AI_External_Gate_Select(a)             (a)
+
+
+/* 16 bit registers shadowed from DAQ-STC */
+#define Window_Address                 0x00
+#define Window_Data                    0x02
+#define Interrupt_A_Ack                        0x04
+#define AI_Status_1                    0x04
+#define Interrupt_B_Ack                        0x06
+#define AO_Status_1                    0x06
+#define AI_Command_2                   0x08
+#define G_Status                       0x08
+#define AO_Command_2                   0x0a
+#define AI_Status_2                    0x0a
+#define G0_Command                     0x0c
+#define AO_Status_2                    0x0c
+#define G1_Command                     0x0e
+#define DIO_Parallel_Input             0x0e
+
+#define G_Autoincrement_Register(a)    (68+(a))
+#define G_Command_Register(a)          (6+(a))
+#define G_HW_Save_Register1(a)         (8+(a)*2)
+#define G_HW_Save_Register2(a)         (9+(a)*2)
+#define G_Input_Select_Register(a)     (36+(a))
+#define G_Load_A_Register1(a)          (28+(a)*4)
+#define G_Load_A_Register2(a)          (29+(a)*4)
+#define G_Load_B_Register1(a)          (30+(a)*4)
+#define G_Load_B_Register2(a)          (31+(a)*4)
+#define G_Mode_Register(a)             (26+(a))
+#define G_Save_Register1(a)            (12+(a)*2)
+#define G_Save_Register2(a)            (13+(a)*2)
+#define G_Status_Register              4
+
+/* command register */
+#define G_Disarm_Copy                  _bit15          /* strobe */
+#define G_Save_Trace_Copy              _bit14
+#define G_Arm_Copy                     _bit13          /* strobe */
+#define G_Bank_Switch_Enable           _bit12
+#define G_Bank_Switch_Mode             _bit11
+#define G_Bank_Switch_Start            _bit10          /* strobe */
+#define G_Little_Big_Endian            _bit9
+#define G_Synchronized_Gate            _bit8
+#define G_Write_Switch                 _bit7
+#define G_Up_Down(a)                   (((a)&0x03)<<5)
+#define G_Disarm                       _bit4           /* strobe */
+#define G_Analog_Trigger_Reset         _bit3           /* strobe */
+#define G_Load                         _bit2           /* strobe */
+#define G_Save_Trace                   _bit1
+#define G_Arm                          _bit0           /* strobe */
+
+/* input select register */
+#define G_Source_Polarity              _bit15
+#define G_Output_Polarity              _bit14
+#define G_OR_Gate                      _bit13
+#define G_Gate_Select_Load_Source      _bit12
+#define G_Gate_Select(a)               (((a)&0x1f)<<7)
+#define G_Source_Select(a)             (((a)&0x1f)<<2)
+#define G_Write_Acknowledges_Irq       _bit1
+#define G_Read_Acknowledges_Irq                _bit0
+
+/* mode register */
+#define G_Reload_Source_Switching      _bit15
+#define G_Loading_On_Gate              _bit14
+#define G_Gate_Polarity                        _bit13
+#define G_Loading_On_TC                        _bit12
+#define G_Counting_Once(a)             (((a)&0x03)<<10)
+#define G_Output_Mode(a)               (((a)&0x03)<<8)
+#define G_Load_Source_Select           _bit7
+#define G_Stop_Mode(a)                 (((a)&0x03)<<5)
+#define G_Trigger_Mode_For_Edge_Gate(a)        (((a)&0x03)<<3)
+#define G_Gate_On_Both_Edges           _bit1
+#define G_Gating_Mode(a)               (((a)&0x03)<<0)
+
+
+
+/* Additional windowed registers unique to E series */
+
+#define Configuration_Memory_Clear     82
+#define ADC_FIFO_Clear                 83
+#define DAC_FIFO_Clear                 84
+
+
+/* i/o port offsets */
+
+/* 8 bit registers */
+#define Status                         0x01
+#define Serial_Command                 0x0d
+#define Misc_Command                   0x0f
+#define Port_A                         0x19
+#define Port_B                         0x1b
+#define Port_C                         0x1d
+#define Configuration                  0x1f
+#define Strobes                                0x01
+#define Channel_A_Mode                 0x03
+#define Channel_B_Mode                 0x05
+#define Channel_C_Mode                 0x07
+#define AI_AO_Select                   0x09
+#define G0_G1_Select                   0x0b
+
+/* 16 bit registers */
+#define Configuration_Memory_Low       0x10
+#define Configuration_Memory_High      0x12
+#define ADC_FIFO_Data_Register         0x1c
+#define AO_Configuration               0x16
+#define DAC_FIFO_Data                  0x1e
+#define DAC0_Direct_Data               0x18
+#define DAC1_Direct_Data               0x1a
+
+
+#define SerDacLd(x)                    (0x08<<(x))
+
+/*
+       This is stuff unique to the NI E series drivers,
+       but I thought I'd put it here anyway.
+*/
+
+enum{ ai_gain_16=0, ai_gain_8, ai_gain_14, ai_gain_8_602x };
+extern struct caldac_struct caldac_mb88341,
+       caldac_dac8800,
+       caldac_dac8043,
+       caldac_ad8522;
+
+typedef struct ni_board_struct{
+       int device_id;
+       char *name;
+       
+       int n_adchan;
+       int adbits;
+       
+       int ai_fifo_depth;
+       int alwaysdither;
+       int gainlkup;
+
+       int n_aochan;
+       int aobits;
+       
+       int ao_fifo_depth;
+       int aorangelkup;
+       
+       int ao_unipolar;
+       
+       int has_8255;
+
+       struct caldac_struct **caldac;
+}ni_board;
+
+static ni_board ni_boards[];
+#define n_ni_boards  (sizeof(ni_boards)/sizeof(ni_board))
+
+#define boardtype ni_boards[dev->board]
+
+#define NI_E_IRQ_FLAGS         0
+
+
+
+#endif /* _COMEDI_NI_STC_H */
+
diff --git a/comedi/drivers/pcl711.c b/comedi/drivers/pcl711.c
new file mode 100644 (file)
index 0000000..b76ac64
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+   module/pcl711.c
+   hardware driver for PC-LabCard PCL-711 and AdSys ACL-8112
+   and compatibles
+
+   COMEDI - Linux Control and Measurement Device Interface
+   Copyright (C) 1998 David A. Schleef <ds@stm.lbl.gov>
+   Janne Jalkanen <jalkanen@cs.hut.fi>
+   Eric Bunn <ebu@cs.hut.fi>
+
+   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.
+
+ */
+
+/*
+   Dave Andruczyk <dave@tech.buffalostate.edu> also wrote a
+   driver for the PCL-711.  I used a few ideas from his driver
+   here.  His driver also has more comments, if you are
+   interested in understanding how this driver works.
+   http://tech.buffalostate.edu/~dave/driver/
+
+   The ACL-8112 driver was hacked from the sources of the PCL-711
+   driver (the 744 chip used on the 8112 is almost the same as
+   the 711b chip, but it has more I/O channels) by
+   Janne Jalkanen (jalkanen@cs.hut.fi) and
+   Erik Bunn (ebu@cs.hut.fi).  Remerged with the PCL-711 driver
+   by ds.
+
+   [acl-8112]
+   This driver supports both TRIGNOW and TRIGCLK,
+   but does not yet support DMA transfers.  It also supports
+   both high (HG) and low (DG) versions of the card, though
+   the HG version has been untested.
+
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+
+
+#define PCL711_SIZE 16
+
+#define PCL711_CTR0 0
+#define PCL711_CTR1 1
+#define PCL711_CTR2 2
+#define PCL711_CTRCTL 3
+#define PCL711_AD_LO 4
+#define PCL711_DA0_LO 4
+#define PCL711_AD_HI 5
+#define PCL711_DA0_HI 5
+#define PCL711_DI_LO 6
+#define PCL711_DA1_LO 6
+#define PCL711_DI_HI 7
+#define PCL711_DA1_HI 7
+#define PCL711_CLRINTR 8
+#define PCL711_GAIN 9
+#define PCL711_MUX 10
+#define PCL711_MODE 11
+#define PCL711_SOFTTRIG 12
+#define PCL711_DO_LO 13
+#define PCL711_DO_HI 14
+
+/*
+--BEGIN-RANGE-DEFS--
+RANGE_pcl711b_ai
+        -5      5
+        -2.5    2.5
+        -1.25   1.25
+        -0.625  0.625
+        -0.3125 0.3125
+RANGE_acl8112hg_ai
+        -5      5
+        -0.5    0.5
+        -0.05   0.05
+        -0.005  0.005
+        0       10
+        0       1
+        0       0.1
+        0       0.01
+        -10     10
+        -1      1
+        -0.1    0.1
+        -0.01   0.01
+RANGE_acl8112dg_ai
+        -5      5
+        -2.5    2.5
+        -1.25   1.25
+        -0.625  0.625
+        0       10
+        0       5
+        0       2.5
+        0       1.25
+        -10     10
+---END-RANGE-DEFS---
+*/
+
+static int pcl711_attach(comedi_device *dev,comedi_devconfig *it);
+static int pcl711_detach(comedi_device *dev);
+static int pcl711_recognize(char *name);
+comedi_driver driver_pcl711={
+       driver_name:    "pcl711",
+       module:         &__this_module,
+       attach:         pcl711_attach,
+       detach:         pcl711_detach,
+       recognize:      pcl711_recognize,
+};
+
+typedef int bool;
+
+/*
+ * flags
+ */
+
+#define PCL711_TIMEOUT 100
+#define PCL711_DRDY 0x10
+
+
+typedef struct {
+       char *name;
+       int is_pcl711b;
+       int is_8112;
+       int is_dg;
+       int n_ranges;
+       int n_aichan;
+       int n_aochan;
+       int maxirq;
+       int ai_range_type;
+       int ai_timer_type;
+} boardtype;
+
+static boardtype boardtypes[] =
+{
+       {"pcl711", 0, 0, 0, 5, 8, 1, 0, RANGE_bipolar5, 0},
+       {"pcl711b", 1, 0, 0, 5, 8, 1, 7, RANGE_pcl711b_ai,
+        TIMER_acl8112},
+       {"acl8112hg", 0, 1, 0, 12, 16, 2, 15, RANGE_acl8112hg_ai,
+        TIMER_acl8112},
+       {"acl8112dg", 0, 1, 1, 9, 16, 2, 15, RANGE_acl8112dg_ai,
+        TIMER_acl8112},
+};
+#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
+
+typedef struct {
+       int board;
+       int adchan;
+       int ntrig;
+       int aip[8];
+       int mode;
+} pcl711_private;
+
+#define devpriv ((pcl711_private *)dev->private)
+#define this_board (boardtypes+dev->board)
+
+static void pcl711_interrupt(int irq, void *d, struct pt_regs *regs)
+{
+       int lo, hi;
+       int data;
+       comedi_device *dev = d;
+       comedi_subdevice *s = dev->subdevices + 0;
+
+       hi = inb(dev->iobase + PCL711_AD_HI);
+       lo = inb(dev->iobase + PCL711_AD_LO);
+       outb(0, dev->iobase + PCL711_CLRINTR);
+
+       data = (hi << 8) | lo;
+
+       if (!(--devpriv->ntrig)) {
+               if (this_board->is_8112) {
+                       outb(1, dev->iobase + PCL711_MODE);
+               } else {
+                       outb(0, dev->iobase + PCL711_MODE);
+               }
+               s->busy = 0;
+
+               comedi_done(dev,s);
+       }
+}
+
+static void pcl711_set_changain(comedi_device * dev, int chan)
+{
+       int chan_register;
+
+       outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
+       
+       chan_register=CR_CHAN(chan);
+
+       if (this_board->is_8112) {
+
+               /*
+                *  Set the correct channel.  The two channel banks are switched
+                *  using the mask value.
+                *  NB: To use differential channels, you should use mask = 0x30,
+                *  but I haven't written the support for this yet. /JJ
+                */
+
+               if (chan_register >= 8){
+                       chan_register = 0x20 | (chan_register & 0x7);
+               }else{
+                       chan_register |= 0x10;
+               }
+       } else {
+               outb(chan_register, dev->iobase + PCL711_MUX);
+       }
+}
+
+static int pcl711_ai_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       int hi, lo, i;
+       int nmax=40;    /* 1000 us / 25 us */
+       int n;
+
+       if(it->n<=nmax)nmax=it->n;
+
+       pcl711_set_changain(dev,it->chanlist[0]);
+       
+       /*
+          a sensible precaution to wait for the mux to
+          settle here.  is 10us enough?
+       */
+       udelay(10);
+
+for(n=0;n<nmax;n++){
+       /*
+        *  Write the correct mode (software polling) and start polling by writing
+        *  to the trigger register
+        */
+       outb(1, dev->iobase + PCL711_MODE);
+
+       if (this_board->is_8112) {
+       }else{
+               outb(0, dev->iobase + PCL711_SOFTTRIG);
+       }
+
+       i=PCL711_TIMEOUT;
+       while(--i){
+               hi = inb(dev->iobase + PCL711_AD_HI);
+               if (!(hi & PCL711_DRDY))
+                       goto ok;
+               udelay(5);
+       }
+       rt_printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
+       return -ETIME;
+       
+ok:
+       lo = inb(dev->iobase + PCL711_AD_LO);
+
+       it->data[n] = ((hi & 0xf) << 8) | lo;
+}
+
+       return n;
+}
+
+static int pcl711_ai_mode4(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       if (!this_board->is_pcl711b || dev->irq == 0)
+               return -EINVAL;
+
+       pcl711_set_changain(dev,it->chanlist[0]);
+
+       /*
+        *  Set mode to "no internal trigger"
+        */
+       outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
+
+       return 0;
+}
+
+
+static int pcl711_ai_mode1(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       if (!dev->irq)
+               return -EINVAL;
+
+       pcl711_set_changain(dev,it->chanlist[0]);
+
+       /*
+        *  Set timers
+        *      timer chip is an 8253, with timers 1 and 2
+        *      cascaded
+        *  0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
+        *        Mode 2 = Rate generator
+        *
+        *  0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
+        */
+
+
+       outb(0x74, dev->iobase + PCL711_CTRCTL);
+       outb((it->trigvar >> 16) & 0xff, dev->iobase + PCL711_CTR1);
+       outb((it->trigvar >> 24) & 0xff, dev->iobase + PCL711_CTR1);
+       outb(0xb4, dev->iobase + PCL711_CTRCTL);
+       outb(it->trigvar & 0xff, dev->iobase + PCL711_CTR2);
+       outb((it->trigvar >> 8) & 0xff, dev->iobase + PCL711_CTR2);
+
+       /* clear pending interrupts (just in case) */
+       outb(0, dev->iobase + PCL711_CLRINTR);
+
+       /*
+        *  Set mode to IRQ transfer
+        */
+       outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
+
+       return 0;
+}
+
+/*
+   analog output
+*/
+static int pcl711_ao(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       int chan = CR_CHAN(it->chanlist[0]);
+       sampl_t data = it->data[0];
+
+       outb((data & 0xff), dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
+       outb((data >> 8), dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
+
+       return 0;
+}
+
+/* Digital port read - Untested on 8112 */
+static int pcl711_di(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       int data;
+       int chan;
+       int i;
+
+       data = inb(dev->iobase + PCL711_DI_LO) |
+           (inb(dev->iobase + PCL711_DI_HI) << 8);
+
+       for(i=0;i<it->n_chan;i++){
+               chan=CR_CHAN(it->chanlist[i]);
+               it->data[i]=(data>>chan)&1;
+       }
+
+       return it->n_chan;
+}
+
+/* Digital port write - Untested on 8112 */
+static int pcl711_do(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+{
+       int mask, data;
+       int chan;
+       int i;
+
+       data=s->state;
+       for(i=0;i<it->n_chan;i++){
+               chan=CR_CHAN(it->chanlist[i]);
+               mask=(1<<chan);
+               data &= ~mask;
+               if(it->data[i])
+                       data |= mask;
+       }
+       outb(data & 0xff, dev->iobase + PCL711_DO_LO);
+       outb((data >> 8), dev->iobase + PCL711_DO_HI);
+       s->state = data;
+
+       return it->n_chan;
+}
+
+/*  Free any resources that we have claimed  */
+static void free_resources(comedi_device * dev)
+{
+       if (dev->irq)
+               free_irq(dev->irq, dev);
+
+       if (dev->iobase)
+               release_region(dev->iobase, dev->iosize);
+}
+
+static int pcl711_recognize(char *name)
+{
+       int i;
+
+       for (i = 0; i < n_boardtypes; i++) {
+               if (!strcmp(boardtypes[i].name, name)) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+/*  Initialization */
+static int pcl711_attach(comedi_device * dev, comedi_devconfig * it)
+{
+       int ret;
+       int iobase;
+       int irq;
+       comedi_subdevice *s;
+
+       /* claim our I/O space */
+
+       iobase = it->options[0];
+       printk("comedi%d: pcl711: 0x%04x ", dev->minor, iobase);
+       if (check_region(iobase, PCL711_SIZE) < 0) {
+               printk("I/O port conflict\n");
+               return -EIO;
+       }
+       request_region(dev->iobase, PCL711_SIZE, "pcl711");
+       dev->iobase = iobase;
+       dev->iosize = PCL711_SIZE;
+
+       /* there should be a sanity check here */
+
+       /* set up some name stuff */
+       dev->board_name = boardtypes[dev->board].name;
+
+       /* grab our IRQ */
+       irq = it->options[1];
+       if (irq < 0 || irq > boardtypes[dev->board].maxirq) {
+               printk("irq out of range\n");
+               free_resources(dev);
+               return -EINVAL;
+       }
+       if (irq) {
+               if (request_irq(irq, pcl711_interrupt, SA_INTERRUPT, "pcl711", dev)) {
+                       printk("unable to allocate irq %d\n", irq);
+                       free_resources(dev);
+                       return -EINVAL;
+               } else {
+                       printk("( irq = %d )\n", irq);
+               }
+       }
+       dev->irq = irq;
+
+       dev->n_subdevices = 4;
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+       if((ret=alloc_private(dev,sizeof(pcl711_private)))<0)
+               return ret;
+
+       s = dev->subdevices + 0;
+       /* AI subdevice */
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = this_board->n_aichan;
+       s->maxdata = 0xfff;
+       s->len_chanlist = 1;
+       s->timer_type = this_board->ai_timer_type;
+       s->range_type = this_board->ai_range_type;
+       s->trig[0] = pcl711_ai_mode0;
+       s->trig[1] = pcl711_ai_mode1;
+       s->trig[4] = pcl711_ai_mode4;
+
+       s++;
+       /* AO subdevice */
+       s->type = COMEDI_SUBD_AO;
+       s->subdev_flags = SDF_WRITEABLE;
+       s->n_chan = this_board->n_aochan;
+       s->maxdata = 0xfff;
+       s->len_chanlist = 1;
+       s->range_type = RANGE_bipolar5;
+       s->trig[0] = pcl711_ao;
+
+       s++;
+       /* 16-bit digital input */
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = 16;
+       s->maxdata = 1;
+       s->len_chanlist = 16;
+       s->range_type = RANGE_digital;
+       s->trig[0] = pcl711_di;
+
+       s++;
+       /* 16-bit digital out */
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITEABLE;
+       s->n_chan = 16;
+       s->maxdata = 1;
+       s->len_chanlist = 16;
+       s->range_type = RANGE_digital;
+       s->state=0;
+       s->trig[0] = pcl711_do;
+
+       /*
+          this is the "base value" for the mode register, which is
+          used for the irq on the PCL711
+        */
+       if(this_board->is_pcl711b){
+               devpriv->mode=(dev->irq<<4);
+       }
+
+       /* clear DAC */
+       outb(0, dev->iobase + PCL711_DA0_LO);
+       outb(0, dev->iobase + PCL711_DA0_HI);
+       outb(0, dev->iobase + PCL711_DA1_LO);
+       outb(0, dev->iobase + PCL711_DA1_HI);
+
+       printk("\n");
+
+       return 0;
+}
+
+
+/*
+ *  Removes device
+ */
+
+static int pcl711_detach(comedi_device * dev)
+{
+       printk("comedi%d: pcl711: remove\n", dev->minor);
+
+       free_resources(dev);
+
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_pcl711);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_pcl711);
+}
+#endif
diff --git a/comedi/drivers/pcl725.c b/comedi/drivers/pcl725.c
new file mode 100644 (file)
index 0000000..daf9249
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Driver for PCL725 and clones
+ * David A. Schleef
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+
+
+#define PCL725_SIZE 2
+
+#define PCL725_DO 0
+#define PCL725_DI 1
+
+static int pcl725_attach(comedi_device *dev,comedi_devconfig *it);
+static int pcl725_detach(comedi_device *dev);
+comedi_driver driver_pcl725={
+       driver_name:    "pcl725",
+       module:         &__this_module,
+       attach:         pcl725_attach,
+       detach:         pcl725_detach,
+};
+
+static int pcl725_do(comedi_device *dev,comedi_subdevice *s,comedi_trig *it);
+static int pcl725_di(comedi_device *dev,comedi_subdevice *s,comedi_trig *it);
+
+static int pcl725_do(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       do_pack(&s->state,it);
+       
+       outb(s->state,dev->iobase+PCL725_DO);
+
+       return it->n_chan;
+}
+
+static int pcl725_di(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       unsigned int bits;
+       
+       bits=inb(dev->iobase+PCL725_DI);
+       
+       return di_unpack(bits,it);
+}
+
+static int pcl725_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+
+       dev->iobase=it->options[0];
+       printk("comedi%d: pcl725: 0x%04x ",dev->minor,dev->iobase);
+       if(check_region(dev->iobase,PCL725_SIZE)<0){
+               printk("I/O port conflict\n");
+               return -EIO;
+       }
+       request_region(dev->iobase,PCL725_SIZE,"pcl725");
+       dev->board_name="pcl725";
+       dev->iobase=dev->iobase;
+       dev->iosize=PCL725_SIZE;
+       dev->irq=0;
+
+       dev->n_subdevices=2;
+
+       if(alloc_subdevices(dev)<0)
+               return -ENOMEM;
+
+       s=dev->subdevices+0;
+       /* do */
+       s->type=COMEDI_SUBD_DO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->maxdata=1;
+       s->n_chan=8;
+       s->trig[0]=pcl725_do;
+       s->range_type=RANGE_digital;
+
+       s=dev->subdevices+1;
+       /* do */
+       s->type=COMEDI_SUBD_DI;
+       s->subdev_flags=SDF_READABLE;
+       s->maxdata=1;
+       s->n_chan=8;
+       s->trig[0]=pcl725_di;
+       s->range_type=RANGE_digital;
+
+       printk("\n");
+
+       return 0;
+}
+
+
+static int pcl725_detach(comedi_device *dev)
+{
+       printk("comedi%d: pcl725: remove\n",dev->minor);
+       
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_pcl725);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_pcl725);
+}
+#endif
diff --git a/comedi/drivers/pcl726.c b/comedi/drivers/pcl726.c
new file mode 100644 (file)
index 0000000..c3dd430
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+    module/pcl726.c
+    hardware driver for PC-LabCard PCL-726 and compatibles
+      ACL-6126
+
+    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.
+
+*/
+
+
+/*
+    Thanks to Circuit Specialists for having programming info (!) on
+    their web page.  (http://www.cir.com/)
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+
+#undef PCL726_IRQ      /* no interrupt support (yet) */
+
+
+#define PCL726_SIZE 16
+
+#define PCL726_DAC0_HI 0
+#define PCL726_DAC0_LO 1
+
+#define PCL726_DO_HI 12
+#define PCL726_DO_LO 13
+#define PCL726_DI_HI 14
+#define PCL726_DI_LO 15
+
+
+static int pcl726_attach(comedi_device *dev,comedi_devconfig *it);
+static int pcl726_detach(comedi_device *dev);
+comedi_driver driver_pcl726={
+       driver_name:    "pcl726",
+       module:         &__this_module,
+       attach:         pcl726_attach,
+       detach:         pcl726_detach,
+};
+
+
+typedef struct{
+       int bipolar[6];
+}pcl726_private;
+#define devpriv ((pcl726_private *)dev->private)
+
+
+static int pcl726_ao(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int hi,lo;
+       int chan=CR_CHAN(it->chanlist[0]);
+
+       lo=it->data[0]&0xff;
+       hi=(it->data[0]>>8)&0xf;
+       if(devpriv->bipolar[chan])hi^=0x8;
+/*
+       the programming info did not say which order to write bytes.
+       switch the order of the next two lines if you get glitches.
+*/
+       outb(hi,dev->iobase+PCL726_DAC0_HI + 2*chan);
+       outb(lo,dev->iobase+PCL726_DAC0_LO + 2*chan);
+       
+       return 1;
+}
+
+static int pcl726_di(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       unsigned int bits;
+       
+       bits=inb(dev->iobase+PCL726_DI_LO)|
+               (inb(dev->iobase+PCL726_DI_HI)<<8);
+       
+       return di_unpack(bits,it);
+}
+
+static int pcl726_do(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       do_pack(&s->state,it);
+       
+       outb(s->state&0xff,dev->iobase+PCL726_DO_LO);
+       outb((s->state>>8),dev->iobase+PCL726_DO_HI);
+       
+       return it->n_chan;
+}
+
+static int pcl726_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       int ret;
+       
+       dev->iobase=it->options[0];
+       printk("comedi%d: pcl726: 0x%04x ",dev->minor,dev->iobase);
+       if(check_region(dev->iobase,PCL726_SIZE)<0){
+               printk("I/O port conflict\n");
+               return -EIO;
+       }
+       
+       dev->board_name="pcl726";
+       
+#ifdef PCL726_IRQ
+       irq=dev->options[1];
+       if(!is_b)irq=0;
+       if(irq<0 || irq==1 || irq>7){
+               printk("irq out of range\n");
+               return -EIO;
+       }
+       if(irq){
+               if(request_irq(irq,pcl726_interrupt,SA_INTERRUPT,"pcl726",dev)){
+                       printk("unable to allocate irq %d\n",irq);
+                       irq=0;
+               }else{
+                       printk("( irq = %d )\n",irq);
+               }
+       }
+       dev->irq=irq;
+#endif
+       
+       request_region(dev->iobase,PCL726_SIZE,"pcl726");
+       dev->iosize=PCL726_SIZE;
+
+       if((ret=alloc_private(dev,sizeof(pcl726_private)))<0)
+               return -ENOMEM;
+
+       dev->n_subdevices=3;
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+
+       s=dev->subdevices+0;
+       /* ao */
+       s->type=COMEDI_SUBD_AO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->n_chan=6;
+       s->maxdata=0xfff;
+       s->trig[0]=pcl726_ao;
+       s->range_type=RANGE_unknown;    /* XXX */
+
+       s=dev->subdevices+1;
+       /* di */
+       s->type=COMEDI_SUBD_DI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=16;
+       s->maxdata=1;
+       s->trig[0]=pcl726_di;
+       s->range_type=RANGE_digital;
+
+       s=dev->subdevices+2;
+       /* do */
+       s->type=COMEDI_SUBD_DO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->n_chan=16;
+       s->maxdata=1;
+       s->trig[0]=pcl726_do;
+       s->range_type=RANGE_digital;
+
+       printk("\n");
+       
+       return 0;
+}
+
+
+static int pcl726_detach(comedi_device *dev)
+{
+       printk("comedi%d: pcl726: remove\n",dev->minor);
+       
+#ifdef PCL726_IRQ
+       if(dev->irq){
+               free_irq(dev->irq,dev);
+       }
+#endif
+
+       release_region(dev->iobase,dev->iosize);
+
+       return 0;
+}
+
+
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_pcl726);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_pcl726);
+}
+#endif
diff --git a/comedi/drivers/rti800.c b/comedi/drivers/rti800.c
new file mode 100644 (file)
index 0000000..3165760
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+   module/rti800.c
+   hardware driver for Analog Devices RTI-800/815 board
+
+   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.
+
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+
+#define RTI800_SIZE 16
+
+#define RTI800_CSR 0
+#define RTI800_MUXGAIN 1
+#define RTI800_CONVERT 2
+#define RTI800_ADCLO 3
+#define RTI800_ADCHI 4
+#define RTI800_DAC0LO 5
+#define RTI800_DAC0HI 6
+#define RTI800_DAC1LO 7
+#define RTI800_DAC1HI 8
+#define RTI800_CLRFLAGS 9
+#define RTI800_DI 10
+#define RTI800_DO 11
+#define RTI800_9513A_DATA 12
+#define RTI800_9513A_CNTRL 13
+#define RTI800_9513A_STATUS 13
+
+
+/*
+ * flags for CSR register
+ */
+
+#define RTI800_BUSY            0x80
+#define RTI800_DONE            0x40
+#define RTI800_OVERRUN         0x20
+#define RTI800_TCR             0x10
+#define RTI800_DMA_ENAB                0x08
+#define RTI800_INTR_TC         0x04
+#define RTI800_INTR_EC         0x02
+#define RTI800_INTR_OVRN       0x01
+
+#define Am9513_8BITBUS
+
+#define Am9513_output_control(a)       outb(a,dev->iobase+RTI800_9513A_CNTRL)
+#define Am9513_output_data(a)          outb(a,dev->iobase+RTI800_9513A_DATA)
+#define Am9513_input_data()            inb(dev->iobase+RTI800_9513A_DATA)
+#define Am9513_input_status()          inb(dev->iobase+RTI800_9513A_STATUS)
+
+#include <am9513.h>
+
+/*
+--BEGIN-RANGE-DEFS--
+RANGE_rti800_ai_10_bipolar
+        -10     10
+        -1      1
+        -0.1    0.1
+        -0.02   0.02
+RANGE_rti800_ai_5_bipolar
+        -5      5
+        -0.5    0.5
+        -0.05   0.05
+        -0.01   0.01
+RANGE_rti800_ai_10_unipolar
+        0       10
+        0       1
+        0       0.1
+        0       0.02
+---END-RANGE-DEFS---
+*/
+
+static int rti800_attach(comedi_device *dev,comedi_devconfig *it);
+static int rti800_detach(comedi_device *dev);
+static int rti800_recognize(char *name);
+comedi_driver driver_rti800={
+       driver_name:    "rti800",
+       module:         &__this_module,
+       attach:         rti800_attach,
+       detach:         rti800_detach,
+       recognize:      rti800_recognize,
+};
+
+static void rti800_interrupt(int irq, void *dev, struct pt_regs *regs);
+
+typedef struct {
+       enum {
+               adc_diff, adc_pseudodiff, adc_singleended
+       } adc_mux;
+       enum {
+               adc_bipolar10, adc_bipolar5, adc_unipolar10
+       } adc_range;
+       enum {
+               adc_2comp, adc_straight
+       } adc_coding;
+       enum {
+               dac_bipolar10, dac_unipolar10
+       } dac0_range, dac1_range;
+       enum {
+               dac_2comp, dac_straight
+       } dac0_coding, dac1_coding;
+       int ao_range_type_list[2];
+} rti800_private;
+
+#define devpriv ((rti800_private *)dev->private)
+
+#define RTI800_TIMEOUT 10
+
+static void rti800_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+
+
+}
+
+static int gaindelay[]={10,20,40,80};
+
+static int rti800_ai_mode0(comedi_device * dev, comedi_subdevice *s, comedi_trig * it)
+{
+  int i;
+  for(i=0 ; i < it->n_chan ; i++) {
+    int t, hi, lo, gain;
+       int chan;
+       int data;
+       int status;
+
+    chan = CR_CHAN(it->chanlist[i]);
+    gain = CR_RANGE(it->chanlist[i]);
+
+       inb(dev->iobase + RTI800_ADCHI);
+       outb(0,dev->iobase+RTI800_CLRFLAGS);
+
+       outb(chan | (gain << 5), dev->iobase + RTI800_MUXGAIN);
+
+       /* contrary to the docs, there needs to be a delay here */
+       udelay(gaindelay[gain]);
+
+       outb(0, dev->iobase + RTI800_CONVERT);
+    for (t = 0; t < RTI800_TIMEOUT; t++) {
+               status=inb(dev->iobase+RTI800_CSR);
+#if DEBUG
+      rt_printk("status=%x\n",status);
+#endif
+               if(status & RTI800_OVERRUN){
+                       rt_printk("rti800: a/d overflow\n");
+                       outb(0,dev->iobase+RTI800_CLRFLAGS);
+
+                       return -ETIME;
+               }
+               if (status & RTI800_DONE)
+                       break;
+      udelay(8);
+       }
+    if(t==RTI800_TIMEOUT){
+               rt_printk("rti800: timeout\n");
+
+               return -ETIME;
+       }
+       lo = inb(dev->iobase + RTI800_ADCLO);
+       hi = inb(dev->iobase + RTI800_ADCHI);
+
+       data = (hi << 8) | lo;
+       data &= 0xfff;
+       if (devpriv->adc_coding == adc_2comp) {
+               data ^= 0x800;
+       }
+    it->data[i]=data;
+  }
+  return i;
+
+}
+
+static int rti800_ai_mode1(comedi_device * dev, comedi_subdevice *s, comedi_trig * it)
+{
+       return -EINVAL;
+}
+
+
+static int rti800_ao(comedi_device * dev, comedi_subdevice *s, comedi_trig * it)
+{
+  int i;
+  for(i=0 ; i < it->n_chan ; i++) {
+       int chan;
+       int data;
+
+    chan=CR_CHAN(it->chanlist[i]);
+    data=it->data[i];
+
+       switch(chan){
+       case 0:
+               if (devpriv->dac0_coding == dac_2comp) {
+                       data ^= 0x800;
+               }
+               outb(data & 0xff, dev->iobase + RTI800_DAC0LO);
+               outb(data >> 8, dev->iobase + RTI800_DAC0HI);
+       break;
+       case 1:
+               if (devpriv->dac1_coding == dac_2comp) {
+                       data ^= 0x800;
+               }
+               outb(data & 0xff, dev->iobase + RTI800_DAC1LO);
+               outb(data >> 8, dev->iobase + RTI800_DAC1HI);
+       break;
+       default:
+               return -EINVAL;
+       }
+  }
+  return i;
+}
+
+static int rti800_di(comedi_device * dev, comedi_subdevice *s, comedi_trig * it)
+{
+       unsigned int bits;
+       
+       bits = inb(dev->iobase + RTI800_DI);
+
+       return di_unpack(bits,it);
+}
+
+static int rti800_do(comedi_device * dev, comedi_subdevice *s, comedi_trig * it)
+{
+       do_pack(&s->state,it);
+
+       /* Outputs are inverted... */
+       outb(s->state ^ 0xff, dev->iobase + RTI800_DO);
+
+       return it->n_chan;
+}
+
+
+/*
+   options[0] - I/O port
+   options[1] - irq
+   options[2] - a/d mux
+       0=differential, 1=pseudodiff, 2=single
+   options[3] - a/d range
+       0=bipolar10, 1=bipolar5, 2=unipolar10
+   options[4] - a/d coding
+       0=2's comp, 1=straight binary
+   options[5] - dac0 range
+       0=bipolar10, 1=unipolar10
+   options[6] - dac0 coding
+       0=2's comp, 1=straight binary
+   options[7] - dac1 range
+   options[8] - dac1 coding
+ */
+
+static int rti800_recognize(char *name)
+{
+       if (!strcmp("rti800", name))return 0;
+       if (!strcmp("rti815", name))return 0;
+
+       return -1;
+}
+
+static int rti800_attach(comedi_device * dev, comedi_devconfig * it)
+{
+       int irq;
+       int iobase;
+       int ret;
+       comedi_subdevice *s;
+
+       iobase = it->options[0];
+       printk("comedi%d: rti800: 0x%04x ", dev->minor, iobase);
+       if (check_region(iobase, RTI800_SIZE) < 0) {
+               printk("I/O port conflict\n");
+               return -EIO;
+       }
+       request_region(dev->iobase, RTI800_SIZE, "rti800");
+       dev->iobase = iobase;
+       dev->iosize = RTI800_SIZE;
+
+#ifdef DEBUG
+       printk("fingerprint=%x,%x,%x,%x,%x ",
+               inb(dev->iobase + 0),
+               inb(dev->iobase + 1),
+               inb(dev->iobase + 2),
+               inb(dev->iobase + 3),
+               inb(dev->iobase + 4));
+#endif
+
+       outb(0,dev->iobase+RTI800_CSR);
+       inb(dev->iobase+RTI800_ADCHI);
+       outb(0,dev->iobase+RTI800_CLRFLAGS);
+
+       irq=it->options[1];
+       if(irq>0){
+               printk("( irq = %d )\n",irq);
+               if((ret=request_irq(irq,rti800_interrupt, SA_INTERRUPT, "rti800", dev))<0)
+                       return ret;
+               dev->irq=irq;
+       }else if(irq == 0){
+               printk("( no irq )");
+       }
+
+       if (dev->board == 0)
+               dev->board_name = "rti800";
+       else
+               dev->board_name = "rti815";
+
+
+       if (dev->board != 0) {
+               dev->n_subdevices=4;
+       }else{
+               dev->n_subdevices=3;
+       }
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+       if((ret=alloc_private(dev,sizeof(rti800_private)))<0)
+               return ret;
+       
+       devpriv->adc_mux = it->options[2];
+       devpriv->adc_range = it->options[3];
+       devpriv->adc_coding = it->options[4];
+       devpriv->dac0_range = it->options[5];
+       devpriv->dac0_coding = it->options[6];
+       devpriv->dac1_range = it->options[7];
+       devpriv->dac1_coding = it->options[8];
+
+       s=dev->subdevices+0;
+       /* ai subdevice */
+       s->type=COMEDI_SUBD_AI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=(devpriv->adc_mux? 16 : 8);
+       s->trig[0]=rti800_ai_mode0;
+       s->trig[1]=rti800_ai_mode1;
+       s->maxdata=0xfff;
+       switch (devpriv->adc_range) {
+       case adc_bipolar10:
+               s->range_type = RANGE_rti800_ai_10_bipolar;
+               break;
+       case adc_bipolar5:
+               s->range_type = RANGE_rti800_ai_5_bipolar;
+               break;
+       case adc_unipolar10:
+               s->range_type = RANGE_rti800_ai_10_unipolar;
+               break;
+       }
+
+       if (dev->board == 1) {
+               s++;
+               /* ao subdevice (only on rti815) */
+               s->type=COMEDI_SUBD_AO;
+               s->subdev_flags=SDF_WRITEABLE;
+               s->n_chan=2;
+               s->trig[0]=rti800_ao;
+               s->maxdata=0xfff;
+               s->range_type=0;
+               s->range_type_list=devpriv->ao_range_type_list;
+               switch (devpriv->dac0_range) {
+               case dac_bipolar10:
+                       devpriv->ao_range_type_list[0] = RANGE_bipolar10;
+                       break;
+               case dac_unipolar10:
+                       devpriv->ao_range_type_list[0] = RANGE_unipolar10;
+                       break;
+               }
+               switch (devpriv->dac1_range) {
+               case dac_bipolar10:
+                       devpriv->ao_range_type_list[1] = RANGE_bipolar10;
+                       break;
+               case dac_unipolar10:
+                       devpriv->ao_range_type_list[1] = RANGE_unipolar10;
+                       break;
+               }
+       }
+
+       s++;
+       /* di */
+       s->type=COMEDI_SUBD_DI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=8;
+       s->trig[0]=rti800_di;
+       s->maxdata=1;
+       s->range_type=RANGE_digital;
+
+       s++;
+       /* do */
+       s->type=COMEDI_SUBD_DO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->n_chan=8;
+       s->trig[0]=rti800_do;
+       s->maxdata=1;
+       s->range_type=RANGE_digital;
+
+
+/* don't yet know how to deal with counter/timers */
+#if 0
+       s++;
+       /* do */
+       s->type=COMEDI_SUBD_TIMER;
+       s->n_chan=0;
+       s->trig[0]=NULL;
+       s->maxdata=0
+#endif
+
+       printk("\n");
+
+       return 0;
+}
+
+
+static int rti800_detach(comedi_device * dev)
+{
+       printk("comedi%d: rti800: remove\n", dev->minor);
+
+       if(dev->iobase)
+               release_region(dev->iobase, dev->iosize);
+
+       if(dev->irq)
+               free_irq(dev->irq,dev);
+
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_rti800);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_rti800);
+}
+#endif
diff --git a/comedi/drivers/rti802.c b/comedi/drivers/rti802.c
new file mode 100644 (file)
index 0000000..8b52dad
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+   module/rti802.c
+   hardware driver for Analog Devices RTI-802 board
+
+   COMEDI - Linux Control and Measurement Device Interface
+   Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
+
+   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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+
+#define RTI802_SIZE 4
+
+#define RTI802_SELECT 0
+#define RTI802_DATALOW 1
+#define RTI802_DATAHIGH 2
+
+static int rti802_attach(comedi_device *dev,comedi_devconfig *it);
+static int rti802_detach(comedi_device *dev);
+comedi_driver driver_rti802={
+       driver_name:    "rti802",
+       module:         &__this_module,
+       attach:         rti802_attach,
+       detach:         rti802_detach,
+};
+
+static void rti802_free_resources(comedi_device * dev);
+
+typedef struct {
+       enum {
+               dac_2comp, dac_straight
+       } dac_coding[8];
+       unsigned int range_type_list[8];
+} rti802_private;
+
+#define devpriv ((rti802_private *)dev->private)
+
+static int rti802_ao(comedi_device * dev, comedi_subdevice *s, comedi_trig * it)
+{
+  int i;
+  for(i=0 ; i < it->n_chan ; i++) {
+    int chan = CR_CHAN(it->chanlist[i]);
+    int data = it->data[i];
+
+       if (devpriv->dac_coding[chan] == dac_2comp) {
+               data ^= 0x800;
+       }
+       outb(chan, dev->iobase + RTI802_SELECT);
+       outb(data & 0xff, dev->iobase + RTI802_DATALOW);
+       outb(data >> 8, dev->iobase + RTI802_DATAHIGH);
+  }
+  return i;
+}
+
+/*
+   options:
+    [0] - i/o base
+    [1] - unused
+    [2] - dac#0  0=two's comp, 1=straight
+    [3] - dac#0  0=bipolar, 1=unipolar
+    [4] - dac#1 ...
+    ...
+    [17] - dac#7 ...
+ */
+
+static int rti802_attach(comedi_device * dev, comedi_devconfig * it)
+{
+       comedi_subdevice *s;
+       int i;
+
+       dev->iobase = it->options[0];
+       printk("comedi%d: rti802: 0x%04x ", dev->minor, dev->iobase);
+       if (check_region(dev->iobase, RTI802_SIZE) < 0) {
+               printk("I/O port conflict\n");
+               return -EIO;
+       }
+       request_region(dev->iobase, RTI802_SIZE, "rti802");
+
+       dev->board_name = "rti802";
+
+       dev->n_subdevices = 1;
+       if(alloc_subdevices(dev)<0 || alloc_private(dev,sizeof(rti802_private))){
+               return -ENOMEM;
+       }
+
+       s=dev->subdevices;
+       /* ao subdevice */
+       s->type=COMEDI_SUBD_AO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->maxdata=0xfff;
+       s->n_chan=8;
+       s->trig[0] = rti802_ao;
+       s->range_type_list=devpriv->range_type_list;
+
+       for (i = 0; i < 8; i++) {
+               devpriv->dac_coding[i] = (it->options[3 + 2 * i])
+                   ? (dac_straight)
+                   : (dac_2comp);
+               devpriv->range_type_list[i] = (it->options[2 + 2 * i])
+                       ? RANGE_unipolar10
+                       : RANGE_bipolar10;
+       }
+
+       printk("\n");
+
+       return 0;
+}
+
+static void rti802_free_resources(comedi_device * dev)
+{
+       if(dev->iobase)
+               release_region(dev->iobase, RTI802_SIZE);
+}
+
+static int rti802_detach(comedi_device * dev)
+{
+       printk("comedi%d: rti802: remove\n", dev->minor);
+
+       rti802_free_resources(dev);
+
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       comedi_driver_register(&driver_rti802);
+       
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       comedi_driver_unregister(&driver_rti802);
+}
+#endif
diff --git a/comedi/dummy.c b/comedi/dummy.c
new file mode 100644 (file)
index 0000000..44a71a8
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * dummy driver
+ * David A. Schleef
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+/* this structure is for data unique to this hardware driver.  If
+   several hardware drivers keep similar information in this structure,
+   feel free to suggest moving the variable to the comedi_device struct.  */
+typedef struct{
+       int data;
+}dummy_private;
+#define devpriv ((dummy_private *)dev->private)
+
+static int dummy_attach(comedi_device *dev,comedi_devconfig *it);
+static int dummy_detach(comedi_device *dev);
+static int dummy_recognize(char *name);
+comedi_driver driver_dummy={
+       driver_name:    "dummy",
+       attach:         dummy_attach,
+       detach:         dummy_detach,
+       recognize:      dummy_recognize,
+};
+
+static int dummy_ai(comedi_device *dev,comedi_subdevice *s,comedi_trig *it);
+static int dummy_ao(comedi_device *dev,comedi_subdevice *s,comedi_trig *it);
+
+static int dummy_recognize(char *name)
+{
+       if(!strcmp("dummy",name))return 0;
+       if(!strcmp("example",name))return 1;
+
+       return -1;
+}
+
+static int dummy_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+
+/* there is a *_init() function for each hardware driver installed.
+   Each one is called in sequence.
+   if it->name is a device that this hardware driver supports, we try
+   to initialize it.  If everything works, return a 1.  If there is
+   an error, return the error.  If we do not match it->name, return a
+   0 so that the next *_init() function can be tried. */
+
+/* *dev is zeroed except for dev->minor, which contains the minor
+   number being used.  *it has configuration information (not really
+   used here--see other drivers.)  this function is responsible for
+   filling the rest of *dev.
+   no longer true
+ */
+
+       printk("comedi%d: dummy: ",dev->minor);
+       
+       /* this is an appropriate place to put probe and initialization
+          code.  remember to fill in dev->iobase and friends. */
+       dev->board_name="dummy";
+
+       if(alloc_private(dev,sizeof(dummy_private))<0)
+               return -ENOMEM;
+#if DEBUG
+       printk("malloc ok\n");
+#endif
+
+       dev->n_subdevices=2;
+       if(alloc_subdevices(dev)<0)
+               return -ENOMEM;
+
+       /* analog input subdevice */
+       s=dev->subdevices+0;
+       s->type=COMEDI_SUBD_AI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=1;
+       s->maxdata=0xffff;
+       s->range_type=RANGE_unknown;
+       s->trig[0]=dummy_ai;
+       
+       /* analog output subdevice */
+       s=dev->subdevices+1;
+       s->type=COMEDI_SUBD_AO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->n_chan=1;
+       s->maxdata=0xffff;
+       s->range_type=RANGE_unknown;
+       s->trig[0]=dummy_ao;
+       
+       return 1;
+}
+
+
+static int dummy_detach(comedi_device *dev)
+{
+       printk("comedi%d: dummy: remove\n",dev->minor);
+       
+       return 0;
+}
+
+static int dummy_ai(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       it->data[0]=devpriv->data;
+
+       return 0;
+}
+
+static int dummy_ao(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       devpriv->data=it->data[0];
+
+       return 0;
+}
+
diff --git a/comedi/kcomedilib/Makefile b/comedi/kcomedilib/Makefile
new file mode 100644 (file)
index 0000000..fc53018
--- /dev/null
@@ -0,0 +1,13 @@
+
+ALL_SUB_DIRS :=
+MOD_SUB_DIRS :=
+SUB_DIRS     :=
+MOD_LIST_NAME := MISC_MODULES
+
+EXTRA_CFLAGS := -I ../
+
+M_OBJS := kcomedilib.o
+
+
+include $(TOPDIR)/Rules.make
+
diff --git a/comedi/kcomedilib/kcomedilib.c b/comedi/kcomedilib/kcomedilib.c
new file mode 100644 (file)
index 0000000..6c9a546
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+    kcomedilib/kcomedilib.c
+    a comedlib interface for kernel modules
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1997-2000 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 <comedi_module.h>
+
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/io.h>
+#ifdef LINUX_V22
+#include <asm/uaccess.h>
+#endif
+
+
+extern volatile int rtcomedi_lock_semaphore;
+
+
+#if 0
+/* need more thot */
+int comedi_devinfo_ioctl(unsigned int minor,comedi_devinfo *arg);
+int comedi_subdinfo_ioctl(unsigned int minor,comedi_subdinfo *arg,void *file);
+int comedi_chaninfo_ioctl(unsigned int minor,comedi_chaninfo *arg);
+#endif
+
+
+
+static inline int minor_to_dev(unsigned int minor,comedi_device **dev)
+{
+       if(minor>=COMEDI_NDEVICES)
+               return -ENODEV;
+
+       *dev=comedi_devices+minor;
+
+       if(!(*dev)->attached)
+               return -ENODEV;
+
+       return 0;
+}
+
+
+/*
+   These functions are #if 0'd because they aren't appropriate
+   inside RTLinux, at least, not in this form.  Interface needs
+   thot.
+ */
+#if 0
+
+/*
+       COMEDI_DEVINFO
+       device info ioctl
+       
+       arg:
+               pointer to devinfo structure
+       
+       reads:
+               none
+       
+       writes:
+               devinfo structure
+               
+*/
+static int do_devinfo_ioctl(comedi_device *dev,comedi_devinfo *arg)
+{
+       comedi_devinfo devinfo;
+       
+       
+       /* fill devinfo structure */
+       devinfo.version_code=COMEDI_VERSION_CODE;
+       devinfo.n_subdevs=dev->n_subdevices;
+       memcpy(devinfo.driver_name,dev->driver_name,COMEDI_NAMELEN);
+       memcpy(devinfo.board_name,dev->board_name,COMEDI_NAMELEN);
+       memcpy(devinfo.options,dev->options,COMEDI_NDEVCONFOPTS*sizeof(int));
+       
+
+       if(copy_to_user(arg,&devinfo,sizeof(comedi_devinfo)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+/*
+       COMEDI_SUBDINFO
+       subdevice info ioctl
+       
+       arg:
+               pointer to array of subdevice info structures
+       
+       reads:
+               none
+       
+       writes:
+               array of subdevice info structures at arg
+               
+*/
+static int do_subdinfo_ioctl(comedi_device *dev,comedi_subdinfo *arg,void *file)
+{
+       int ret,i;
+       comedi_subdinfo *tmp,*us;
+       comedi_subdevice *s;
+       
+
+       tmp=kmalloc(dev->n_subdevices*sizeof(comedi_subdinfo),GFP_KERNEL);
+       if(!tmp)
+               return -ENOMEM;
+       
+       /* fill subdinfo structs */
+       for(i=0;i<dev->n_subdevices;i++){
+               s=dev->subdevices+i;
+               us=tmp+i;
+               
+               us->type                = s->type;
+               us->n_chan              = s->n_chan;
+               us->subd_flags          = s->subdev_flags;
+               us->timer_type          = s->timer_type;
+               us->len_chanlist        = s->len_chanlist;
+               us->maxdata             = s->maxdata;
+               us->range_type          = s->range_type;
+               
+               if(s->busy)
+                       us->subd_flags |= SDF_BUSY;
+               if(s->busy == file)
+                       us->subd_flags |= SDF_BUSY_OWNER;
+               if(s->lock)
+                       us->subd_flags |= SDF_LOCKED;
+               if(s->lock == file)
+                       us->subd_flags |= SDF_LOCK_OWNER;
+               if(s->maxdata_list)
+                       us->subd_flags |= SDF_MAXDATA;
+               if(s->flaglist)
+                       us->subd_flags |= SDF_FLAGS;
+               if(s->range_type_list)
+                       us->subd_flags |= SDF_RANGETYPE;
+
+       }
+       
+       ret=copy_to_user(arg,tmp,dev->n_subdevices*sizeof(comedi_subdinfo));
+       
+       kfree(tmp);
+       
+       return ret?-EFAULT:0;
+}
+
+
+/*
+       COMEDI_CHANINFO
+       subdevice info ioctl
+       
+       arg:
+               pointer to chaninfo structure
+       
+       reads:
+               chaninfo structure at arg
+       
+       writes:
+               arrays at elements of chaninfo structure
+       
+*/
+static int do_chaninfo_ioctl(comedi_device *dev,comedi_chaninfo *arg)
+{
+       comedi_subdevice *s;
+       comedi_chaninfo it;
+       int ret;
+       
+       if(copy_from_user(&it,arg,sizeof(comedi_chaninfo)))
+               return -EFAULT;
+       
+       if(it.subdev>=dev->n_subdevices)
+               return -EINVAL;
+       s=dev->subdevices+it.subdev;
+       
+       if(it.flaglist){
+               if(s->subdev_flags & SDF_FLAGS)
+                       ret=copy_to_user(it.flaglist,s->flaglist,s->n_chan*sizeof(unsigned int));
+               else
+                       ret=clear_user(it.flaglist,s->n_chan*sizeof(unsigned int));
+               if(ret)return -EFAULT;
+       }
+                       
+       if(it.rangelist){
+               if(s->subdev_flags & SDF_FLAGS)
+                       ret=copy_to_user(it.rangelist,s->range_type_list,s->n_chan*sizeof(unsigned int));
+               else
+                       ret=clear_user(it.rangelist,s->n_chan*sizeof(unsigned int));
+               if(ret)return -EFAULT;
+       }
+       
+       return 0;
+}
+#endif
+
+
+/*
+       COMEDI_TRIG
+       trigger ioctl
+       
+       arg:
+               pointer to trig structure
+       
+       reads:
+               trig structure at arg
+               channel/range list
+       
+       writes:
+               modified trig structure at arg
+               data list
+
+       this function is too complicated
+*/
+int comedi_trig_ioctl(unsigned int minor,unsigned int subdev,comedi_trig *it)
+{
+       comedi_device *dev;
+       comedi_subdevice *s;
+       int ret=0;
+
+       if((ret=minor_to_dev(minor,&dev))<0)
+               return ret;
+       
+       if(it->subdev>=dev->n_subdevices)
+               return -ENODEV;
+
+       s=dev->subdevices+it->subdev;
+       if(s->type==COMEDI_SUBD_UNUSED)
+               return -EIO;
+       
+       /* is subdevice RT capable? (important!) */
+       if(!(s->subdev_flags&SDF_RT)){
+               ret=-EINVAL;
+               goto cleanup;
+       }
+
+       /* are we locked? (ioctl lock) */
+       if(s->lock && s->lock!=&rtcomedi_lock_semaphore)
+               return -EACCES;
+
+       /* are we busy? */
+       if(s->busy)
+               return -EBUSY;
+       s->busy=(void *)&rtcomedi_lock_semaphore;
+
+       /* make sure channel/gain list isn't too long */
+       if(it->n_chan > s->len_chanlist){
+               ret = -EINVAL;
+               goto cleanup;
+       }
+       
+       /* make sure each element in channel/gain list is valid */
+       if((ret=check_chanlist(s,it->n_chan,it->chanlist))<0)
+               goto cleanup;
+       
+       s->buf_user_ptr=0;
+       s->buf_user_count=0;
+       s->buf_int_ptr=0;
+       s->buf_int_count=0;
+
+       if(it->data==NULL){
+               ret=-EINVAL;
+               goto cleanup;
+       }
+
+       if(!it->data_len){
+#if 0
+               ret=-EINVAL;
+               goto cleanup;
+#else
+               it->data_len=it->n_chan*it->n;
+               rt_printk("comedi: warning: trig->data_len not set\n");
+#endif
+       }
+
+       if(it->mode>=5 || s->trig[it->mode]==NULL){
+               ret=-EINVAL;
+               goto cleanup;
+       }
+
+       s->cur_trig=*it;
+       
+       ret=s->trig[it->mode](dev,s,it);
+       
+       if(ret==0)return 0;
+       if(ret<0)goto cleanup;
+
+       if(ret>it->n*it->n_chan){
+               rt_printk("comedi: (bug) trig returned too many samples\n");
+       }
+
+cleanup:
+       s->busy=NULL;
+
+       return ret;
+}
+
+/*
+   This function does the same as above, but without any sanity
+   checks.  Any insanity in code calling this function must be
+   assumed by the writer.
+ */
+int __comedi_trig_ioctl(unsigned int minor,unsigned int subdev,comedi_trig *it)
+{
+       comedi_device *dev;
+       comedi_subdevice *s;
+       int ret=0;
+
+       dev=comedi_devices+minor;
+       s=dev->subdevices+subdev;
+
+       s->cur_trig=*it;
+       
+       ret=s->trig[it->mode](dev,s,it);
+       
+       return ret;
+}
+
+/*
+       COMEDI_LOCK
+       lock subdevice
+       
+       arg:
+               subdevice number
+       
+       reads:
+               none
+       
+       writes:
+               none
+
+       non-RT linux always controls rtcomedi_lock_semaphore.  If an
+       RT-linux process wants the lock, it first checks rtcomedi_lock_semaphore.
+       If it is 1, it knows it is pre-empting this function, and fails.
+       Obviously, if RT-linux fails to get a lock, it *must* allow
+       linux to run, since that is the only way to free the lock.
+       
+       This function is not SMP compatible.
+
+       necessary locking:
+       - ioctl/rt lock  (this type)
+       - lock while subdevice busy
+       - lock while subdevice being programmed
+       
+*/
+int comedi_lock_ioctl(unsigned int minor,unsigned int subdev)
+{
+       int ret=0;
+       comedi_subdevice *s;
+       comedi_device *dev;
+
+       if(rtcomedi_lock_semaphore)
+               return -EBUSY;
+       
+       if((ret=minor_to_dev(minor,&dev))<0)
+               return ret;
+       
+       if(subdev>=dev->n_subdevices)
+               return -EINVAL;
+       s=dev->subdevices+subdev;
+       
+       if(s->busy)
+               return -EBUSY;
+
+       /* &rtcomedi_lock_semaphore is just a convenient address */
+
+       if(s->lock && s->lock!=&rtcomedi_lock_semaphore){
+               ret=-EACCES;
+       }else{
+               s->lock=(void *)&rtcomedi_lock_semaphore;
+
+               if(s->do_lock)
+                       s->do_lock(dev,s);
+       }
+       
+       return ret;
+}
+
+
+/*
+       COMEDI_UNLOCK
+       unlock subdevice
+       
+       arg:
+               subdevice number
+       
+       reads:
+               none
+       
+       writes:
+               none
+
+*/
+int comedi_unlock_ioctl(unsigned int minor,unsigned int subdev)
+{
+       int ret=0;
+       comedi_subdevice *s;
+       comedi_device *dev;
+
+       if(rtcomedi_lock_semaphore)
+               return -EBUSY;
+       
+       if((ret=minor_to_dev(minor,&dev))<0)
+               return ret;
+       
+       if(subdev>=dev->n_subdevices)
+               return -EINVAL;
+       s=dev->subdevices+subdev;
+       
+       if(s->busy)
+               return -EBUSY;
+
+       if(s->lock && s->lock!=&rtcomedi_lock_semaphore)
+               return -EACCES;
+       
+       if(s->do_unlock)
+               s->do_unlock(dev,s);
+
+       if(s->lock==&rtcomedi_lock_semaphore){
+               s->lock=NULL;
+
+               s->cb_mask=0;
+               s->cb_func=NULL;
+               s->cb_arg=NULL;
+       }
+
+       return 0;
+}
+
+/*
+       COMEDI_CANCEL
+       cancel acquisition ioctl
+       
+       arg:
+               subdevice number
+       
+       reads:
+               nothing
+       
+       writes:
+               nothing
+
+*/
+int comedi_cancel_ioctl(unsigned int minor,unsigned int subdev)
+{
+       int ret=0;
+       comedi_subdevice *s;
+       comedi_device *dev;
+
+       if(rtcomedi_lock_semaphore)
+               return -EBUSY;
+       
+       if((ret=minor_to_dev(minor,&dev))<0)
+               return ret;
+       
+       if(subdev>=dev->n_subdevices)
+               return -EINVAL;
+       s=dev->subdevices+subdev;
+       
+       if(s->lock && s->lock!=&rtcomedi_lock_semaphore)
+               return -EACCES;
+       
+       if(!s->busy)
+               return 0;
+
+       if(s->busy!=&rtcomedi_lock_semaphore)
+               return -EBUSY;
+
+       if(!s->cancel)
+               return -EINVAL;
+
+       if((ret=s->cancel(dev,s)))
+               return ret;
+
+       s->busy=NULL;
+
+       return 0;
+}
+       
+/*
+   registration of callback functions
+
+   XXX - this needs additional work.  Specifically, being SDF_RT is _not_ a
+   sufficient condition for being able to do callbacks.
+ */
+int comedi_register_callback(unsigned int minor,unsigned int subdev,
+               unsigned int mask,int (*cb)(unsigned int,void *),void *arg)
+{
+       comedi_device *dev;
+       comedi_subdevice *s;
+       int ret;
+
+       if((ret=minor_to_dev(minor,&dev))<0)
+               return ret;
+       
+       if(subdev>=dev->n_subdevices)
+               return -ENODEV;
+
+       s=dev->subdevices+subdev;
+       if(s->type==COMEDI_SUBD_UNUSED)
+               return -EIO;
+       
+       /* is subdevice RT capable? (important!) */
+       if(!(s->subdev_flags&SDF_RT))
+               return -EINVAL;
+
+       /* are we locked? (ioctl lock) */
+       if(s->lock && s->lock!=&rtcomedi_lock_semaphore)
+               return -EACCES;
+
+       /* are we busy? */
+       if(s->busy)
+               return -EBUSY;
+
+       if(!mask){
+               s->cb_mask=0;
+               s->cb_func=NULL;
+               s->cb_arg=NULL;
+       }else{
+               s->cb_mask=mask;
+               s->cb_func=cb;
+               s->cb_arg=arg;
+       }
+
+       return 0;
+}
+
+
diff --git a/comedi/kern_compat.h b/comedi/kern_compat.h
new file mode 100644 (file)
index 0000000..77a6936
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+    kern_compat.h
+    Kernel compatibility header file
+
+    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.
+
+*/
+
+/*
+   Portions taken from Don Becker's network device drivers, and
+   modified.
+*/
+
+/*
+   The purpose of this file is to make it easy to write modules
+   that will compile correctly for multiple kernel versions.
+   This file can only provide backward compatibility, i.e.,
+   write your driver for 2.3.x, include this header file, and
+   theoretically, it will compile and run on 2.0.37.  However,
+   some interface changes require superset definitions to
+   allow compilation for all supported kernel versions, so
+   you have to use the interface provided in this file to
+   compile for all kernels.
+
+   If your driver is written for the 2.2.x interface, define
+   COMPAT_V22 before including this file.
+*/
+
+#ifndef _KERN_COMPAT_H
+#define _KERN_COMPAT_H
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/kdev_t.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c)  (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0)
+#error kernel versions prior to 2.0 not supported
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+#define LINUX_V20
+#else
+#define LINUX_V22
+#endif
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) /* XXX */
+#define RDEV_OF_FILE(x)        ((x)->f_inode->i_rdev)
+#else
+#define RDEV_OF_FILE(x)        ((x)->f_dentry->d_inode->i_rdev)
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) /* XXX */
+#define signal_pending(x)      (((x)->signal) & (~(x)->blocked))
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) /* XXX */
+/* somewhere about 2.1.4 */
+
+#include <asm/segment.h>
+
+static inline int copy_to_user(void * to,const void *from,unsigned long n_bytes)
+{
+       int i;
+
+       if((i=verify_area(VERIFY_WRITE,to,n_bytes)) != 0)
+               return i;
+       memcpy_tofs(to,from,n_bytes);
+       return 0;
+}
+
+static inline int copy_from_user(void * to,const void *from,unsigned long n_bytes)
+{
+       int i;
+       if((i=verify_area(VERIFY_READ,from,n_bytes))!=0)
+               return i;
+       memcpy_fromfs(to,from,n_bytes);
+       return 0;
+}
+
+static inline int clear_user(void * mem,unsigned long len)
+{
+       char *cmem=mem;
+       
+       if(verify_area(VERIFY_WRITE,mem,len))
+               return len;
+       /* this is slow, but I'm lazy */
+       while(len--){put_user(0,cmem);cmem++;}
+       return 0;
+}
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+static inline void __process_timeout(unsigned long __data)
+{
+       struct task_struct * p = (struct task_struct *) __data;
+
+       p->timeout=0;
+       wake_up_process(p);
+}
+
+static inline long interruptible_sleep_on_timeout(struct wait_queue ** p,
+       long timeout)
+{
+       struct timer_list timer;
+       unsigned long expires=jiffies+timeout;
+
+       init_timer(&timer);
+       timer.expires=expires;
+       timer.data=(unsigned long)current;
+       timer.function=__process_timeout;
+       add_timer(&timer);
+
+       interruptible_sleep_on(p);
+
+       del_timer(&timer);
+
+       return jiffies-expires;
+}
+#else
+#define HAVE_INTERRUPTIBLE_SLEEP_ON_TIMEOUT
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+#ifndef __SMP__
+/* XXX */
+#define claim_dma_lock()       0
+#define release_dma_lock(a)    
+#endif
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+#ifndef __alpha__
+#define ioremap(a,b) \
+        (((a)<0x100000) ? (void *)((u_long)(a)) : vremap(a,b))
+#define iounmap(v) \
+               do { if ((u_long)(v) > 0x100000) vfree(v); } while (0)
+#endif
+#endif
+
+#if LINUX_VERSION_CODE < 0x020115
+#define MODULE_AUTHOR(a)
+#define MODULE_DESCRIPTION(a)
+#define MODULE_PARM(a,b)
+#endif
+
+#if LINUX_VERSION_CODE < 0x20138
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#define le32_to_cpu(val) (val)
+#define cpu_to_le32(val) (val)
+#endif
+
+#if LINUX_VERSION_CODE <= 0x20139
+#define net_device_stats enet_statistics
+#define NETSTATS_VER2
+#endif
+
+#if LINUX_VERSION_CODE < 0x20155
+#include <linux/bios32.h>
+#define PCI_SUPPORT_VER1
+#else
+#define PCI_SUPPORT_VER2
+#endif
+
+#if LINUX_VERSION_CODE < 0x20159
+#define DEV_FREE_SKB(skb) dev_kfree_skb (skb, FREE_WRITE);
+#else  /* Grrr, unneeded incompatible change. */
+#define DEV_FREE_SKB(skb) dev_kfree_skb(skb);
+#endif
+
+#ifndef COMPAT_V22
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,5)
+typedef struct wait_queue *wait_queue_head_t;
+#define DECLARE_WAITQUEUE(x,y) struct wait_queue x={y,NULL}
+#define init_waitqueue_head(x)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,10)                /* ? */
+#define file_atomic_inc(x)     ((*(x))++)
+#else
+#define file_atomic_inc(x)     atomic_inc(x)
+#endif
+
+
+#endif
+
+
+#endif /* _KERN_COMPAT_H */
+
+
+
+
diff --git a/comedi/kvmem.c b/comedi/kvmem.c
new file mode 100644 (file)
index 0000000..6ed3874
--- /dev/null
@@ -0,0 +1,90 @@
+#include "kvmem.h"
+
+/* allocate user space mmapable block of memory in the kernel space */
+void * rvmalloc(unsigned long size)
+{
+       void * mem;
+       unsigned long adr, page;
+        
+       mem=vmalloc(size);
+       if (mem) 
+       {
+               memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+               adr=(unsigned long) mem;
+               while (size > 0) 
+                {
+#if LINUX_VERSION_CODE < 0x020300
+                       page = kvirt_to_phys(adr);
+                       mem_map_reserve(MAP_NR(phys_to_virt(page)));
+#else
+                       page = kvirt_to_pa(adr);
+                       mem_map_reserve(MAP_NR(__va(page)));
+#endif
+                       adr+=PAGE_SIZE;
+                       size-=PAGE_SIZE;
+               }
+       }
+       return mem;
+}
+
+void rvfree(void * mem, unsigned long size)
+{
+        unsigned long adr, page;
+        
+       if (mem) 
+       {
+               adr=(unsigned long) mem;
+               while (size > 0) 
+                {
+#if LINUX_VERSION_CODE < 0x020300
+                       page = kvirt_to_phys(adr);
+                       mem_map_unreserve(MAP_NR(phys_to_virt(page)));
+#else
+                       page = kvirt_to_pa(adr);
+                       mem_map_unreserve(MAP_NR(__va(page)));
+#endif
+                       adr+=PAGE_SIZE;
+                       size-=PAGE_SIZE;
+               }
+               vfree(mem);
+       }
+}
+
+/* this function will map (fragment of) rvmalloc'ed memory area to user space */
+int rvmmap(void *mem, unsigned memsize, struct vm_area_struct *vma) {
+       unsigned long pos, size, start=vma->vm_start;
+#if LINUX_VERSION_CODE < 0x20300
+       unsigned long offset = vma->vm_offset;
+#else
+       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+#endif
+       /* this is not time critical code, so we check the arguments */
+       /* vma->vm_offset HAS to be checked (and is checked)*/
+       if (offset<0)
+               return -EFAULT;
+       size = vma->vm_end - vma->vm_start;
+       if (size + offset > memsize)
+               return -EFAULT;
+       pos = (unsigned long) mem + offset;
+       if (pos%PAGE_SIZE || start%PAGE_SIZE || size%PAGE_SIZE)
+               return -EFAULT;
+               
+       while (size>0) {
+#if LINUX_VERSION_CODE < 0x020300
+               if (remap_page_range(start,kvirt_to_phys(pos), PAGE_SIZE, 
+                       vma->vm_page_prot )) {
+                       /* FIXME: what should we do here to unmap previous pages ?*/
+                       printk(KERN_ERR "rvmmap failed: vm_start=0x%lx, vm_end=0x%lx, size=0x%lx, pos=0x%lx; please report to motyl@stan.chemie.unibas.ch\n",vma->vm_start,vma->vm_end,size,pos);
+                       return -EFAULT;
+               }
+#else
+               if (remap_page_range(start, kvirt_to_pa(pos),
+                   PAGE_SIZE, PAGE_SHARED))
+                       return -EAGAIN;
+#endif
+               pos+=PAGE_SIZE;
+               start+=PAGE_SIZE;
+               size-=PAGE_SIZE;
+       }
+       return 0;
+}
diff --git a/comedi/kvmem.h b/comedi/kvmem.h
new file mode 100644 (file)
index 0000000..a09427e
--- /dev/null
@@ -0,0 +1,114 @@
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+#ifndef _KVMEM_H
+#define _KVMEM_H
+#include <linux/version.h>
+#include <linux/malloc.h>
+#include <linux/wrapper.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+
+/* convert virtual user memory address to physical address */
+/* (virt_to_phys only works for kmalloced kernel memory) */
+
+#if LINUX_VERSION_CODE < 0x020300
+static inline unsigned long uvirt_to_phys(unsigned long adr)
+{
+       pgd_t *pgd;
+       pmd_t *pmd;
+       pte_t *ptep, pte;
+  
+       pgd = pgd_offset(current->mm, adr);
+       if (pgd_none(*pgd))
+               return 0;
+       pmd = pmd_offset(pgd, adr);
+       if (pmd_none(*pmd))
+               return 0;
+       ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/);
+       pte = *ptep;
+       if(pte_present(pte))
+               return virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1))));
+       return 0;
+}
+
+static inline unsigned long uvirt_to_bus(unsigned long adr) 
+{
+       return virt_to_bus(phys_to_virt(uvirt_to_phys(adr)));
+}
+
+/* convert virtual kernel memory address to physical address */
+/* (virt_to_phys only works for kmalloced kernel memory) */
+
+static inline unsigned long kvirt_to_phys(unsigned long adr) 
+{
+       return uvirt_to_phys(VMALLOC_VMADDR(adr));
+}
+
+static inline unsigned long kvirt_to_bus(unsigned long adr) 
+{
+       return uvirt_to_bus(VMALLOC_VMADDR(adr));
+}
+
+#else
+
+static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
+{
+       unsigned long ret = 0UL;
+       pmd_t *pmd;
+       pte_t *ptep, pte;
+
+
+       if(!pgd_none(*pgd)) {
+               pmd = pmd_offset(pgd, adr);
+               if (!pmd_none(*pmd)) {
+                       ptep = pte_offset(pmd, adr);
+                       pte = *ptep;
+                       if(pte_present(pte))
+                               ret = (page_address(pte_page(pte))|(adr&(PAGE_SIZE-1)));
+               }
+       }
+       return ret;
+}
+
+static inline unsigned long uvirt_to_bus(unsigned long adr)
+{
+       unsigned long kva, ret;
+
+       kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);
+       ret = virt_to_bus((void *)kva);
+
+       return ret;
+}
+
+static inline unsigned long kvirt_to_bus(unsigned long adr)
+{
+       unsigned long va, kva, ret;
+
+       va = VMALLOC_VMADDR(adr);
+       kva = uvirt_to_kva(pgd_offset_k(va), va);
+       ret = virt_to_bus((void *)kva);
+
+       return ret;
+}
+
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+       unsigned long va, kva, ret;
+
+       va = VMALLOC_VMADDR(adr);
+       kva = uvirt_to_kva(pgd_offset_k(va), va);
+       ret = __pa(kva);
+
+       return ret;
+}
+
+
+#endif
+
+
+extern void * rvmalloc(unsigned long size);
+extern void rvfree(void * mem, unsigned long size);
+extern int rvmmap(void *mem, unsigned memsize, struct vm_area_struct *vma);
+#endif
diff --git a/comedi/mk_range.c b/comedi/mk_range.c
new file mode 100644 (file)
index 0000000..aa85587
--- /dev/null
@@ -0,0 +1,130 @@
+
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "../include/comedi.h"
+
+char s[100];
+char name[100];
+
+int get_flags(char *p);
+
+#define skip_white(a)  {while(*(a) && isspace(*(a)))(a)++;}
+
+void help(void)
+{
+       fprintf(stderr, "Use the source, dumbass\n");
+       exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+       char *p;
+       double a, b;
+       int i = 0, j = 0, ln = 0;
+       int mode = 0;
+       int flags = 0;
+       int n;
+
+       if (argc != 2)
+               help();
+       if (!strcmp(argv[1], "include")) {
+               mode = 1;
+       } else if (!strcmp(argv[1], "source")) {
+               mode = 2;
+       } else
+               help();
+
+       if(mode==1){
+       }else{
+               printf("#ifdef RANGE_C\n");
+               printf("comedi_krange comedi_kranges[]={\n");
+       }
+       while (1) {
+               fgets(s, 100, stdin);
+               ln++;
+               if (feof(stdin))
+                       break;
+
+               s[strlen(s) - 1] = 0;
+               p = s;
+               skip_white(p);
+               if(p==s){
+                       if (mode == 1) {
+                               if (i)
+                                       printf("%d)\n", j);
+                               printf("#define %s __RANGE(%d,", p, i);
+                       } else {
+                               printf("/* %s */\n", p);
+                       }
+                       j = 0;
+               }else{
+                       if (isdigit(*p) || (*p) == '-') {
+                               sscanf(p, "%lf %lf%n", &a, &b , &n);
+                               p+=n;
+
+                               flags=get_flags(p);
+                               if(flags<0){
+                                       printf("\n\n#error while running scripts/mk_range, line %d, unknown flag\n", ln);
+                                       return 1;
+                               }
+
+                               if (mode == 2) {
+                                       printf("\t{%d,%d,%d},\n", (int)(1e6*a), (int)(1e6*b),flags);
+                               }
+                               i++;
+                               j++;
+                       } else {
+                               printf("\n\n#error while running scripts/mk_range, line %d\n", ln);
+                               return 1;
+                       }
+               }
+       }
+       if(mode==1){
+               printf("%d)\n", j);
+       }else{
+               printf("};\n");
+               printf("unsigned int comedi_max_range=%d;\n",i);
+               printf("#endif\n");
+       }
+       return 0;
+}
+
+int get_flags(char *p)
+{
+       int flags=0;
+
+       while(*p){
+               skip_white(p);
+               if(!strncmp(p,"ext",3)){
+                       flags|=RF_EXTERNAL;
+                       p+=3;
+                       continue;
+               }
+               if(!strncmp(p,"mA",2)){
+                       flags|=UNIT_mA;
+                       p+=2;
+                       continue;
+               }
+               if(!strncmp(p,"volt",4)){
+                       flags|=UNIT_volt;
+                       p+=4;
+                       continue;
+               }
+               if(!strncmp(p,"none",4)){
+                       flags|=UNIT_none;
+                       p+=4;
+                       continue;
+               }
+               if(!strncmp(p,"unitless",8)){
+                       flags|=UNIT_none;
+                       p+=8;
+                       continue;
+               }
+               return -1;
+       }
+       return flags;
+}
+
diff --git a/comedi/module.c b/comedi/module.c
new file mode 100644 (file)
index 0000000..c514a6c
--- /dev/null
@@ -0,0 +1,1598 @@
+/*
+    module/module.c
+    comedi kernel module
+
+    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.
+
+*/
+
+#undef DEBUG
+
+#include <comedi_module.h>
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/kmod.h>
+#include <asm/io.h>
+#ifdef LINUX_V22
+#include <asm/uaccess.h>
+#endif
+
+comedi_device *comedi_devices;
+
+
+static int do_devconfig_ioctl(comedi_device *dev,comedi_devconfig *arg,kdev_t minor);
+static int do_devinfo_ioctl(comedi_device *dev,comedi_devinfo *arg);
+static int do_subdinfo_ioctl(comedi_device *dev,comedi_subdinfo *arg,void *file);
+static int do_chaninfo_ioctl(comedi_device *dev,comedi_chaninfo *arg);
+static int do_trig_ioctl(comedi_device *dev,void *arg,void *file);
+static int do_cmd_ioctl(comedi_device *dev,void *arg,void *file);
+static int do_lock_ioctl(comedi_device *dev,unsigned int arg,void * file);
+static int do_unlock_ioctl(comedi_device *dev,unsigned int arg,void * file);
+static int do_cancel_ioctl(comedi_device *dev,unsigned int arg,void *file);
+
+static void do_become_nonbusy(comedi_device *dev,comedi_subdevice *s);
+
+static int comedi_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg)
+{
+       kdev_t minor=MINOR(inode->i_rdev);
+       comedi_device *dev=comedi_devices+minor;
+
+       
+       switch(cmd)
+       {
+       case COMEDI_DEVCONFIG:
+               return do_devconfig_ioctl(dev,(void *)arg,minor);
+       case COMEDI_DEVINFO:
+               return do_devinfo_ioctl(dev,(void *)arg);
+       case COMEDI_SUBDINFO:
+               return do_subdinfo_ioctl(dev,(void *)arg,file);
+       case COMEDI_CHANINFO:
+               return do_chaninfo_ioctl(dev,(void *)arg);
+       case COMEDI_RANGEINFO:
+               return do_rangeinfo_ioctl(dev,(void *)arg);
+       case COMEDI_TRIG:
+               return do_trig_ioctl(dev,(void *)arg,file);
+       case COMEDI_LOCK:
+               return do_lock_ioctl(dev,arg,file);
+       case COMEDI_UNLOCK:
+               return do_unlock_ioctl(dev,arg,file);
+       case COMEDI_CANCEL:
+               return do_cancel_ioctl(dev,arg,file);
+       case COMEDI_CMD:
+               return do_cmd_ioctl(dev,(void *)arg,file);
+       default:
+               return -EIO;
+       }
+}
+
+
+/*
+       COMEDI_DEVCONFIG
+       device config ioctl
+       
+       arg:
+               pointer to devconfig structure
+       
+       reads:
+               devconfig structure at arg
+       
+       writes:
+               none
+*/
+static int do_devconfig_ioctl(comedi_device *dev,comedi_devconfig *arg,kdev_t minor)
+{
+       comedi_devconfig it;
+       
+       if(!suser())
+               return -EPERM;
+       
+       if(arg==NULL){
+               return comedi_device_detach(dev);
+       }
+
+       if(copy_from_user(&it,arg,sizeof(comedi_devconfig)))
+               return -EFAULT;
+       
+       it.board_name[COMEDI_NAMELEN-1]=0;
+       
+       return comedi_device_attach(dev,&it);
+}
+
+
+/*
+       COMEDI_DEVINFO
+       device info ioctl
+       
+       arg:
+               pointer to devinfo structure
+       
+       reads:
+               none
+       
+       writes:
+               devinfo structure
+               
+*/
+static int do_devinfo_ioctl(comedi_device *dev,comedi_devinfo *arg)
+{
+       comedi_devinfo devinfo;
+       
+       
+       /* fill devinfo structure */
+       devinfo.version_code=COMEDI_VERSION_CODE;
+       devinfo.n_subdevs=dev->n_subdevices;
+#if 1
+       if(!dev->board_name){
+               printk("BUG: dev->board_name=<%p>\n",dev->board_name);
+               return -EFAULT;
+       }
+#endif
+       memcpy(devinfo.driver_name,dev->driver->driver_name,COMEDI_NAMELEN);
+       memcpy(devinfo.board_name,dev->board_name,COMEDI_NAMELEN);
+       memcpy(devinfo.options,dev->options,COMEDI_NDEVCONFOPTS*sizeof(int));
+       
+
+       if(copy_to_user(arg,&devinfo,sizeof(comedi_devinfo)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+/*
+       COMEDI_SUBDINFO
+       subdevice info ioctl
+       
+       arg:
+               pointer to array of subdevice info structures
+       
+       reads:
+               none
+       
+       writes:
+               array of subdevice info structures at arg
+               
+*/
+static int do_subdinfo_ioctl(comedi_device *dev,comedi_subdinfo *arg,void *file)
+{
+       int ret,i;
+       comedi_subdinfo *tmp,*us;
+       comedi_subdevice *s;
+       
+
+       tmp=kmalloc(dev->n_subdevices*sizeof(comedi_subdinfo),GFP_KERNEL);
+       if(!tmp)
+               return -ENOMEM;
+       
+       memset(tmp,0,sizeof(comedi_subdinfo)*dev->n_subdevices);
+
+       /* fill subdinfo structs */
+       for(i=0;i<dev->n_subdevices;i++){
+               s=dev->subdevices+i;
+               us=tmp+i;
+               
+               us->type                = s->type;
+               us->n_chan              = s->n_chan;
+               us->subd_flags          = s->subdev_flags;
+               us->timer_type          = s->timer_type;
+               us->len_chanlist        = s->len_chanlist;
+               us->maxdata             = s->maxdata;
+               us->range_type          = s->range_type;
+               us->flags               = s->flags;
+               
+               if(s->busy)
+                       us->subd_flags |= SDF_BUSY;
+               if(s->busy == file)
+                       us->subd_flags |= SDF_BUSY_OWNER;
+               if(s->lock)
+                       us->subd_flags |= SDF_LOCKED;
+               if(s->lock == file)
+                       us->subd_flags |= SDF_LOCK_OWNER;
+               if(!s->maxdata && s->maxdata_list)
+                       us->subd_flags |= SDF_MAXDATA;
+               if(s->flaglist)
+                       us->subd_flags |= SDF_FLAGS;
+               if(s->range_type_list)
+                       us->subd_flags |= SDF_RANGETYPE;
+               if(s->trig[0])
+                       us->subd_flags |= SDF_MODE0;
+               if(s->trig[1])
+                       us->subd_flags |= SDF_MODE1;
+               if(s->trig[2])
+                       us->subd_flags |= SDF_MODE2;
+               if(s->trig[3])
+                       us->subd_flags |= SDF_MODE3;
+               if(s->trig[4])
+                       us->subd_flags |= SDF_MODE4;
+       }
+       
+       ret=copy_to_user(arg,tmp,dev->n_subdevices*sizeof(comedi_subdinfo));
+       
+       kfree(tmp);
+       
+       return ret?-EFAULT:0;
+}
+
+
+/*
+       COMEDI_CHANINFO
+       subdevice info ioctl
+       
+       arg:
+               pointer to chaninfo structure
+       
+       reads:
+               chaninfo structure at arg
+       
+       writes:
+               arrays at elements of chaninfo structure
+       
+*/
+static int do_chaninfo_ioctl(comedi_device *dev,comedi_chaninfo *arg)
+{
+       comedi_subdevice *s;
+       comedi_chaninfo it;
+       
+       if(copy_from_user(&it,arg,sizeof(comedi_chaninfo)))
+               return -EFAULT;
+       
+       if(it.subdev>=dev->n_subdevices)
+               return -EINVAL;
+       s=dev->subdevices+it.subdev;
+       
+       if(it.maxdata_list){
+               if(s->maxdata || !s->maxdata_list)
+                       return -EINVAL;
+               if(copy_to_user(it.maxdata_list,s->maxdata_list,s->n_chan*sizeof(lsampl_t)))
+                       return -EFAULT;
+       }
+
+       if(it.flaglist){
+               if(!s->flaglist)return -EINVAL;
+               if(copy_to_user(it.flaglist,s->flaglist,s->n_chan*sizeof(unsigned int)))
+                       return -EFAULT;
+       }
+                       
+       if(it.rangelist){
+               if(!s->range_type_list)return -EINVAL;
+               if(copy_to_user(it.rangelist,s->range_type_list,s->n_chan*sizeof(unsigned int)))
+                       return -EFAULT;
+       }
+       
+       return 0;
+}
+
+
+/*
+       COMEDI_TRIG
+       trigger ioctl
+       
+       arg:
+               pointer to trig structure
+       
+       reads:
+               trig structure at arg
+               channel/range list
+       
+       writes:
+               modified trig structure at arg
+               data list
+
+       this function is too complicated
+*/
+static int do_trig_ioctl(comedi_device *dev,void *arg,void *file)
+{
+       comedi_trig user_trig;
+       comedi_subdevice *s;
+       int ret=0,i,bufsz;
+       int reading;
+       
+#if 0
+DPRINTK("entering do_trig_ioctl()\n");
+#endif
+       if(copy_from_user(&user_trig,arg,sizeof(comedi_trig))){
+               DPRINTK("bad trig address\n");
+               return -EFAULT;
+       }
+       
+#if 0
+       /* this appears to be the only way to check if we are allowed
+          to write to an area. */
+       if(copy_to_user(arg,&user_trig,sizeof(comedi_trig)))
+               return -EFAULT;
+#endif
+       
+       if(user_trig.subdev>=dev->n_subdevices){
+               DPRINTK("%d no such subdevice\n",user_trig.subdev);
+               return -ENODEV;
+       }
+
+       s=dev->subdevices+user_trig.subdev;
+       if(s->type==COMEDI_SUBD_UNUSED){
+               DPRINTK("%d not used device\n",user_trig.subdev);
+               return -EIO;
+       }
+       
+       /* are we locked? (ioctl lock) */
+       if(s->lock && s->lock!=file){
+               DPRINTK("device locked\n");
+               return -EACCES;
+       }
+
+       /* are we busy? */
+       if(s->busy){
+               DPRINTK("device busy\n");
+               return -EBUSY;
+       }
+       s->busy=file;
+
+       /* make sure channel/gain list isn't too long */
+       if(user_trig.n_chan > s->len_chanlist){
+               DPRINTK("channel/gain list too long %d > %d\n",user_trig.n_chan,s->len_chanlist);
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       
+       s->cur_trig=user_trig;
+       s->cur_trig.chanlist=NULL;
+       s->cur_trig.data=NULL;
+
+       /* load channel/gain list */
+       s->cur_trig.chanlist=kmalloc(s->cur_trig.n_chan*sizeof(int),GFP_KERNEL);
+       if(!s->cur_trig.chanlist){
+               DPRINTK("allocation failed\n");
+               ret = -ENOMEM;
+               goto cleanup;
+       }
+       
+       if(copy_from_user(s->cur_trig.chanlist,user_trig.chanlist,s->cur_trig.n_chan*sizeof(int))){
+               DPRINTK("fault reading chanlist\n");
+               ret = -EFAULT;
+               goto cleanup;
+       }
+       
+       /* make sure each element in channel/gain list is valid */
+       if((ret=check_chanlist(s,s->cur_trig.n_chan,s->cur_trig.chanlist))<0){
+               DPRINTK("bad chanlist\n");
+               goto cleanup;
+       }
+       
+       if(!s->prealloc_bufsz){
+               /* allocate temporary buffer */
+
+               if(s->subdev_flags&SDF_LSAMPL){
+                       bufsz=s->cur_trig.n*s->cur_trig.n_chan*sizeof(lsampl_t);
+               }else{
+                       bufsz=s->cur_trig.n*s->cur_trig.n_chan*sizeof(sampl_t);
+               }
+
+               if(!(s->cur_trig.data=kmalloc(bufsz,GFP_KERNEL))){
+                       DPRINTK("failed to allocate buffer\n");
+                       ret=-ENOMEM;
+                       goto cleanup;
+               }
+       }else{
+               bufsz=s->prealloc_bufsz;
+               if(!s->prealloc_buf){
+                       printk("comedi: bug: s->prealloc_buf=NULL\n");
+               }
+               s->cur_trig.data=s->prealloc_buf;
+       }
+       s->cur_trig.data_len=bufsz;
+
+#if 0
+/* debugging */
+memset(s->cur_trig.data,0xef,bufsz);
+#endif
+
+       s->buf_int_ptr=0;
+       s->buf_int_count=0;
+if(s->subdev_flags & SDF_READABLE){
+       s->buf_user_ptr=0;
+       s->buf_user_count=0;
+}
+       
+#if 0
+       if(user_trig.data==NULL){
+               /* XXX this *should* indicate that we want to transfer via read/write */
+               ret=-EINVAL;
+               goto cleanup;
+       }
+#endif
+
+       if(s->subdev_flags & SDF_WRITEABLE){
+               if(s->subdev_flags & SDF_READABLE){
+                       /* bidirectional, so we defer to trig structure */
+                       if(user_trig.flags&TRIG_WRITE){
+                               reading=0;
+                       }else{
+                               reading=1;
+                       }
+               }else{
+                       reading=0;
+               }
+       }else{
+               /* subdev is read-only */
+               reading=1;
+       }
+       if(!reading && user_trig.data){
+               if(s->subdev_flags&SDF_LSAMPL){
+                       i=s->cur_trig.n*s->cur_trig.n_chan*sizeof(lsampl_t);
+               }else{
+                       i=s->cur_trig.n*s->cur_trig.n_chan*sizeof(sampl_t);
+               }
+               if(copy_from_user(s->cur_trig.data,user_trig.data,i)){
+                       DPRINTK("bad address %p,%p (%d)\n",s->cur_trig.data,user_trig.data,i);
+                       ret=-EFAULT;
+                       goto cleanup;
+               }
+       }
+
+       if(s->cur_trig.mode>=5 || s->trig[s->cur_trig.mode]==NULL){
+               DPRINTK("bad mode %d\n",s->cur_trig.mode);
+               ret=-EINVAL;
+               goto cleanup;
+       }
+
+       /* mark as non-RT operation */
+       s->cur_trig.flags &= ~TRIG_RT;
+
+       s->subdev_flags|=SDF_RUNNING;
+
+       ret=s->trig[s->cur_trig.mode](dev,s,&s->cur_trig);
+       
+       if(ret==0)return 0;
+
+       if(ret<0)goto cleanup;
+
+       if(s->subdev_flags&SDF_LSAMPL){
+               i=ret*sizeof(lsampl_t);
+       }else{
+               i=ret*sizeof(sampl_t);
+       }
+       if(i>bufsz){
+               printk("comedi: (bug) trig returned too many samples\n");
+               i=bufsz;
+       }
+       if(reading){
+               if(copy_to_user(user_trig.data,s->cur_trig.data,i)){
+                       ret=-EFAULT;
+                       goto cleanup;
+               }
+       }
+cleanup:
+
+       do_become_nonbusy(dev,s);
+       
+       return ret;
+}
+
+/*
+       COMEDI_CMD
+       command ioctl
+       
+       arg:
+               pointer to cmd structure
+       
+       reads:
+               cmd structure at arg
+               channel/range list
+       
+       writes:
+               modified cmd structure at arg
+
+*/
+static int do_cmd_ioctl(comedi_device *dev,void *arg,void *file)
+{
+       comedi_cmd user_cmd;
+       comedi_subdevice *s;
+       int ret=0;
+       
+DPRINTK("entering do_cmd_ioctl()\n");
+       if(copy_from_user(&user_cmd,arg,sizeof(comedi_cmd))){
+               DPRINTK("bad cmd address\n");
+               return -EFAULT;
+       }
+       
+       if(user_cmd.subdev>=dev->n_subdevices){
+               DPRINTK("%d no such subdevice\n",user_cmd.subdev);
+               return -ENODEV;
+       }
+
+       s=dev->subdevices+user_cmd.subdev;
+       if(s->type==COMEDI_SUBD_UNUSED){
+               DPRINTK("%d not valid subdevice\n",user_cmd.subdev);
+               return -EIO;
+       }
+       
+       if(!s->do_cmd){
+               DPRINTK("subdevice does not support commands\n");
+               return -EIO;
+       }
+       
+       /* are we locked? (ioctl lock) */
+       if(s->lock && s->lock!=file){
+               DPRINTK("subdevice locked\n");
+               return -EACCES;
+       }
+
+       /* are we busy? */
+       if(s->busy){
+               DPRINTK("subdevice busy\n");
+               return -EBUSY;
+       }
+       s->busy=file;
+
+       /* make sure channel/gain list isn't too long */
+       if(user_cmd.chanlist_len > s->len_chanlist){
+               DPRINTK("channel/gain list too long %d > %d\n",user_cmd.chanlist_len,s->len_chanlist);
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       s->cmd=user_cmd;
+       s->cmd.chanlist=NULL;
+       s->cmd.data=NULL;
+
+       /* load channel/gain list */
+       /* we should have this already allocated */
+       s->cmd.chanlist=kmalloc(s->cmd.chanlist_len*sizeof(int),GFP_KERNEL);
+       if(!s->cmd.chanlist){
+               DPRINTK("allocation failed\n");
+               ret = -ENOMEM;
+               goto cleanup;
+       }
+       
+       if(copy_from_user(s->cmd.chanlist,user_cmd.chanlist,s->cmd.chanlist_len*sizeof(int))){
+               DPRINTK("fault reading chanlist\n");
+               ret = -EFAULT;
+               goto cleanup;
+       }
+       
+       /* make sure each element in channel/gain list is valid */
+       if((ret=check_chanlist(s,s->cmd.chanlist_len,s->cmd.chanlist))<0){
+               DPRINTK("bad chanlist\n");
+               goto cleanup;
+       }
+       
+       if(!s->prealloc_bufsz){
+               ret=-ENOMEM;
+               DPRINTK("no buffer (?)\n");
+               goto cleanup;
+       }
+       s->cmd.data_len=s->prealloc_bufsz;
+
+       s->buf_int_ptr=0;
+       s->buf_int_count=0;
+if(s->subdev_flags & SDF_READABLE){
+       s->buf_user_ptr=0;
+       s->buf_user_count=0;
+}
+       
+       /* mark as non-RT operation */
+       s->cmd.flags &= ~TRIG_RT;
+
+       s->subdev_flags|=SDF_RUNNING;
+
+       ret=s->do_cmd(dev,s);
+       
+       if(ret==0)return 0;
+
+cleanup:
+       do_become_nonbusy(dev,s);
+       
+       return ret;
+}
+
+
+/*
+       COMEDI_LOCK
+       lock subdevice
+       
+       arg:
+               subdevice number
+       
+       reads:
+               none
+       
+       writes:
+               none
+
+       non-RT linux always controls rtcomedi_lock_semaphore.  If an
+       RT-linux process wants the lock, it first checks rtcomedi_lock_semaphore.
+       If it is 1, it knows it is pre-empting this function, and fails.
+       Obviously, if RT-linux fails to get a lock, it *must* allow
+       linux to run, since that is the only way to free the lock.
+       
+       This function is not SMP compatible.
+
+       necessary locking:
+       - ioctl/rt lock  (this type)
+       - lock while subdevice busy
+       - lock while subdevice being programmed
+       
+*/
+
+volatile int rtcomedi_lock_semaphore=0;
+
+static int do_lock_ioctl(comedi_device *dev,unsigned int arg,void * file)
+{
+       int ret=0;
+       comedi_subdevice *s;
+       
+       if(arg>=dev->n_subdevices)
+               return -EINVAL;
+       s=dev->subdevices+arg;
+       
+       if(s->busy)
+               return -EBUSY;
+
+       rtcomedi_lock_semaphore=1;
+       
+       if(s->lock && s->lock!=file){
+               ret=-EACCES;
+       }else{
+               s->lock=file;
+       }
+       
+       rtcomedi_lock_semaphore=0;
+
+       if(ret<0)
+               return ret;
+
+#if 0
+       if(s->lock_f)
+               ret=s->lock_f(dev,s);
+#endif
+
+       return ret;
+}
+
+
+/*
+       COMEDI_UNLOCK
+       unlock subdevice
+       
+       arg:
+               subdevice number
+       
+       reads:
+               none
+       
+       writes:
+               none
+
+       This function isn't protected by the semaphore, since
+       we already own the lock.
+*/
+static int do_unlock_ioctl(comedi_device *dev,unsigned int arg,void * file)
+{
+       comedi_subdevice *s;
+       
+       if(arg>=dev->n_subdevices)
+               return -EINVAL;
+       s=dev->subdevices+arg;
+       
+       if(s->busy)
+               return -EBUSY;
+
+       if(s->lock && s->lock!=file)
+               return -EACCES;
+       
+       if(s->lock==file){
+#if 0
+               if(s->unlock)
+                       s->unlock(dev,s);
+#endif
+
+               s->lock=NULL;
+       }
+
+       return 0;
+}
+
+static int do_cancel(comedi_device *dev,comedi_subdevice *s);
+/*
+       COMEDI_CANCEL
+       cancel acquisition ioctl
+       
+       arg:
+               subdevice number
+       
+       reads:
+               nothing
+       
+       writes:
+               nothing
+
+*/
+static int do_cancel_ioctl(comedi_device *dev,unsigned int arg,void *file)
+{
+       comedi_subdevice *s;
+       
+       if(arg>=dev->n_subdevices)
+               return -EINVAL;
+       s=dev->subdevices+arg;
+       
+       if(s->lock && s->lock!=file)
+               return -EACCES;
+       
+       if(!s->busy)
+               return 0;
+
+       if(s->busy!=file)
+               return -EBUSY;
+
+       return do_cancel(dev,s);
+}
+       
+static int do_cancel(comedi_device *dev,comedi_subdevice *s)
+{
+       int ret=0;
+
+       if((s->subdev_flags&SDF_RUNNING) && s->cancel)
+               ret=s->cancel(dev,s);
+
+       do_become_nonbusy(dev,s);
+
+       return ret;
+}
+
+#ifdef LINUX_V22
+/*
+   comedi_mmap_v22
+
+   mmap issues:
+       - mmap has issues with lock and busy
+       - mmap has issues with reference counting
+       - RT issues?
+
+   unmapping:
+       vm_ops->unmap()
+       - this needs to call comedi_cancel, or whatever.
+ */
+static int comedi_mmap_v22(struct file * file, struct vm_area_struct *vma)
+{
+       kdev_t minor=MINOR(RDEV_OF_FILE(file));
+       comedi_device *dev=comedi_devices+minor;
+       comedi_subdevice *s;
+       int size;
+       unsigned long offset;
+
+#if LINUX_VERSION_CODE < 0x020300
+       offset=vma->vm_offset;
+#else
+       offset=0;       /* XXX */
+#endif
+       if(offset >= dev->n_subdevices)
+               return -EIO;
+       s=dev->subdevices+offset;
+
+       if((vma->vm_flags & VM_WRITE) && !(s->subdev_flags & SDF_WRITEABLE))
+               return -EINVAL;
+
+       if((vma->vm_flags & VM_READ) && !(s->subdev_flags & SDF_READABLE))
+               return -EINVAL;
+
+       size = vma->vm_end - vma->vm_start;
+       if(size>(1<<16))
+               return -EINVAL;
+
+       if(remap_page_range(vma->vm_start, virt_to_phys(s->prealloc_buf),
+               size,vma->vm_page_prot))
+               return -EAGAIN;
+       
+       vma->vm_file=file;
+#if 0
+       file->f_count++;
+#else
+       file_atomic_inc(&file->f_count);
+#endif
+
+       /* mark subdev as mapped */
+       
+       /* call subdev about mmap, if necessary */
+printk("mmap done\n");
+
+       return 0;
+}
+
+#if 0
+/*
+   I can't find a driver that notices when it gets unmapped.
+ */
+static void *comedi_unmap(struct vm_area_struct *area,unsigned long x,size_t y)
+{
+       printk("comedi unmap\n");
+
+       return NULL;
+}
+#endif
+
+#endif
+
+
+static ssize_t comedi_write_v22(struct file *file,const char *buf,size_t nbytes,loff_t *offset)
+{
+       comedi_device *dev;
+       comedi_subdevice *s;
+       int n,m,count=0,retval=0;
+       DECLARE_WAITQUEUE(wait,current);
+       int sample_size;
+       void *buf_ptr;
+       unsigned int buf_len;
+
+       dev=comedi_devices+MINOR(RDEV_OF_FILE(file));
+       s=dev->subdevices+file->f_pos;
+
+       if(s->subdev_flags&SDF_LSAMPL){
+               sample_size=sizeof(lsampl_t);
+       }else{
+               sample_size=sizeof(sampl_t);
+       }
+       if(nbytes%sample_size)
+               nbytes-=nbytes%sample_size;
+
+       if(!nbytes)return 0;
+
+       if(!(s->subdev_flags&SDF_WRITEABLE))
+               return -EIO;
+
+       if(!s->busy){
+               buf_ptr=s->prealloc_buf;
+               buf_len=s->prealloc_bufsz;
+       }else{
+               if(s->busy != file)
+                       return -EACCES;
+
+               buf_ptr=s->cur_trig.data;
+               buf_len=s->cur_trig.data_len;
+       }
+
+       if(!buf_ptr)
+               return -EIO;
+
+       add_wait_queue(&dev->wait,&wait);
+       while(nbytes>0 && !retval){
+               current->state=TASK_INTERRUPTIBLE;
+
+               n=nbytes;
+
+               m=buf_len-(s->buf_user_count-s->buf_int_count);
+               if(s->buf_user_ptr+m > buf_len){
+                       m=buf_len - s->buf_user_ptr;
+               }
+               if(m<n)n=m;
+
+               if(n==0){
+                       if(file->f_flags&O_NONBLOCK){
+                               retval=-EAGAIN;
+                               break;
+                       }
+                       if(signal_pending(current)){
+                               retval=-ERESTARTSYS;
+                               break;
+                       }
+                       if(!(s->subdev_flags&SDF_RUNNING)){
+                               do_become_nonbusy(dev,s);
+                               break;
+                       }
+                       schedule();
+                       continue;
+               }
+               m=copy_from_user(buf_ptr+s->buf_user_ptr,buf,n);
+               if(m) retval=-EFAULT;
+               n-=m;
+               
+               count+=n;
+               nbytes-=n;
+               s->buf_user_ptr+=n;
+               s->buf_user_count+=n;
+
+               if(s->buf_user_ptr>=buf_len ){
+                       s->buf_user_ptr=0;
+               }
+
+               buf+=n;
+               break;  /* makes device work like a pipe */
+       }
+       current->state=TASK_RUNNING;
+       remove_wait_queue(&dev->wait,&wait);
+
+       return (count ? count : retval);
+}
+
+
+static ssize_t comedi_read_v22(struct file * file,char *buf,size_t nbytes,loff_t *offset)
+{
+       comedi_device *dev;
+       comedi_subdevice *s;
+       int n,m,count=0,retval=0;
+       DECLARE_WAITQUEUE(wait,current);
+       int sample_size;
+
+       dev=comedi_devices+MINOR(RDEV_OF_FILE(file));
+       if(file->f_pos>=dev->n_subdevices)
+               return -EIO;
+       s=dev->subdevices+file->f_pos;
+
+       if(s->subdev_flags&SDF_LSAMPL){
+               sample_size=sizeof(lsampl_t);
+       }else{
+               sample_size=sizeof(sampl_t);
+       }
+       if(nbytes%sample_size)
+               nbytes-=nbytes%sample_size;
+
+       if(!nbytes)return 0;
+
+       if(!s->busy)
+               return 0;
+
+       if(!s->cur_trig.data || !(s->subdev_flags&SDF_READABLE))
+               return -EIO;
+
+       if(s->busy != file)
+               return -EACCES;
+
+       add_wait_queue(&dev->wait,&wait);
+       while(nbytes>0 && !retval){
+               current->state=TASK_INTERRUPTIBLE;
+
+               n=nbytes;
+
+               m=s->buf_int_count-s->buf_user_count;
+               if(m>s->cur_trig.data_len){
+                       s->buf_user_count=s->buf_int_count;
+                       s->buf_user_ptr=s->buf_int_ptr;
+                       retval=-EINVAL; /* OVERRUN */
+                       break;
+               }
+               if(s->buf_user_ptr+m > s->cur_trig.data_len){
+                       m=s->cur_trig.data_len - s->buf_user_ptr;
+#if 0
+printk("m is %d\n",m);
+#endif
+               }
+               if(m<n)n=m;
+
+               if(n==0){
+                       if(!(s->subdev_flags&SDF_RUNNING)){
+                               do_become_nonbusy(dev,s);
+                               retval=-EINVAL;
+                               break;
+                       }
+                       if(file->f_flags&O_NONBLOCK){
+                               retval=-EAGAIN;
+                               break;
+                       }
+                       if(signal_pending(current)){
+                               retval=-ERESTARTSYS;
+                               break;
+                       }
+                       schedule();
+                       continue;
+               }
+               m=copy_to_user(buf,((void *)(s->cur_trig.data))+s->buf_user_ptr,n);
+               if(m) retval=-EFAULT;
+               n-=m;
+               
+               count+=n;
+               nbytes-=n;
+               s->buf_user_ptr+=n;
+               s->buf_user_count+=n;
+
+               if(s->buf_user_ptr>=s->cur_trig.data_len ){
+                       s->buf_user_ptr=0;
+               }
+
+               buf+=n;
+               break;  /* makes device work like a pipe */
+       }
+       if(!(s->subdev_flags&SDF_RUNNING) && s->buf_int_count-s->buf_user_count==0){
+               do_become_nonbusy(dev,s);
+       }
+       current->state=TASK_RUNNING;
+       remove_wait_queue(&dev->wait,&wait);
+
+       return (count ? count : retval);
+}
+
+/*
+   This function restores a subdevice to an idle state.
+ */
+static void do_become_nonbusy(comedi_device *dev,comedi_subdevice *s)
+{
+#if 0
+       printk("becoming non-busy\n");
+#endif
+       /* we do this because it's useful for the non-standard cases */
+       s->subdev_flags &= ~SDF_RUNNING;
+
+       if(s->cur_trig.chanlist){
+               kfree(s->cur_trig.chanlist);
+               s->cur_trig.chanlist=NULL;
+       }
+
+       if(s->cur_trig.data){
+               if(s->cur_trig.data != s->prealloc_buf)
+                       kfree(s->cur_trig.data);
+
+               s->cur_trig.data=NULL;
+       }
+
+       s->buf_user_ptr=0;
+       s->buf_int_ptr=0;
+       s->buf_user_count=0;
+       s->buf_int_count=0;
+
+       s->busy=NULL;
+}
+
+/* no chance that these will change soon */
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+
+static loff_t comedi_lseek_v22(struct file *file,loff_t offset,int origin)
+{
+       comedi_device *dev;
+       loff_t new_offset;
+       
+       dev=comedi_devices+MINOR(RDEV_OF_FILE(file));
+
+       switch(origin){
+       case SEEK_SET:
+               new_offset = offset;
+               break;
+       case SEEK_CUR:
+               new_offset = file->f_pos + offset;
+               break;
+       case SEEK_END:
+               new_offset = dev->n_subdevices + offset;
+               break;
+       default:
+               return -EINVAL;
+       }
+       if(new_offset<0 || new_offset >= dev->n_subdevices)
+               return -EINVAL;
+
+       return file->f_pos=new_offset;
+}
+
+static int comedi_open(struct inode *inode,struct file *file)
+{
+       kdev_t minor=MINOR(inode->i_rdev);
+       comedi_device *dev;
+       static int in_comedi_open=0;
+       char mod[32];
+
+       if(minor>=COMEDI_NDEVICES)return -ENODEV;
+
+       dev=comedi_devices+minor;
+       if(dev->attached)
+               goto ok;
+       if(in_comedi_open && suser())
+               goto ok;
+
+       in_comedi_open=1;
+
+       sprintf(mod,"char-major-%i-%i",COMEDI_MAJOR,minor);
+       request_module(mod);
+
+       in_comedi_open=0;
+
+       if(dev->attached || suser())
+               goto ok;
+       return -ENODEV;
+
+ok:
+       MOD_INC_USE_COUNT;
+       if(dev->attached){
+               __MOD_INC_USE_COUNT(dev->driver->module);
+       }
+       dev->use_count++;
+
+       return 0;
+}
+
+static int comedi_close_v22(struct inode *inode,struct file *file)
+{
+       comedi_device *dev=comedi_devices+MINOR(inode->i_rdev);
+       comedi_subdevice *s;
+       int i;
+
+       for(i=0;i<dev->n_subdevices;i++){
+               s=dev->subdevices+i;
+
+               if(s->busy==file){
+                       do_cancel(dev,s);
+               }
+               if(s->lock==file){
+                       s->lock=NULL;
+               }
+       }
+
+       MOD_DEC_USE_COUNT;
+       if(dev->attached){
+               __MOD_DEC_USE_COUNT(dev->driver->module);
+       }
+
+       dev->use_count--;
+       
+       return 0;
+}
+
+
+/*
+       kernel compatibility
+*/
+
+#ifdef LINUX_V20
+
+static int comedi_write_v20(struct inode *inode,struct file *file,const char *buf,int nbytes)
+{
+       return comedi_write_v22(file,buf,nbytes,NULL);
+}
+
+static int comedi_read_v20(struct inode *inode,struct file *file,char *buf,int nbytes)
+{
+       return comedi_read_v22(file,buf,nbytes,NULL);
+}
+
+static int comedi_lseek_v20(struct inode * inode,struct file *file,off_t offset,int origin)
+{
+       return comedi_lseek_v22(file,offset,origin);
+}
+
+static void comedi_close_v20(struct inode *inode,struct file *file)
+{
+       comedi_close_v22(inode,file);
+}
+
+#define comedi_ioctl_v20 comedi_ioctl
+#define comedi_open_v20 comedi_open
+
+static struct file_operations comedi_fops={
+       lseek           : comedi_lseek_v20,
+       ioctl           : comedi_ioctl_v20,
+       open            : comedi_open_v20,
+       release         : comedi_close_v20,
+       read            : comedi_read_v20,
+       write           : comedi_write_v20,
+};
+
+#endif
+
+#ifdef LINUX_V22
+
+#define comedi_ioctl_v22 comedi_ioctl
+#define comedi_open_v22 comedi_open
+
+static struct file_operations comedi_fops={
+       llseek          : comedi_lseek_v22,
+       ioctl           : comedi_ioctl_v22,
+       open            : comedi_open_v22,
+       release         : comedi_close_v22,
+       read            : comedi_read_v22,
+       write           : comedi_write_v22,
+       mmap            : comedi_mmap_v22,
+};
+#endif
+
+void mite_init(void);
+void mite_cleanup(void);
+void init_drivers(void);
+
+
+int init_module(void)
+{
+       printk("comedi: version " COMEDI_VERSION " - David Schleef <ds@stm.lbl.gov>\n");
+       if(register_chrdev(COMEDI_MAJOR,"comedi",&comedi_fops)){
+               printk("comedi: unable to get major %d\n",COMEDI_MAJOR);
+               return -EIO;
+       }
+       comedi_devices=(comedi_device *)kmalloc(sizeof(comedi_device)*COMEDI_NDEVICES,GFP_KERNEL);
+       if(!comedi_devices)
+               return -ENOMEM;
+       memset(comedi_devices,0,sizeof(comedi_device)*COMEDI_NDEVICES);
+#if 0
+       init_polling();
+#endif
+
+       /* XXX requires /proc interface */
+       comedi_proc_init();
+       
+#ifdef CONFIG_COMEDI_RTL
+       comedi_rtl_init();
+#endif
+#ifdef CONFIG_COMEDI_RTL_V1
+       comedi_rtlv1_init();
+#endif
+#ifdef CONFIG_COMEDI_RTAI
+       comedi_rtai_init();
+#endif
+#if 0
+#ifdef CONFIG_COMEDI_MITE
+       mite_init();
+#endif
+#endif
+       init_drivers();
+
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       int i;
+
+       if(MOD_IN_USE)
+               printk("comedi: module in use -- remove delayed\n");
+
+       unregister_chrdev(COMEDI_MAJOR,"comedi");
+
+       comedi_proc_cleanup();
+#if 0
+       comedi_polling_cleanup();
+#endif
+       for(i=0;i<COMEDI_NDEVICES;i++){
+               if(comedi_devices[i].attached)
+                       comedi_device_detach(comedi_devices+i);
+       }
+       kfree(comedi_devices);
+
+#ifdef CONFIG_COMEDI_RTL
+       comedi_rtl_cleanup();
+#endif
+#ifdef CONFIG_COMEDI_RTL_V1
+       comedi_rtlv1_cleanup();
+#endif
+#ifdef CONFIG_COMEDI_RTAI
+       comedi_rtai_cleanup();
+#endif
+#if 0
+#ifdef CONFIG_COMEDI_MITE
+       mite_cleanup();
+#endif
+#endif
+
+}
+
+
+void comedi_error(comedi_device *dev,const char *s)
+{
+       rt_printk("comedi%d: %s: %s\n",dev->minor,dev->driver->driver_name,s);
+}
+
+void comedi_done(comedi_device *dev,comedi_subdevice *s)
+{
+#if 0
+       DPRINTK("comedi_done\n");
+#endif
+
+       if(!(s->cur_trig.flags&TRIG_RT))
+               wake_up_interruptible(&dev->wait);
+       else if(s->cb_mask&COMEDI_CB_EOA)
+               s->cb_func(COMEDI_CB_EOA,s->cb_arg);
+
+       s->subdev_flags &= ~SDF_RUNNING;
+}
+
+void comedi_bufcheck(comedi_device *dev,comedi_subdevice *s)
+{
+#if 0
+       DPRINTK("comedi_bufcheck\n");
+#endif
+
+#if 0
+       if((!(s->cur_trig.flags&TRIG_RT)) &&
+          (s->buf_int_count-s->buf_user_count >= 16))
+               wake_up_interruptible(&dev->wait);
+#else
+       if(!(s->cur_trig.flags&TRIG_RT))
+               wake_up_interruptible(&dev->wait);
+#endif
+}
+
+/*
+   this function should be called by your interrupt routine
+   at end-of-scan events
+ */
+void comedi_eos(comedi_device *dev,comedi_subdevice *s)
+{
+       if(s->cb_mask&COMEDI_CB_EOS){
+               s->cb_func(COMEDI_CB_EOS,s->cb_arg);
+               return;
+       }
+       if((s->cur_trig.flags&TRIG_WAKE_EOS)){
+               wake_up_interruptible(&dev->wait);
+       }
+}
+
+/*
+   this function should be called by your interrupt routine
+   at buffer rollover events
+ */
+void comedi_eobuf(comedi_device *dev,comedi_subdevice *s)
+{
+       if(s->cb_mask&COMEDI_CB_EOBUF){
+               s->cb_func(COMEDI_CB_EOBUF,s->cb_arg);
+       }
+}
+
+
+int di_unpack(unsigned int bits,comedi_trig *it)
+{
+       int chan;
+       int i;
+
+       for(i=0;i<it->n_chan;i++){
+               chan=CR_CHAN(it->chanlist[i]);
+               it->data[i]=(bits>>chan)&1;
+       }
+
+       return i;
+}
+
+int do_pack(unsigned int *bits,comedi_trig *it)
+{
+       int chan;
+       int mask;
+       int i;
+
+       for(i=0;i<it->n_chan;i++){
+               chan=CR_CHAN(it->chanlist[i]);
+               mask=1<<chan;
+               (*bits) &= ~mask;
+               if(it->data[i])
+                       (*bits) |=mask;
+       }
+
+       return i;
+}
+
+int mode_to_command(comedi_cmd *cmd,comedi_trig *it)
+{
+       memset(cmd,0,sizeof(comedi_cmd));
+       cmd->subdev=it->subdev;
+       cmd->chanlist_len=it->n_chan;
+       cmd->chanlist=it->chanlist;
+       cmd->data=it->data;
+       cmd->data_len=it->data_len;
+
+       cmd->start_src=TRIG_NOW;
+
+       switch(it->mode){
+       case 1:
+               cmd->scan_begin_src=TRIG_FOLLOW;
+               cmd->convert_src=TRIG_TIMER;
+               cmd->convert_arg=it->trigvar;
+               cmd->scan_end_src=TRIG_COUNT;
+               cmd->scan_end_arg=it->n_chan;
+               cmd->stop_src=TRIG_COUNT;
+               cmd->stop_arg=it->n;
+               
+               break;
+       case 2:
+               cmd->scan_begin_src=TRIG_TIMER;
+               cmd->scan_begin_arg=it->trigvar;
+               cmd->convert_src=TRIG_TIMER;
+               cmd->convert_arg=it->trigvar1;
+               cmd->scan_end_src=TRIG_COUNT;
+               cmd->scan_end_arg=it->n_chan;
+               cmd->stop_src=TRIG_COUNT;
+               cmd->stop_arg=it->n;
+               
+               break;
+       case 3:
+               cmd->scan_begin_src=TRIG_FOLLOW;
+               cmd->convert_src=TRIG_EXT;
+               cmd->convert_arg=it->trigvar;
+               cmd->scan_end_src=TRIG_COUNT;
+               cmd->scan_end_arg=it->n_chan;
+               cmd->stop_src=TRIG_COUNT;
+               cmd->stop_arg=it->n;
+
+               break;
+       case 4:
+               cmd->scan_begin_src=TRIG_EXT;
+               cmd->scan_begin_arg=it->trigvar;
+               cmd->convert_src=TRIG_TIMER;
+               cmd->convert_arg=it->trigvar1;
+               cmd->scan_end_src=TRIG_COUNT;
+               cmd->scan_end_arg=it->n_chan;
+               cmd->stop_src=TRIG_COUNT;
+               cmd->stop_arg=it->n;
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int command_to_mode(comedi_trig *it,comedi_cmd *cmd)
+{
+       it->subdev=cmd->subdev;
+       it->flags=0;
+       it->n_chan=cmd->chanlist_len;
+       it->chanlist=cmd->chanlist;
+       it->data=cmd->data;
+       it->data_len=cmd->data_len;
+
+       if(cmd->start_src==TRIG_NOW &&
+          cmd->scan_begin_src==TRIG_FOLLOW &&
+          cmd->convert_src==TRIG_TIMER &&
+          cmd->scan_end_src==TRIG_COUNT &&
+          cmd->stop_src==TRIG_COUNT){
+               /* mode 1 */
+
+               it->mode=1;
+               it->trigsrc=0;
+               it->trigvar=0;
+               it->n=cmd->stop_arg;
+
+               return 0;
+       }
+       if(cmd->start_src==TRIG_NOW &&
+          cmd->scan_begin_src==TRIG_TIMER &&
+          cmd->convert_src==TRIG_TIMER &&
+          cmd->scan_end_src==TRIG_COUNT &&
+          cmd->stop_src==TRIG_COUNT){
+               /* mode 2 */
+
+               it->mode=2;
+               it->trigsrc=0;
+               it->trigvar=cmd->scan_begin_arg;
+               it->trigvar1=cmd->convert_arg;
+               it->n=cmd->stop_arg;
+
+               return 0;
+       }
+       if(cmd->start_src==TRIG_NOW &&
+          cmd->scan_begin_src==TRIG_FOLLOW &&
+          cmd->convert_src==TRIG_EXT &&
+          cmd->scan_end_src==TRIG_COUNT &&
+          cmd->stop_src==TRIG_COUNT){
+               /* mode 3 */
+               /* nobody actually uses mode 3, so... */
+
+               return -EINVAL;
+       }
+       if(cmd->start_src==TRIG_NOW &&
+          cmd->scan_begin_src==TRIG_EXT &&
+          cmd->convert_src==TRIG_TIMER &&
+          cmd->scan_end_src==TRIG_COUNT &&
+          cmd->stop_src==TRIG_COUNT){
+               /* mode 4 */
+
+               it->mode=4;
+               it->trigsrc=0;
+               it->trigvar=cmd->scan_begin_arg;
+               it->trigvar1=cmd->convert_arg;
+               it->n=cmd->stop_arg;
+
+               return 0;
+       }
+       return -EINVAL;
+}
+
+#ifdef CONFIG_COMEDI_RT
+
+static struct comedi_irq_struct *comedi_irqs;
+
+int comedi_request_irq(unsigned irq,void (*handler)(int, void *,struct pt_regs *),
+               unsigned long flags,const char *device,void *dev_id)
+{
+       struct comedi_irq_struct *it;
+       int ret;
+
+       it=kmalloc(sizeof(*it),GFP_KERNEL);
+       if(!it)
+               return -ENOMEM;
+
+       it->handler=handler;
+       it->irq=irq;
+       it->dev_id=dev_id;
+       it->flags=flags;
+
+       ret=request_irq(irq,handler,flags&~SA_PRIORITY,device,dev_id);
+       if(ret<0){
+               kfree(it);
+               return ret;
+       }
+
+       if(flags&SA_PRIORITY){
+               get_priority_irq(it);
+       }
+
+       it->next=comedi_irqs;
+       comedi_irqs=it;
+
+       return 0;
+}
+
+int comedi_change_irq_flags(unsigned int irq,void *dev_id,unsigned long flags)
+{
+       struct comedi_irq_struct *it;
+
+       it=get_irq_struct(irq);
+       if(it){
+               if((it->flags&~SA_PRIORITY)!=(flags&~SA_PRIORITY))
+                       return -EINVAL;
+
+               if((it->flags&SA_PRIORITY)==(flags&SA_PRIORITY))
+                       return 0;
+
+               it->flags=flags;
+               if(flags&SA_PRIORITY){
+                       return get_priority_irq(it);
+               }else{
+                       return free_priority_irq(it);
+               }
+       }
+
+       return -EINVAL;
+}
+
+void comedi_free_irq(unsigned int irq,void *dev_id)
+{
+       struct comedi_irq_struct *it,*prev;
+
+       prev=NULL;
+       for(it=comedi_irqs;it;it=it->next){
+               if(it->irq==irq){
+                       break;
+               }
+               prev=it;
+       }
+       if(it->flags&SA_PRIORITY)
+               free_priority_irq(it);
+
+       free_irq(it->irq,it->dev_id);
+
+       if(prev) prev->next=it->next;
+       else comedi_irqs=it->next;
+
+       kfree(it);
+}
+
+struct comedi_irq_struct *get_irq_struct(unsigned int irq)
+{
+       struct comedi_irq_struct *it;
+
+       for(it=comedi_irqs;it;it=it->next){
+               if(it->irq==irq){
+                       return it;
+               }
+       }
+       return NULL;
+}
+
+#endif
+
diff --git a/comedi/old/atmio-16d.c b/comedi/old/atmio-16d.c
new file mode 100644 (file)
index 0000000..a427de3
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+    module/atmio-16d.c
+    hardware driver for National Instruments AT-MIO-16D
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1995 Claus Schroeter <clausi@chemie.fu-berlin.de>
+    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.
+
+*/
+
+/*
+       This driver is an adaptation of one written by clausi
+       
+       specifications can be found in NI document 320489.pdf
+*/
+
+#define Command_Register_1             0x00    /* wo */
+#define Status_Register                        0x00    /* ro */
+#define Command_Register_2             0x02    /* wo */
+
+#define Start_Convert_Register         0x08    /* wo */
+#define Start_DAQ_Register             0x0a    /* wo */
+#define AD_Clear_Register              0x0c    /* wo */
+#define External_Strobe_Register       0x0e    /* wo */
+
+#define DAC0_Register                  0x10    /* wo */
+#define DAC1_Register                  0x12    /* wo */
+#define INT2CLR_Register               0x14    /* wo */
+
+#define Mux_Counter_Register           0x04    /* wo */
+#define Mux_Gain_Register              0x06    /* wo */
+#define AD_FIFO_Register               0x16    /* ro */
+#define DMA_TC_INT_Clear_Register      0x16    /* wo */
+
+#define Am9513A_Data_Register          0x18    /* rw */
+#define Am9513A_Command_Register       0x1a    /* wo */
+#define Am9513A_Status_Register                0x1a    /* ro */
+
+#define MIO_16_Digital_Input_Register  0x1c    /* ro */
+#define MIO_16_Digital_Output_Register 0x1c    /* wo */
+
+#define RTSI_Switch_Shift_Register     0x1e    /* wo 8 */
+#define RTSI_Switch_Strobe_Register    0x1f    /* wo 8 */
+
+#define DIO_24_PORTA_Register          0x00    /* rw 8 */
+#define DIO_24_PORTB_Register          0x01    /* rw 8 */
+#define DIO_24_PORTC_Register          0x02    /* rw 8 */
+#define DIO_24_CNFG_Register           0x03    /* wo 8 */
+
+
+#define _B(b)          ((struct bitchan){_B_CHAN,(b)})
+
+#define _B_CHAN                Command_Register_1
+#define DAQSTOPINTEN   _bit9
+#define        TCINTEN         _bit8
+#define CONVINTEN      _bit7
+#define DBDMA          _bit6
+#define DMAEN          _bit5
+#define DAQEN          _bit4
+#define SCANEN         _bit3
+#define SCANDIV                _bit2
+#define CNT32          _bit1
+#define TWOSCADC       _bit0
+#undef _B_CHAN
+
+#define _B_CHAN                Status_Register
+#define GINT           _bit15
+#define DAQSTOPINT     _bit14
+#define CONVAVAIL      _bit13
+#define OUT2INT                _bit12
+#define DAQPROG                _bit11
+#define DMATCINT       _bit10
+#define OVERFLOW       _bit9
+#define OVERRUN                _bit8
+#define GAIN1          _bit7
+#define GAIN0          _bit6
+#define DMACH          _bit5
+#define MUX1EN         _bit4
+#define MUX0EN         _bit3
+#define MA2            _bit2
+#define MA1            _bit1
+#define MA0            _bit0
+#undef _B_CHAN
+
+#define _B_CHAN                Command_Register_2
+#define DOUTEN1                _bit9
+#define DOUTEN0                _bit8
+#define INTEN          _bit7
+#define INT2EN         _bit6
+#define LDAC           _bit5
+#define SCN2           _bit4
+#define A4RCV          _bit3
+#define A4DRV          _bit2
+#define A2RCV          _bit1
+#define A2DRV          _bit0
+#undef _B_CHAN
+
+
+
+
+
+
diff --git a/comedi/old/atmio-16f.c b/comedi/old/atmio-16f.c
new file mode 100644 (file)
index 0000000..4829992
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+    module/ni_F.c
+    hardware driver for National Instruments AT-MIO-16F
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1995 Claus Schroeter <clausi@chemie.fu-berlin.de>
+    Copyright (C) 1999 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.
+
+*/
+
+/*
+       This driver is an adaptation of one written by clausi
+*/
+
+#include <comedi_module.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+#define ATMIO16F_SIZE 16
+
+#define Command_Register_1             0x00    /* wo */
+#define Command_Register_2             0x02    /* wo */
+#define Command_Register_3             0x04    /* wo */
+#define Command_Register_4             0x06    /* wo */
+#define Status_Register_1              0x18    /* ro */
+#define Status_Register_2              0x1a    /* ro */
+
+#define ADC_FIFO_Register              0x00    /* ro */
+#define CONFIGMEM_Register             0x08    /* wo */
+
+#define DAC0_Register                  0x10    /* wo */
+#define DAC1_Register                  0x12    /* wo */
+
+#define CONFIGMEMCLR_Register          0x1b    /* ro 8 */
+#define CONFIGMEMLD_Register           0x1b    /* wo 8 */
+#define DAQ_Clear_Register             0x19    /* ro 8 */
+#define DAQ_Start_Register             0x1d    /* ro 8 */
+#define Single_Conversion_Register     0x1d    /* wo 8 */
+#define ADC_Calibration_Register       0x1f    /* wo 8 */
+
+#define TMRREQ_Clear_Register          0x1f    /* ro 8 */
+#define DAC_Update_Register            0x18    /* wo */
+#define DAC_Clear_Register             0x1e    /* ro 8 */
+
+#define DMA_Channel_Clear_Register     0x0b    /* ro 8 */
+#define DMATCA_Clear_Register          0x19    /* wo 8 */
+#define DMATCB_Clear_Register          0x09    /* ro 8 */
+#define External_Strobe_Register       0x1e    /* wo 8 */
+#define Calibration_DAC_0_Load_Register        0x0a    /* wo 8 */
+#define Calibration_DAC_1_Load_Register        0x1a    /* wo 8 */
+
+#define Am9513A_Data_Register          0x14    /* rw */
+#define Am9513A_Command_Register       0x16    /* wo */
+#define Am9513A_Status_Register                0x16    /* ro */
+
+#define Digital_Input_Register         0x1c    /* ro */
+#define Digital_Output_Register                0x1c    /* wo */
+
+#define RTSI_Switch_Shift_Register     0x0c    /* wo 8 */
+#define RTSI_Switch_Strobe_Register    0x0e    /* wo 8 */
+
+
+#define EEPROMCS       _bit15
+#define SDATA          _bit14
+#define SCLK           _bit13
+#define SCANDIV                _bit12
+#define INTGATE                _bit10
+#define RETRIG_DIS     _bit9
+#define DAQEN          _bit8
+#define SCANEN         _bit7
+#define SCN2           _bit6
+#define CNT32          _bit5
+#define RTSITRIG       _bit4
+
+#define A4RCV          _bit15
+#define A4DRV          _bit14
+#define A2RCV          _bit13
+#define A2DRV          _bit12
+#define BIPDAC1                _bit11
+#define BIPDAC0                _bit10
+#define EXTREFDAC1     _bit9
+#define EXTREFDAC0     _bit8
+#define EISA_DMA       _bit7
+#define DMACHBB                _bits543
+#define DMACHAB                _bits210
+
+
+
+/* stuff i don't know */
+#define MIO_ADCLR 0
+#define BIPOLAR 0
+#define DIFF 0
+#define MIO_SCONV 0
+#define MIO_SR 0
+#define FIFOEF 0
+#define MIO_ADFIFO 0
+#define mio_reset_bitcmd(a,b) /* */
+#define mio_set_bitcmd(a,b) /* */
+
+
+
+#define DB_ERROR      (1<<0)
+#define DB_OVERFLOW   (1<<1)
+#define DB_HALF       (1<<2)
+#define DB_EMPTY      (1<<3)
+
+#define DAQ_RUNNING (1<<0)
+#define DAQ_DACUP   (1<<1)
+#define DAQ_SCONV   (1<<2)
+#define DAQ_BIPOLAR (1<<3)
+#define DAQ_DIFF    (1<<4)
+#define DAQ_SYNC    (1<<5)
+
+
+typedef struct {
+       int adc;
+       int dac0;
+} AdcVal;
+
+typedef struct{
+       int DaqMode;
+       int CurrentDac;
+       int WgenStat;
+       int admode[16];
+}ni_F_private;
+#define devpriv ((ni_F_private *)dev->private)
+
+int atmio16f_rem(comedi_device *dev);
+
+#if 0
+static void mio_SetDaqMode(int mode)
+{
+       devpriv->DaqMode = mode;
+}
+#endif
+
+/**********************************************************************/
+static int atmio16f_ai(comedi_device * dev, comedi_subdevice *s, comedi_trig * it)
+{
+       int chan,aref,range;
+       int i;
+
+       chan=CR_CHAN(it->chanlist[0]);
+       range=CR_RANGE(it->chanlist[0]);
+       aref=CR_AREF(it->chanlist[0]);
+
+       outw(0x00, dev->iobase + MIO_ADCLR);
+
+       /* set ADC UNIPOLAR/BIPOLAR mode */
+       if (range)
+               mio_reset_bitcmd(BIPOLAR, 0);
+       else
+               mio_set_bitcmd(BIPOLAR, 0);
+
+       /* set ADC DIFF/RSE mode */
+       if (aref)       /* XXX check */
+               mio_set_bitcmd(DIFF, 0);
+       else
+               mio_reset_bitcmd(DIFF, 0);
+
+       outw(0x00, dev->iobase + MIO_SCONV);
+
+       /* poll status register */
+       for(i=0;i<25;i++){
+               if( inw(dev->iobase + MIO_SR) & FIFOEF )
+                       break;
+               udelay(5);
+       }
+       if(i==25){
+               comedi_error(dev,"timeout");
+               return -ETIME;
+       }
+
+       /* read value */
+       it->data[0] = inw(dev->iobase + MIO_ADFIFO);
+
+       return 1;
+}
+
+
+#if 0
+/***********************************************************************
+ *
+ *  Doublebuffer FIFO mechanism
+ *
+ */
+
+#define MAX_DBUFSIZE 1024
+
+struct wait_queue *DaqWait = NULL;
+struct semaphore DaqSem = MUTEX;
+
+
+unsigned int DblBuf[2][MAX_DBUFSIZE + 1];
+
+unsigned int DblBufState = 0;
+
+unsigned int ActualReadCount = 0;
+unsigned int ActualWriteCount = 0;
+unsigned int ActualResidue = 0;
+
+unsigned int ActualWrite = 0;
+unsigned int ActualRead = 0;
+
+/***********************************************************************/
+void mio_DaqInit(void)
+{
+
+       DblBufState = 0;
+       ActualResidue = 0;
+       ActualWriteCount = 0;
+       ActualReadCount = 0;
+
+       ActualRead = 0;
+       ActualWrite = 0;
+       DaqMode = 0;
+
+       /*clear A/D circuit */
+       outw(0x0000, MIO_ADCLR);
+
+}
+
+
+/**********************************************************************/
+void mio_SwapWrite(void)
+{
+       ActualWrite = (ActualWrite ? 0 : 1);
+       DBGprint(DBG_DATA, ("Swapped Write to %d", ActualWrite));
+       ActualWriteCount = 0;
+       if (ActualResidue >= (2 * MAX_DBUFSIZE)) {      /* overflow buffer bit set */
+               DBGprint(DBG_DATA, ("Buffer Overflow"));
+               DblBufState |= (DB_ERROR | DB_OVERFLOW);
+       }
+}
+
+/**********************************************************************/
+void mio_SwapRead(void)
+{
+       ActualRead = (ActualRead ? 0 : 1);
+       DBGprint(DBG_DATA, ("Swapped Read to %d", ActualRead));
+       ActualReadCount = 0;
+}
+
+/**********************************************************************/
+void mio_AddDBuf(unsigned int val)
+{
+       if (ActualWriteCount < MAX_DBUFSIZE) {
+               DblBuf[ActualWrite][ActualWriteCount] = val;
+               ActualWriteCount++;
+               ActualResidue++;
+       } else {
+               mio_SwapWrite();
+       }
+       if (ActualResidue >= MAX_DBUFSIZE) {
+               DblBufState |= DB_HALF;
+               if (DaqMode & DAQ_SYNC)
+                       wake_up_interruptible(&DaqWait);
+       }
+}
+
+/**********************************************************************/
+unsigned int mio_GetDBuf(void)
+{
+       unsigned int ret = 0;
+
+       if (ActualResidue > 0) {
+               if (ActualReadCount < MAX_DBUFSIZE) {
+                       ret = DblBuf[ActualRead][ActualReadCount];
+                       ActualReadCount++;
+                       ActualResidue--;
+               } else {
+                       mio_SwapRead();
+               }
+               if (ActualResidue < MAX_DBUFSIZE)
+                       DblBufState &= ~DB_HALF;
+       } else {
+               ret = 0;
+               DblBufState |= DB_EMPTY;
+       }
+
+       DBGprint(DBG_DATA, ("residue=%d", ActualResidue));
+       return ret;
+}
+
+/**********************************************************************/
+int mio_SyncRead(int *buf, int count)
+{
+       int retval, i;
+       int dummy;
+       char *tbuf;
+
+
+       if (retval = verify_area(VERIFY_WRITE, buf, count))
+               return retval;
+
+
+       if (DblBufState & DB_EMPTY) {
+               DBGprint(DBG_DATA, ("Buffer Empty"));
+               return -EINTR;
+       }
+       if (!(DblBufState & DB_HALF) && (DaqMode & DAQ_RUNNING)) {
+               DBGprint(DBG_DATA, ("\nWaiting for DB_HALF"));
+               DaqMode |= DAQ_SYNC;
+               interruptible_sleep_on(&DaqWait);
+               DaqMode &= ~DAQ_SYNC;
+               DBGprint(DBG_DATA, ("\nGot DB_HALF"));
+       }
+       if (DblBufState & DB_OVERFLOW) {
+               DBGprint(DBG_DATA, ("Double Buffer Overflow"));
+               return -EOVERFLOW;
+       }
+       /* copy block to FS */
+       for (i = 0; i < count && i < ActualResidue; i++) {
+               dummy = mio_GetDBuf();
+               DBGprint(DBG_DATA, ("buf=%d", dummy));
+               if (DblBufState & DB_EMPTY) {
+                       DBGprint(DBG_DATA, ("count=%d", i));
+                       return i;
+               }
+               put_fs_long(dummy, (int *) &(buf[i]));
+               /*memcpy_tofs((int *) buf, &dummy ,sizeof(int)); */
+       }
+
+       return count;
+
+}
+
+/**********************************************************************/
+void mio_DaqFinish(void)
+{
+
+       DaqMode &= ~DAQ_RUNNING;
+       if (DaqMode & DAQ_SYNC)
+               wake_up_interruptible(&DaqWait);
+}
+
+/**********************************************************************/
+void mio_DaqStart(int mode)
+{
+
+       DBGprint(DBG_DATA, ("mode (0x%x)", mode));
+
+       mio_DaqInit();
+
+       DaqMode |= (DAQ_RUNNING | mode);
+
+       /*clear A/D circuit */
+       outw(0x0000, MIO_ADCLR);
+
+       /* set ADC UNIPOLAR/BIPOLAR mode */
+       if (DaqMode & DAQ_BIPOLAR)
+               mio_reset_bitcmd(BIPOLAR, 0);
+       else
+               mio_set_bitcmd(BIPOLAR, 0);
+
+       /* set ADC DIFF/RSE mode */
+       if (DaqMode & DAQ_DIFF)
+               mio_set_bitcmd(DIFF, 0);
+       else
+               mio_reset_bitcmd(DIFF, 0);
+
+       if (DaqMode & DAQ_SCONV) {
+               DBGprint(DBG_DATA, ("trigger conversion"));
+               outw(0x00, MIO_SCONV);  /* trigger first value */
+               while (!(inw(MIO_SR) & FIFOEF));        /* sync FIFOEF */
+       }
+       if (DaqMode & DAQ_DACUP)
+               printk("WGEN MODE\n");
+
+}
+
+/***********************************************************************/
+void mio_DaqIRQ(int status)
+{
+       comedi_sample s;
+
+       if (DaqMode & DAQ_RUNNING) {
+
+               /* read FIFO content into Buffer */
+               s.chan = dev->adchan;
+               s.job = dev->job;
+               while (inw(MIO_SR) & FIFOEF) {
+
+                       it->data = inw(dev->iobase + MIO_ADFIFO);
+
+               }
+
+               /* trigger next conversion */
+               if (DaqMode & DAQ_SCONV) {
+                       outw(0x0000, dev->iobase + MIO_SCONV);
+               }
+               if ((DaqMode & DAQ_DACUP) && !(WgenStat & WGEN_RUNNING)) {
+#if DEBUG
+                       if (dbgMask)
+                               printk("Finish DACUP DAQ Cycle\n");
+#endif
+                       DaqMode &= ~DAQ_RUNNING;
+                       while (!(inw(MIO_SR) & FIFOEF));        /* poll status */
+                       val = inw(MIO_ADFIFO) | (CurrentDac << 16);
+                       mio_AddDBuf(val);
+
+                       if (DaqMode & DAQ_SYNC)
+                               wake_up_interruptible(&DaqWait);
+               }
+       } else
+               printk("DAQ-INTERRUPT unknown status (0x%x)", status);
+
+}
+
+#endif
+
+int atmio16f_ao(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       return -EINVAL;
+}
+
+int atmio16f_di(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       return -EINVAL;
+}
+
+int atmio16f_do(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       return -EINVAL;
+}
+
+
+int atmio16f_init(comedi_device *dev,comedi_devconfig *it)
+{
+       int ret=0;
+       comedi_subdevice *s;
+       
+       if(strcmp(it->board_name,"at-mio-16f-5"))
+               return 0;
+       
+       dev->driver_name="atmio16f";
+       dev->board_name="at-mio-16f";
+       if(it->options[0])
+               dev->iobase=it->options[0];
+       else return -EINVAL;
+       
+       printk("comedi: atmio16f: 0x%04x\n",dev->iobase);
+       if(check_region(dev->iobase,ATMIO16F_SIZE)<0){
+               comedi_error(dev,"I/O port conflict");
+               return -EIO;
+       }
+       request_region(dev->iobase,ATMIO16F_SIZE,"atmio16f");
+       dev->iosize=ATMIO16F_SIZE;
+       
+       dev->n_subdevices=4;
+       
+       if((ret=alloc_subdevices(dev))<0)
+               goto cleanup;
+       if((ret=alloc_private(dev,sizeof(ni_F_private)))<0)
+               goto cleanup;
+       
+       s=dev->subdevices+0;
+       /* ai subdevice */
+       s->type=COMEDI_SUBD_AI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=16;
+       s->maxdata=0xfff;
+       s->range_type=RANGE_unknown;
+       s->trig[0]=atmio16f_ai;
+
+       s++;
+       /* ao subdevice */
+       s->type=COMEDI_SUBD_AO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->n_chan=2;
+       s->maxdata=0xfff;
+       s->range_type=RANGE_unknown;
+       s->trig[0]=atmio16f_ao;
+
+       s++;
+       /* di subdevice */
+       s->type=COMEDI_SUBD_DI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=8;
+       s->maxdata=1;
+       s->range_type=RANGE_digital;
+       s->trig[0]=atmio16f_di;
+
+       s++;
+       /* do subdevice */
+       s->type=COMEDI_SUBD_DO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->maxdata=1;
+       s->n_chan=8;
+       s->range_type=RANGE_digital;
+       s->trig[0]=atmio16f_do;
+
+       /* do some init */
+
+       dev->rem=atmio16f_rem;
+
+       return 1;
+cleanup:
+       atmio16f_rem(dev);
+
+       return ret;
+}
+
+
+int atmio16f_rem(comedi_device *dev)
+{
+
+       if(dev->iobase)
+               release_region(dev->iobase,dev->iosize);
+
+       return 0;
+}
+
diff --git a/comedi/old/atmio-16x.c b/comedi/old/atmio-16x.c
new file mode 100644 (file)
index 0000000..c8ec0eb
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+    module/atmio-16x.c
+    hardware driver for National Instruments AT-MIO-16X
+
+    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.
+
+*/
+
+/*
+       specifications are found in NI document 320640b.pdf
+*/
+
+#define Command_Register_1             0x00    /* wo */
+#define Command_Register_2             0x02    /* wo */
+#define Command_Register_3             0x04    /* wo */
+#define Command_Register_4             0x06    /* wo */
+#define Status_Register_1              0x18    /* ro */
+#define Status_Register_2              0x1a    /* ro */
+
+#define ADC_FIFO_Register              0x00    /* ro */
+#define CONFIGMEM_Register             0x08    /* wo */
+
+#define DAC0_Register                  0x10    /* wo */
+#define DAC1_Register                  0x12    /* wo */
+
+#define CONFIGMEMCLR_Register          0x1b    /* ro 8 */
+#define CONFIGMEMLD_Register           0x1b    /* wo 8 */
+#define DAQ_Clear_Register             0x19    /* ro 8 */
+#define DAQ_Start_Register             0x1d    /* ro 8 */
+#define Single_Conversion_Register     0x1d    /* wo 8 */
+#define ADC_Calibration_Register       0x1f    /* wo 8 */
+
+#define TMRREQ_Clear_Register          0x1f    /* ro 8 */
+#define DAC_Update_Register            0x18    /* wo */
+#define DAC_Clear_Register             0x1e    /* ro 8 */
+
+#define DMA_Channel_Clear_Register     0x0b    /* ro 8 */
+#define DMATCA_Clear_Register          0x19    /* wo 8 */
+#define DMATCB_Clear_Register          0x09    /* ro 8 */
+#define External_Strobe_Register       0x1e    /* wo 8 */
+#define Calibration_DAC_0_Load_Register        0x0a    /* wo 8 */
+#define Calibration_DAC_1_Load_Register        0x1a    /* wo 8 */
+
+#define Am9513A_Data_Register          0x14    /* rw */
+#define Am9513A_Command_Register       0x16    /* wo */
+#define Am9513A_Status_Register                0x16    /* ro */
+
+#define Digital_Input_Register         0x1c    /* ro */
+#define Digital_Output_Register                0x1c    /* wo */
+
+#define RTSI_Switch_Shift_Register     0x0c    /* wo 8 */
+#define RTSI_Switch_Strobe_Register    0x0e    /* wo 8 */
+
+#define _B(b)          ((struct bitchan){_B_CHAN,(b)})
+
+#define _B_CHAN                Command_Register_1
+#define EEPROMCS       _bit15
+#define SDATA          _bit14
+#define SCLK           _bit13
+#define SCANDIV                _bit12
+#define INTGATE                _bit10
+#define RETRIG_DIS     _bit9
+#define DAQEN          _bit8
+#define SCANEN         _bit7
+#define SCN2           _bit6
+#define CNT32          _bit5
+#define RTSITRIG       _bit4
+#undef _B_CHAN
+
+#define _B_CHAN                Command_Register_2
+#define A4RCV          _bit15
+#define A4DRV          _bit14
+#define A2RCV          _bit13
+#define A2DRV          _bit12
+#define BIPDAC1                _bit11
+#define BIPDAC0                _bit10
+#define EXTREFDAC1     _bit9
+#define EXTREFDAC0     _bit8
+#define EISA_DMA       _bit7
+#define DMACHBB                _bits543
+#define DMACHAB                _bits210
+#undef _B_CHAN
+
+#define _B_CHAN                Command_Register_3
+#define ADCDSP         _bit15
+#define DIOPBEN                _bit14
+#define DIOPAEN                _bit13
+#define DMATCINT       _bit12
+#define DACCMPLINT     _bit11
+#define DAQCMPLINT     _bit10
+#define IO_INT         _bit9
+#define DMACHA         _bit8
+#define DMACHB         _bit7
+#define ARCREQ         _bit6
+#define DAC1REQ                _bit5
+#define DAC0REQ                _bit4
+#define DRVAIS         _bit3
+#define INTCHB2                _bit2
+#define INTCHB1                _bit1
+#define INTCHB0                _bit0
+#undef _B_CHAN
+
+#define _B_CHAN                Command_Register_3
+#define CLKMODEB1      _bit15
+#define CLKMODEB0      _bit14
+#define DAC1DSP                _bit13
+#define DAC0DSP                _bit12
+#define DACMB3         _bit11
+#define DACMB2         _bit10
+#define DACMB1         _bit9
+#define DACMB0         _bit8
+#define DACGATE                _bit7
+#define DB_DIS         _bit6
+#define CYCLICSTOP     _bit5
+#define ADCFIFOREQ     _bit4
+#define SRC3SEL                _bit3
+#define GATE2SEL       _bit2
+#define FIFODAC                _bit1
+#define EXTTRIG_DIS    _bit0
+#undef _B_CHAN
+
+#define _B_CHAN                Status_Register_1
+#define DAQCOMP                _bit15
+#define DAQPROG                _bit14
+#define ADCFIFOHF      _bit13
+#define ADCFIFOEF      _bit12
+#define DMATCA         _bit11
+#define DMATCB         _bit10
+#define OVERFLOW       _bit9
+#define OVERRUN                _bit8
+#define TMRREQ         _bit7
+#define        DACCOMP         _bit6
+#define DACFIFOFF      _bit5
+#define DACFIFOHF      _bit4
+#define DACFIFOEF      _bit3
+#define EEPROMDATA     _bit2
+#define EEPROMCD       _bit1
+#define CFGMEMEF       _bit0
+#undef _B_CHAN
+
+#define _B_CHAN
+#define ADC_BUSY       _bit0
+#undef _B_CHAN
+
+#define _B_CHAN                CONFIGMEM_Register
+#define CHAN_SE                _bit15
+#define CHAN_AIS       _bit14
+#define CHAN_CAL       _bit13
+#define CHAN_BIP       _bit12
+#define CHANSEL3       _bit9
+#define CHANSEL2       _bit8
+#define CHANSEL1       _bit7
+#define CHANSEL0       _bit6
+#define CH_GAIN2       _bit5
+#define CH_GAIN1       _bit4
+#define CH_GAIN0       _bit3
+#define CHAN_LAST      _bit2
+#define CHAN_GHOST     _bit1
+#define CHAN_DSP       _bit0
+#endif _B_CHAN
+
+
+
+
+
+
+
+
+
+
+int irqbits[]={
+       -1, -1, -1, 0, 1, 2, -1, 3,
+       -1, -1, 4, 5, 6, -1, -1, 7
+}
+
+
+
+
diff --git a/comedi/old/rti860.c b/comedi/old/rti860.c
new file mode 100644 (file)
index 0000000..0ab08d5
--- /dev/null
@@ -0,0 +1,1264 @@
+/*****************************************************************
+  Linux device driver for RTI860 hardware
+  
+  Copyright (C) 1995 Ralf Haller
+            (C) 1995, 1996 Harald Kirsch (kir@iitb.fhg.de)
+
+           Fraunhofer Institut für 
+           Informations- und Datenverarbeitung (IITB)
+           Fraunhoferstr. 1
+           76131 Karlsruhe
+
+*****************************************************************/
+/*
+ *  values for ioctl
+ */
+#define RTI860_SET_PAR 1
+#define RTI860_GET_PAR 2
+#define RTI860_START   3
+#define RTI860_RESET   4
+
+/*
+ * flags
+ */
+#define RTI860_F_PRESENT       (1<<0)
+#define RTI860_F_OPEN          (1<<1)
+#define RTI860_F_BUSY          (1<<2)
+#define RTI860_F_IRQ           (1<<3)
+#define RTI860_F_RESTART       (1<<4)
+
+/* The following flag(s) can be changed by the user */
+#define RTI860_F_USER          (RTI860_F_RESTART)
+
+typedef enum {
+  Channel16    =-16,
+  Channel15    =-15,
+  Channel14    =-14,
+  Channel13    =-13,
+  Channel12    =-12,
+  Channel11    =-11,
+  Channel10    =-10,
+  Channel9     =-9,
+  Channel8     =-8,
+  Channel7     =-7,
+  Channel6     =-6,
+  Channel5     =-5,
+  Channel4     =-4,
+  Channel3     =-3,
+  Channel2     =-2,
+  Channel1     =-1,
+  Channel1_4   = 0,
+  Channel5_8   = 1,
+  Channel9_12  = 2,
+  Channel13_16 = 3,
+  Channel1_16  = 4,
+  Channel1_8   = 5,
+  Channel9_16  = 6
+} Channel;
+
+typedef enum {
+  Free         = 0,
+  BereichPlus  = 1,
+  BereichMinus = 2,
+  PegelPlus    = 3,
+  PegelMinus   = 4,
+  TTLTrigger   = 5
+} Trigger;
+
+/*
+ * parameter struct used for ioctl
+ */
+struct rti860_t
+{
+  unsigned int flags;          /* flags */
+  unsigned int base;           /* base address */
+  short channel;               /* channels to scan, see enum above */
+  unsigned short trigger;      /* trigger mode, see enum above */
+  signed char level;           /* trigger level, a value between -128
+                                  and 127 */
+  int tics;                    /* >0: use the 5MHz internal clock with
+                                  ticks as the divider.
+                                  <0: use the external clock with ticks
+                                  as the divider.
+                                  In both cases the sampling frequency
+                                  is 1.0/(ticks*base_frequency) */
+  int irq;                     /* interrupt channel, 0 if none */
+  unsigned timeout;            /* timeout in seconds */
+  unsigned sleep_time;         /* sleep time in polling mode in jiffies
+                                */
+};
+
+typedef struct rti860_t Rti860;
+
+#define F2TICS(f)      (5e6/(f))
+#define TICS2F(tics)   (5e6/(tics))
+/*****************************************************************
+  Linux device driver for RTI860 hardware
+  
+  Copyright (C) 1995 Ralf Haller
+            (C) 1995, 1996 Harald Kirsch (kir@iitb.fhg.de)
+
+           Fraunhofer Institut für 
+           Informations- und Datenverarbeitung (IITB)
+           Fraunhoferstr. 1
+           76131 Karlsruhe
+
+*****************************************************************/
+
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include "rti860.h"
+/* major device number for rti860 device */
+#define RTI860_MAJOR 58
+/* IMPORTANT: check MAX_CHRDEV in major.h (MAX_CHRDEV >= RTI860_MAJOR) */
+/* maximum number of minors */
+#define RTI860_NO 24
+#define RTI860_MAX 5
+/* size of internal memory (256k) */
+#define        RTI860_MEMSIZE 0x00040000L
+#define RTI860_MEMCHUNK RTI860_MEMSIZE / 32
+/* minimal and maximal 200ns-tics */
+#define RTI860_MAX_TICS        655350000
+#define RTI860_MIN_TICS        25              /* 200kHz */
+#define INDEX(a) (minor_index[a])
+#define SEC2JIFFY(a)    ((a)*HZ)
+#define TICS2USEC(a)   ((a)/5)         /* one tic is 200ns */
+
+
+/* delay for hardware im ms */
+#define delay(a) __delay(a*loops_per_msec)
+
+/*
+ *  hardware registers
+ */
+#define status_reg          ((rti860[INDEX(minor)].base) + 0x00)
+#define control_reg         ((rti860[INDEX(minor)].base) + 0x02)
+#define muxdata_reg         ((rti860[INDEX(minor)].base) + 0x04)
+#define softcon_reg         ((rti860[INDEX(minor)].base) + 0x06)
+#define adcdata_reg         ((rti860[INDEX(minor)].base) + 0x08)
+#define adcmem_reg          ((rti860[INDEX(minor)].base) + 0x0A)
+#define datamem_reg         ((rti860[INDEX(minor)].base) + 0x0C)
+#define hostmem_reg         ((rti860[INDEX(minor)].base) + 0x0E)
+#define adchstmem_reg       ((rti860[INDEX(minor)].base) + 0x10)
+#define muxmemscan_reg      ((rti860[INDEX(minor)].base) + 0x12)
+#define data9513_reg        ((rti860[INDEX(minor)].base) + 0x14)
+#define ctrl9513_reg        ((rti860[INDEX(minor)].base) + 0x16)
+#define flgsclr_reg         ((rti860[INDEX(minor)].base) + 0x18)
+#define trigger_reg         ((rti860[INDEX(minor)].base) + 0x1A)
+#define indaddr_reg         ((rti860[INDEX(minor)].base) + 0x1C)
+
+/*
+ * NB. we must include the kernel idenfication string to install the module.
+ */
+#include <linux/version.h>
+static char kernel_version[] = UTS_RELEASE;
+
+/* parameters for all possible rti860s, can be changed with ioctl */
+static Rti860 rti860[RTI860_MAX] = 
+{{0,0L,Channel1_4,Free,0,5000,0,5,10}, /* default */
+ {0,0L,Channel1_4,Free,0,5000,0,5,10},
+ {0,0L,Channel1_4,Free,0,5000,0,5,10},
+ {0,0L,Channel1_4,Free,0,5000,0,5,10},
+ {0,0L,Channel1_4,Free,0,5000,0,5,10}};
+
+static void rti860_timer( unsigned long );
+
+/* timer for polling mode */
+static struct timer_list timer[RTI860_MAX] =
+{{NULL,NULL,0,0,rti860_timer},
+ {NULL,NULL,0,1,rti860_timer},
+ {NULL,NULL,0,2,rti860_timer},
+ {NULL,NULL,0,3,rti860_timer}};
+
+/* index to rti860-struct */
+static int minor_index[RTI860_NO]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/* number of channels for rti860.channel */
+/* static int num_channels[]={4,4,4,4,16,8,8}; */
+
+/* wait queue for rti860 device driver */
+static struct wait_queue *rti860_wait_q;
+
+/* irq counts */
+static unsigned short irq_count[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/* number of delay loops per msec */
+static int loops_per_msec;
+
+/*****
+  Der folgende Typ beschreibt ein Counter Mode Register des 9513. Die
+  Bits sind in dieser merkwürdigen Reihenfolge, weil der 80x86 die
+  lo-Bytes bei niedrigen Adressen abspeichert.
+*****/
+typedef struct s_CounterMode {
+  unsigned output:3;
+  unsigned increm:1;
+  unsigned bcdCount:1;
+  unsigned repeat:1;
+  unsigned reloadFrom:1;
+  unsigned specialGate:1;
+  unsigned source:4;
+  unsigned sourceEdge:1;
+  unsigned gate:3;
+} CounterMode;
+#define cm2us(x) (* (unsigned short*)&(x) )
+
+#if 0
+/****************************************************************************
+ * read one counter of the 9513
+ ****************************************************************************/
+static void read_counter( unsigned int minor, unsigned *c )
+{
+
+  int i;
+
+  /***** 9513 in 16-bit mode */
+  outw(0xFFEF, ctrl9513_reg);
+
+  /***** Copy to hold register */
+  outw(0xFFA0+0x1F, ctrl9513_reg);
+  
+  /***** Select the first counter */
+  outw(0xFF19, ctrl9513_reg); 
+
+  for( i=0 ; i<5 ; i++ ) {
+
+    /***** Read the counter */
+    c[i] = inw(data9513_reg);  /* hold register */
+}
+}
+#endif
+
+/****************************************************************************
+ * reset the 9513
+ ****************************************************************************/
+static void reset9513( unsigned int minor )
+{
+  outw( 0x0000, control_reg );         /* Control-register zurücksetzen */
+  delay(1);
+  /**** 9513 zweimal zurücksetzen */
+  outw( 0xFFFF, ctrl9513_reg ); delay(1);
+  outb( 0xFF, ctrl9513_reg );  delay(1);
+
+  /***** 
+    Betriebsart setzen, d.h. Master-Mode Register adressieren und dann
+    den Wert 0x3100 als zwei einzelne Bytes ausgeben. Der Mode 0x3100
+    bedeutet:
+    ---Bitpositionen---   ----eingestellte Funktion bei 0x3100----
+    1000 0000 0000 0000 : Frequenzscalierung binär (nicht BCD)
+    0100 0000 0000 0000 : automatisches Adreßincrement
+    0010 0000 0000 0000 : 16-bit Datenbus (ab sofort)
+    0001 0000 0000 0000 : FOUT Aus
+    0000 1111 0000 0000 : FOUT Divider = Division durch 1
+    0000 0000 1111 0000 : FOUT-Quelle ist E1
+    0000 0000 0000 1100 : Comparator 1 und 2 aus
+    0000 0000 0000 0000 : Keine Funktion als Uhr (time-of-day) 
+  *****/
+  outb( 0x17, ctrl9513_reg ); delay(1);
+  outb( 0x00, data9513_reg ); delay(1);
+  outb( 0x31, data9513_reg ); delay(1);
+  
+  /*****
+    Zähler 1 wird initialisiert:
+    0x0005 heißt: kein Gating, zähle steigende Flanke, zähle Ausgang
+    von Zähler 0 (?), kein special gate, nachladen vom Loadregister,
+    einmal zählen, binär zählen, abwärts zählen, active low terminal
+    count pulse
+  *****/
+  outw( 0xFF01, ctrl9513_reg ); delay(1);
+  outw( 0x0005, data9513_reg ); delay(1);
+
+  /* Eine A/D-Wandlung auslösen (warum?) */
+  outw( 0x1234, softcon_reg ); delay(1);
+
+  /*****
+    Zähler 1 erneut initialisieren. (warum?)
+    0x0002 wie oben, außer Ausgabe auf 'terminal count toggled'.
+    Anschließend wird der Ausgabepin definiert auf 0 gesetzt.
+  *****/
+  outw( 0xFF01, ctrl9513_reg ); delay(1);    
+  outw( 0x0002, data9513_reg ); delay(1);
+  outw( 0xFFE1, ctrl9513_reg ); delay(1);
+
+  /*****
+    Zähler 3 identisch mit Zähler 1 initialisieren. Ausgabepin auf 1, da
+    bei 0 die A/D-Wandlung angehalten wird: DONE-Signal. 
+  *****/
+  outw( 0xFF03, ctrl9513_reg ); delay(1);
+  outw( 0x0002, data9513_reg ); delay(1);
+  outw( 0xFFE3, ctrl9513_reg ); delay(1);
+
+  /*****
+    Zähler 5 identisch mit Zähler 1 initialisieren.
+  *****/
+  outw( 0xFF05, ctrl9513_reg ); delay(1);
+  outw( 0x0002, data9513_reg ); delay(1);
+  outw( 0xFFE5, ctrl9513_reg ); delay(1);
+
+  /* Kontrollregister zurücksetzen */  
+  outw( 0x0000, control_reg ); delay(1);
+}
+
+/****************************************************************************
+ * set the trigger
+ ****************************************************************************/
+static void init_overflow_counter( unsigned int minor )
+/*****
+  Zähler 4 und 5 auf dem 9513 sind wohl dazu gedacht, die
+  Speicherüberlaufbits anzutreiben (vgl. RTI860-Handbuch S.4-10 und
+  S.B-13). Beide werden hier initialisiert, so daß nach der halben
+  Speichergröße jeweils ein positiver Puls am pin O5 des 9513
+  ausgegeben wird. Zähler 4 lassen wir bis 4 zählen. Damit treibt
+  er Zähler 5, den wir jeweils bis 0x8000 zählen lassen, so daß insgesamt
+  bis 2^17 = halbe Speichergröße der RTI860 gezählt wird.
+*****/
+{
+  CounterMode mode;
+
+  /***** Zähler 4 und 5 abschalten */
+  outw(0xFFC0 + (1<<(5-1)) + (1<<(4-1)), ctrl9513_reg); delay(1);
+
+  /***** Zähler 4 */
+  mode.gate = 0;
+  mode.sourceEdge = 0;
+  mode.source = 4;
+  mode.specialGate = 0;
+  mode.reloadFrom = 0;
+  mode.repeat = 1;
+  mode.bcdCount = 0;
+  mode.increm = 0;
+  mode.output = 1;
+  outw( 0xFF04, ctrl9513_reg );                delay(1);
+  outw( cm2us(mode), data9513_reg );   delay(1); /* Mode */
+  outw( 0x0004, data9513_reg );        delay(1); /* Loadregister */
+  outw( 0x0000, data9513_reg );        delay(1); /* Holdregister */
+  outw( 0xFF48, ctrl9513_reg );        delay(1); /* Lade von Loadregister */
+
+  /***** Zähler 5 wird von Zähler 4 betrieben */
+  mode.gate = 0;
+  mode.sourceEdge = 0;
+  mode.source = 0;
+  mode.specialGate = 0;
+  mode.reloadFrom = 0;
+  mode.repeat = 1;
+  mode.bcdCount = 0;
+  mode.increm = 0;
+  mode.output = 1;
+  outw( 0xFF05, ctrl9513_reg );        delay(1);
+  outw( cm2us(mode), data9513_reg );   delay(1); /* Mode */
+  outw( 0x8000, data9513_reg );        delay(1); /* Loadregister */
+  outw( 0x0000, data9513_reg );        delay(1); /* Holdregister */
+  outw( 0xFF50, ctrl9513_reg );        delay(1); /* Lade von Loadregister */
+}
+
+/*****************************************************************/
+static void 
+set_trigger(unsigned int minor)
+
+{
+  static unsigned short masks[] = {
+    0x000c, 0x0003, 0x0002, 0x0001, 0x0000, 0x0008
+  };
+  unsigned int mask;
+
+  mask = masks[rti860[INDEX(minor)].trigger];
+  mask |= (0x8000 + rti860[INDEX(minor)].level * 128) & 0xFF00;
+  outw(mask, trigger_reg); delay(5);
+}
+
+/*****************************************************************/
+/*
+  Tell the board, which channels to scan.
+*/
+static void
+set_inputmux(unsigned int minor)
+{
+  unsigned short I;
+  unsigned base=8;
+
+  outw( 0x0000, control_reg ); delay(1);
+
+  if( rti860[INDEX(minor)].channel >= 0 ) {
+    switch( rti860[INDEX(minor)].channel ) {
+    case Channel1_16:
+      for (I = 0; I < 16; I++) {
+       outw(I, muxmemscan_reg ); delay(1);
+       outw(15-I, muxdata_reg); delay(1);
+      }
+      break;
+    case Channel1_8:
+      base = 0;
+    case Channel9_16:
+      for(I=0; I<16; I++) {
+       outw(I, muxmemscan_reg); delay(1);
+       outw((15-I)%8 + base, muxdata_reg); delay(1);
+      }
+      break;
+    case Channel1_4:
+    case Channel5_8:
+    case Channel9_12:
+    case Channel13_16:
+      outw( 0x0000, control_reg );
+      for (I = 0; I<16; I++) {
+       outw(I, muxmemscan_reg); delay(1);
+       outw(((15-I) & 0x03) + (rti860[INDEX(minor)].channel << 2), 
+            muxdata_reg); delay(1);
+      }
+      break;
+    default: 
+      printk(KERN_DEBUG "Wrong mode in set_inputmux!\n");
+    }
+  } else {
+    outw( 0x0000, control_reg );
+    for(I = 0; I < 16; I++) {
+      outw(I, muxmemscan_reg); delay(1);
+      outw(-rti860[INDEX(minor)].channel-1, muxdata_reg); delay(1);
+    }
+  }
+
+  /***** Set start of scan to the last mux-data register */
+  outw(0x0F, muxmemscan_reg);  delay(1);
+  delay(1);
+}
+/****************************************************************************
+ * initialize counter 1
+ ****************************************************************************/
+void 
+init_clock(unsigned int minor)
+/*****
+  Zähler 1 des 9513 ist der Teiler entweder f"ur die interne clock, die
+  mit 5MHz l"auft, oder aber auch f"ur die externe clock.
+*****/
+{
+  unsigned scale, prescale;
+  unsigned mode;
+  /*****
+    Die folgenden Zeilen initialisieren Counter 1. Counter 1
+    erzeugt den Takt für die A/D-Wandlung.
+    Der Modus=0x9025 + (Vorteiler+0x000B)<<8
+    bedeutet (vgl. S.2-141 im Am9513-Handbuch):
+          Gating Control: active hi gate 1      1110 0000 0000 0000
+         Source Edge:    Count on Falling Edge 0001 0000 0000 0000
+         Count Source:   F_Vorteiler           0000 1111 0000 0000
+         Special Gate:   disabled              0000 0000 1000 0000
+         Reload from:    Load Register         0000 0000 0100 0000
+         Repetitively:   TRUE                  0000 0000 0010 0000
+         Count Type:     binary                0000 0000 0001 0000
+         Direction:      count down            0000 0000 0000 1000
+         Output:         Active Lo Pulse       0000 0000 0000 0111
+    Der Mode entspricht 'Mode E' S.2-131 im Handbuch.
+  *****/
+
+  if( rti860[INDEX(minor)].tics < 0 ) {
+    /* select SRC1 as counter input */
+    mode = 0x9025 | (1<<8);
+    scale = -rti860[INDEX(minor)].tics;
+  } else {
+    for(prescale=0,scale=rti860[INDEX(minor)].tics;
+       scale > 65535;    
+       prescale++,scale/=16);
+    /*****
+      Select F1...F5, i.e. a prescaler-output, as counter input
+      The available prescale values are 1, 16, 256, 4096 and 65536.
+      The value of prescale must be 0,1,2,3 or 4.
+    ****/
+    mode = 0x9025 | ((prescale+0xB)<<8);
+  }
+
+  outw( 0xFF01, ctrl9513_reg );        delay(1); /* address counter 1 */
+  outw( mode, data9513_reg );  delay(1); /* set mode */
+  outw( scale, data9513_reg ); delay(1); /* set load register */
+  outw( 0x0000, data9513_reg ); delay(1); /* set hold register */
+  outw( 0xFF41, ctrl9513_reg );        delay(1); /* load counter with load
+                                            register */
+}
+
+/****************************************************************************
+ * initialize counter 2&3
+ ****************************************************************************/
+static void init_posttrigger_counter( unsigned int minor )
+/*****
+  Zähler 2&3 zählt A/D-Wandlungen nach dem Trigger. Sobald 2&3 auf 0
+  zählt, hört die RTI860 mit A/D-Wandlungen auf. Deshalb werden 2&3
+  in RTI860_Start() bei kontinuierlichem Modus nicht scharfgemacht
+*****/
+{
+  /*****
+    Der Modus=0x1221 bedeutet (vgl. S.2-141 im Am9513-Handbuch):
+        Gating Control: No Gating             1110 0000 0000 0000
+        Source Edge:    Count on Falling Edge 0001 0000 0000 0000
+        Count Source:   SRC 2                 0000 1111 0000 0000
+        Special Gate:   disabled              0000 0000 1000 0000
+        Reload from:    Load Register         0000 0000 0100 0000
+        Repetitively:   TRUE                  0000 0000 0010 0000
+        Count Type:     binary                0000 0000 0001 0000
+        Direction:      count down            0000 0000 0000 1000
+        Output:         Active Hi Pulse       0000 0000 0000 0111
+    Es stellt sich die Frage, warum das Loadregister gerade auf
+    samples-1 gesetzt wird, wenn sowieso kontinuierlich gearbeitet
+    wird.
+  *****/
+  outw( 0xFF02, ctrl9513_reg ); delay(1);  /* Adressieren */
+  outw( 0x1221, data9513_reg ); delay(1);  /* Modus setzen */
+  outw( 333, data9513_reg );   delay(1);  /* Loadregister */
+  outw( 0x0000, data9513_reg ); delay(1);  /* Holdregister */
+  outw( 0xFF42, ctrl9513_reg ); delay(1); /* Counter:=Loadregister */
+
+  /*****
+    Counter 3 zählt zusammen mit Counter 2 die gewandelten Daten. Der
+    Output von Counter 3 ist sogar am Ausgabeport der RTI860 zu finden
+    --- als DONE-Signal. Außderdem hält die Karte tatsächlich an,
+    wenn der Ausgang von Counter 3 auf lo geht.
+  *****/
+  outw( 0xFF03, ctrl9513_reg );    /* Source: Counter 3 - single count */
+  delay(1);                         /* TC toggle */ 
+  outw( 0x0002, data9513_reg );    /* Source: Counter 2 */
+  delay(1);                         /* single count, TC toggle */
+  outw( 0x0003, data9513_reg );    /* Loadregister = 3 */
+  delay(1);
+  outw( 0x0000, data9513_reg );    /* Holdregister = 0 */
+  delay(1);
+  outw( 0xFF44, ctrl9513_reg );    /* Load Counter 3 with Loadregister*/
+  delay(1);
+  outw( 0xFFF3, ctrl9513_reg );    /* Counter 3 steppen erstes mal */
+  delay(1);
+  outw( 0xFFF3, ctrl9513_reg );    /* zweites mal */
+  delay(1);
+  outw( 0xFFF3, ctrl9513_reg );    /* drittes mal */
+  delay(1);
+  outw( 0xFFE3|8, ctrl9513_reg );    /* Ausgang Counter 3 auf high */
+  delay(1);
+
+  /*****
+    Eine A/D-Wandlung auslösen, damit  Flipflop U82 (S.6,Schaltplan)
+    auch sicher durchschaltet. 
+    Die Methode hat letztlich doch nicht funktioniert. Deshalb wird
+    jetzt im reset9513 der Ausgang von counter 3 bereits auf High, statt
+    auf Low gesetzt. Das scheint zu funktionieren.
+  *****/
+  /*outw( 0x1234, softcon_reg ); delay(1);*/
+ }
+
+/*****************************************************************/
+/*
+  Clear the board memory
+*/
+static void 
+rti860_clearmem( unsigned int minor )
+{
+  int i;
+  /***** Setze ADC Zieladresse auf 0 */
+  outw( 0x8100, adchstmem_reg );       delay(1); /* most sign. bits */
+  outw( 0x8100, adchstmem_reg );       delay(1);
+  outw( 0x1000-20, adcmem_reg );       delay(1); /* least sign. bits */
+  for(i=0; i<40; i++) {
+    outw( 0, control_reg );
+  }
+
+  outw( 0x8100, adchstmem_reg );       delay(1); /* most sign. bits */
+  outw( 0x8100, adchstmem_reg );       delay(1);
+  outw( 0x0000, adcmem_reg );          delay(1); /* least sign. bits */
+
+  /***** Setze Leseadresse auf 0 */
+  outw( 0x0001, adchstmem_reg );       delay(1); /* most sign. bits */
+  outw( 0x0001, adchstmem_reg );       delay(1);
+  outw( 0x0000, hostmem_reg );         delay(1); /* least sign. bits */
+}
+/*****************************************************************/
+/*
+  initialize the rti860
+*/
+static void 
+rti860_init( unsigned int minor )
+{
+
+  reset9513(minor); delay(5);
+  rti860_clearmem(minor );
+
+  set_inputmux(minor);
+  set_trigger(minor);
+}
+
+/****************************************************************************
+ * start the rti860
+ ****************************************************************************/
+static void rti860_start( unsigned int minor )
+/*****
+  Startet die A/D-Wandlung auf der Karte mit den in RTI860_Init
+  vorgegebenen Parametern. Die A/D-Daten werden im Speicher der Karte
+  abgelegt. Wurde Betriebsart=Fix gewählt, so beendet die Karte
+  selbständig die Messung nach der vorgegebenen Anzahl der
+  Einzelmessungen. Andernfalls muß die Karte mit RTI860_Stop oder
+  RTI_860_Reset angehalten werden.
+  Hinweis: Diese Prozedur setzt den Speicher der Karte nicht zurück.
+*****/
+{
+  unsigned short control;
+
+  reset9513(minor);
+  set_inputmux(minor);
+  set_trigger(minor);
+
+  /***** Steuerwort der RTI860 setzen: */
+  control = (1<<1)     /* Memory Mode Enable */
+          | (1<<2)             /* Scan Mode Enable */
+          | (1<<5);            /* Continuous Transfer Mode Enable */
+
+  if( rti860[INDEX(minor)].channel >= 0 )
+    control |= (1<<8); /* Simultaneous Mode Enable */
+
+  if( rti860[INDEX(minor)].irq > 0 ) {
+    control |= (1<<14);        /* continuous transfer interrupt */
+    control |= (1<<13);        /* error interrupt */
+  }
+  outw_p(control, control_reg); delay(1);
+
+  init_posttrigger_counter(minor);
+  init_clock(minor);
+  init_overflow_counter(minor);
+
+  rti860_clearmem( minor );
+
+  /***** 
+    clear the overflow flags of the 9513, 
+    in particular reset the DONE-bit.
+  *****/
+  outw_p(0x0000, flgsclr_reg); delay(1);
+  outw_p(0x8000, flgsclr_reg); delay(1);
+
+  /***** Zähler 1,4,5 scharfmachen */
+  outw_p(0xFF39, ctrl9513_reg); delay(1);
+
+  /***** Wandlung starten */
+  outw_p(control|1, control_reg); delay(1);
+}
+/*****************************************************************/
+/*
+  Beendet die A/D-Wandlungen auf der Karte. Der Speicher der Karte
+  wird *nicht* gelöscht. Die Karte kann ohne Neuinitialisierung wieder
+  gestartet werden.
+*/
+#if 0
+static void 
+rti860_stop( unsigned int minor )
+{
+  reset9513( minor );
+}
+#endif
+
+#ifdef PROFILE
+/****************************************************************************
+ * get the current readaddress
+ ****************************************************************************/
+static unsigned rti860_get_readaddress( unsigned int minor )
+{
+  return
+    ((unsigned)(inw(adchstmem_reg) & 0x0003) << 16)
+    + inw_p(hostmem_reg);
+}
+
+/****************************************************************************
+ * get the current writeaddress
+ ****************************************************************************/
+static unsigned rti860_get_writeaddress( unsigned int minor)
+{
+  unsigned short hiBits, hiTmp;
+  unsigned writeAdr;
+  /*****
+    Zu der folgenden Konstruktion siehe den Kommentar in Fuellstand.
+  *****/
+  do {
+    hiBits = inw(adchstmem_reg) & 0x0300;
+    writeAdr = inw(adcmem_reg);
+    hiTmp = inw(adchstmem_reg) & 0x0300;
+  } while( hiBits!=hiTmp );
+  writeAdr +=  ( (unsigned)hiBits << 8 );
+
+  return writeAdr;
+}
+#endif
+/*****************************************************************/
+/*
+ get the current amount of data in the memory
+*/
+static unsigned 
+rti860_get_filling( unsigned int minor )
+{
+  unsigned writeAdr, readAdr;
+  unsigned short hiBits, hiTmp;
+
+  /***** 
+    Das Problem beim Auslesen der Schreibadresse ist, daß zwei
+    Speicherzellen aus der Karte gelesen werden müssen, und es ist
+    nicht ausgeschlossen, daß die Karte die Schreibadresse zwischen
+    den beiden Zugriffen erhöht. Wenn diese Erhöhung Ã¼ber die
+    0xFFFF-Marke hinausgeht, passen die beiden gelesenen Werte nicht
+    zueinander, egal in welcher Reihenfolge man sie einliest. Deshalb
+    die folgende Konstruktion:
+  *****/
+  do {
+    hiBits = inw(adchstmem_reg) & 0x0300;
+    writeAdr = inw(adcmem_reg);
+    hiTmp = inw(adchstmem_reg) & 0x0300;
+  } while( hiBits!=hiTmp );
+  writeAdr +=  ( (unsigned)hiBits << 8 );
+
+  hiBits = inw(adchstmem_reg) & 0x0003;
+  readAdr = inw(hostmem_reg) + ( (unsigned)hiBits << 16 );
+  
+  if( writeAdr>=readAdr ){
+    return writeAdr - readAdr;
+  } else {
+    return writeAdr + RTI860_MEMSIZE - readAdr;
+  }
+}
+
+/****************************************************************************
+ * interrupt service routine
+ ****************************************************************************/
+static void rti860_interrupt( int irq, struct pt_regs *regs )
+{
+  /*printk("irq%d\n",irq);*/
+  /* wake us up */
+
+  wake_up(&rti860_wait_q);
+}
+
+/****************************************************************************
+ * timer service routine
+ ****************************************************************************/
+static void rti860_timer( unsigned long data )
+{
+  /*printk("timer%ld\n",data);*/
+  /* wake us up */
+  wake_up(&rti860_wait_q);
+}
+
+/****************************************************************************
+ * allocate the interrupt
+ ****************************************************************************/
+static void rti860_alloc_irq( int minor )
+{
+  int ret;
+
+  /* irq already allocated from driver ? */
+  if( irq_count[rti860[INDEX(minor)].irq] == 0 ) {
+      
+    /* get the interrupt */
+    ret = request_irq(rti860[INDEX(minor)].irq, 
+                     rti860_interrupt, 
+                     0/*SA_INTERRUPT*/, 
+                     "rti860",
+                     NULL);
+    if( ret ) {
+      /* no interrupt */
+      printk(KERN_WARNING 
+            "cannot allocate irq%d forrti860 device, "
+            "switching to polling mode.\n",
+            rti860[INDEX(minor)].irq);
+      rti860[INDEX(minor)].flags &= ~RTI860_F_IRQ;
+      return;
+    }
+    else {
+      /* got interrupt */
+      printk(KERN_INFO "allocated irq%d for rti860 device\n",
+            rti860[INDEX(minor)].irq);
+    }
+  }
+
+  /* increase irq count */
+  irq_count[rti860[INDEX(minor)].irq]++;
+
+  /* set flags */
+  rti860[INDEX(minor)].flags |= RTI860_F_IRQ;
+
+}
+
+/****************************************************************************
+ * free the interrupt
+ ****************************************************************************/
+static void rti860_free_irq(int minor)
+{
+  /* interrupt to free ? */
+  if( rti860[INDEX(minor)].flags & RTI860_F_IRQ ) {
+
+    /* decrease irq_count */
+    if( irq_count[rti860[INDEX(minor)].irq] == 0 ) {
+      printk("rti860: irq_count less than zero! setting back...\n");
+      irq_count[rti860[INDEX(minor)].irq] = 0;
+    }
+
+    if( --irq_count[rti860[INDEX(minor)].irq] == 0 ) {
+      /* free interrupt and reset flags */
+      free_irq( rti860[INDEX(minor)].irq, NULL );
+      printk(KERN_INFO "freed irq%d of rti860 device\n",
+            rti860[INDEX(minor)].irq);
+    }
+
+    /* reset flags */
+    rti860[INDEX(minor)].flags &= ~RTI860_F_IRQ;
+
+  }
+}
+
+/****************************************************************************
+ * read data from hardware
+ ****************************************************************************/
+static int rti860_read(struct inode *inode, struct file *file, char *buf,
+                      int count)
+{
+  int ret, pre, post;
+  int index=0;
+  unsigned num_samples = 0;
+  unsigned int minor = MINOR(inode->i_rdev);
+#if 0
+  static unsigned long jiffies1,jiffies2;
+  unsigned dummy;
+#endif
+#if 0
+  unsigned c[5],w,i;
+  static unsigned long mittel,stdabw,min,max,n;
+  static unsigned a[100];
+#endif
+
+#if 0
+  jiffies1=jiffies;
+#endif
+
+  /***** only even byte count allowed */
+  count &= 0xFFFFFFFE;
+
+  /* check user memory */
+  ret = verify_area( VERIFY_WRITE, buf, count );
+  if( ret ) return ret;
+
+  /***** start the driver and the device, if not yet busy. */
+  if( (rti860[INDEX(minor)].flags & RTI860_F_BUSY) == 0 ) {
+
+    if( rti860[INDEX(minor)].irq > 0 ) {
+      /* we want an interupt, so get it */
+      rti860_alloc_irq(minor);
+    }
+    /* reset and start the hardware */
+    rti860_start(minor);
+
+    rti860[INDEX(minor)].flags |= RTI860_F_BUSY;
+  }
+
+  num_samples = rti860_get_filling(minor);
+
+  /***** read all the data */
+  while( index < count ) {
+
+    /***** Check for overflow and restart device in restart-mode */
+    if( inw(status_reg) & (1<<6) ) {
+      if( rti860[INDEX(minor)].flags & RTI860_F_RESTART ) {
+       rti860_start(minor);
+      } else {
+       /* return data already read or error if nothing read */
+       return index ? index : -EIO;
+      }
+    }
+
+    /***** 
+      We are about to enter a wait-loop below which includes some
+      sleeping. In polling mode, or if hardware fifo overflow happens at
+      the wrong time, there will be no more samples arriving anymore for
+      a long time. If this time is longer than the timeout, the process
+      will get an ETIME error, however only, if an ioctl() specified a
+      certain timeout.
+    *****/
+    if( rti860[INDEX(minor)].timeout ) {
+      current->timeout = 
+       jiffies + SEC2JIFFY(rti860[INDEX(minor)].timeout);
+    }
+
+    /***** This is the wait-loop */
+    while( !(num_samples=rti860_get_filling(minor)) ) {
+
+      /* non blocked ? */
+      if( file->f_flags & O_NONBLOCK ) return index;
+
+      /***** fire a timer, if in polling mode */
+      if((rti860[INDEX(minor)].flags & RTI860_F_IRQ) == 0 ){
+
+       /* check if timer already set */
+       if( timer[INDEX(minor)].next == NULL 
+           && timer[INDEX(minor)].prev == NULL ) {
+
+         timer[INDEX(minor)].expires = 
+           jiffies + rti860[INDEX(minor)].sleep_time;
+         add_timer( &(timer[INDEX(minor)]) );
+       }
+      }
+
+      /***** fall asleep until interrupt or timer arrives */
+      interruptible_sleep_on(&rti860_wait_q);
+
+      if (current->signal & ~current->blocked) {
+       return index;
+      }
+    
+      /***** timeout occured ? */
+      if( current->timeout==0 ) return (-ETIME);
+    }
+    
+    if( num_samples > (count-index)/sizeof(short) )
+      num_samples = (count-index)/sizeof(short);
+
+    pre = inw(adchstmem_reg)&0x2;
+
+    for( ; num_samples>0; num_samples--) {
+      /* read data and copy to user space */
+      /* division by 16 because the 4 lower bits are not used */ 
+      put_fs_word( (short)inw_p(datamem_reg)/16, &(buf[index]));
+      index = index + sizeof(short);
+
+      post = inw(adchstmem_reg)&0x2;
+      if( pre != post ) {
+       outw( 0x8000, flgsclr_reg );
+      }
+    }
+  }
+
+#if 0
+  printk("%ld %ld %d\n",
+        jiffies-jiffies1,
+        jiffies-jiffies2,
+        dummy);
+  jiffies2=jiffies;
+#endif
+
+  return index;
+}
+
+/****************************************************************************
+ * test if data can be read
+ ****************************************************************************/
+static int rti860_select(struct inode *inode, struct file *file, 
+                        int sel_type, select_table *wait)
+{
+  unsigned int minor = MINOR(inode->i_rdev);
+
+  /* ready for read ? */
+  if( sel_type == SEL_IN ) {
+
+    /* data available ? */
+    if( rti860_get_filling(minor) == 0 ) {
+
+      /* insert in wait queue */
+      select_wait(&rti860_wait_q,wait);
+
+      /* nothing to read */
+      return 0;
+    }
+
+    /* ready to read */
+    return 1;
+  }
+
+  /* always ready for write and exceptions */
+  return 1;
+}
+/*****************************************************************/
+static char
+checkParams(Rti860 *p)
+{
+  /***** 
+    There is an enumeration type with limited range for channel.
+  *****/ 
+  if( p->channel<-16 || p->channel>6 ) return 0;
+
+  /*****
+    There is an enumeration type with limited range for trigger type.
+  *****/    
+  if( p->trigger>5 ) return 0;
+
+  /*****
+    Ticks must be positive or 16bit negative but not 0,-1,-2;
+  *****/
+  if( p->tics<=-65536 ) return 0;
+  if( p->tics<=0 && p->tics>=-1 ) return 0;
+
+  /***** 
+    We allow all irqs which can be jumpered according table 2.4 (page
+    2-8) of the rti860 manual.
+  *****/
+  if( p->irq!=0 && p->irq!=3 && p->irq!=5 && p->irq!=7
+      && p->irq!=10 && p->irq!=11 && p->irq!=12 && p->irq != 15)
+    return 0;
+
+  if( p->sleep_time<1 ) return 0;
+  return 1;
+}
+/*****************************************************************/
+static int 
+rti860_ioctl(struct inode *inode, struct file *file,
+            unsigned int cmd, unsigned long arg)
+{
+  unsigned int minor = MINOR(inode->i_rdev);
+  Rti860 tmprti860;
+  Rti860 *ptr = (Rti860 *)arg;
+  int error;
+
+  switch( cmd ) {
+  case RTI860_SET_PAR:
+    /* device busy ? */
+    if( rti860[INDEX(minor)].flags & RTI860_F_BUSY ) return (-EBUSY);
+
+    /* check memory */
+    error = verify_area( VERIFY_READ, ptr, sizeof(Rti860) );
+    if (error) return (error);
+
+    /* copy data */
+    memcpy_fromfs( (void *)&tmprti860, ptr, sizeof(Rti860) );
+
+    /***** 
+      We probably must correct the tics. However we do this only for
+      the internal clock. Doing it for the external clock is rather
+      useless, because the divider tends to so small then that division
+      by 2 or 4 will invalidate it.
+      We do this before checking parameters because the parameters must
+      come out correct afterwards.
+    *****/
+    if( tmprti860.channel >= 0 && tmprti860.tics>0) {
+      switch( tmprti860.channel ) {
+      case Channel1_16:
+       tmprti860.tics /= 4;
+       break;
+      case Channel1_8:
+      case Channel9_16:      
+       tmprti860.tics /= 2;
+       break;
+      case Channel1_4:
+      case Channel5_8:
+      case Channel9_12:
+      case Channel13_16:
+       break;
+      default:
+       return (-EINVAL);
+      }
+    }
+
+    /* check validity of parameters */
+    if( !checkParams(&tmprti860) ) return -EINVAL;
+    
+    /* set data */
+    rti860[INDEX(minor)].flags = 
+      (rti860[INDEX(minor)].flags & ~RTI860_F_USER) | 
+      (tmprti860.flags & RTI860_F_USER);
+    rti860[INDEX(minor)].channel       = tmprti860.channel;
+    rti860[INDEX(minor)].trigger        = tmprti860.trigger;
+    rti860[INDEX(minor)].level          = tmprti860.level;
+    rti860[INDEX(minor)].tics           = tmprti860.tics;
+    rti860[INDEX(minor)].irq           = tmprti860.irq;
+    rti860[INDEX(minor)].timeout       = tmprti860.timeout;
+    rti860[INDEX(minor)].sleep_time = tmprti860.sleep_time;      
+    return 0;  
+
+
+
+  case RTI860_GET_PAR: /* get actual parameters */
+    /* check memory */
+
+    error = verify_area(VERIFY_WRITE, ptr, sizeof(Rti860));
+    if( error ) return error;
+
+    tmprti860 = rti860[INDEX(minor)];
+
+    /***** need to correct the outgoing ticks? */
+    if( tmprti860.channel >= 0 && tmprti860.tics>0) {
+      switch( tmprti860.channel ) {
+      case Channel1_16:
+       tmprti860.tics *= 4;
+       break;
+      case Channel1_8:
+      case Channel9_16:      
+       tmprti860.tics *= 2;
+       break;
+      default:
+       break;
+      }
+    }
+    /* copy data */
+    memcpy_tofs(ptr, (void *)(&tmprti860), sizeof(Rti860));
+    
+    return 0;
+
+  case RTI860_START:
+    /* device busy ? */
+    if( (rti860[INDEX(minor)].flags & RTI860_F_BUSY) == 0 ) {
+      if( rti860[INDEX(minor)].irq > 0 ) {
+       /* we need an interrupt, get it */
+       rti860_alloc_irq(minor);
+      }
+      /* set device busy */
+      rti860[INDEX(minor)].flags |= RTI860_F_BUSY;
+    }
+
+    /* reset and start the hardware */
+    rti860_start(minor);
+
+    /* oK */
+    return 0;
+    
+  case RTI860_RESET:
+    /* hardware reset */
+    rti860_init(minor);
+    
+    /* set device not busy */
+    rti860[INDEX(minor)].flags &= ~RTI860_F_BUSY;
+
+    /* oK */
+    return 0;
+
+  default:
+    return (-EINVAL);
+  }
+}
+
+/****************************************************************************
+ * open the device
+ ****************************************************************************/
+static int rti860_open(struct inode *inode, struct file *file)
+{
+  unsigned int minor = MINOR(inode->i_rdev);
+
+  /* check minor device number */
+  if( minor >= RTI860_NO )
+    return (-ENODEV);
+
+  /* check if hardware present */
+  if( !(rti860[INDEX(minor)].flags & RTI860_F_PRESENT) )
+    return (-ENODEV);
+
+  /* open for write ? */
+  if( (file->f_flags & O_ACCMODE) != O_RDONLY )
+    return (-EROFS);
+
+  /* already opened ? */
+  if( rti860[INDEX(minor)].flags & RTI860_F_OPEN )
+    return (-EBUSY);
+
+  /* set device open */
+  rti860[INDEX(minor)].flags |= RTI860_F_OPEN;
+
+  /* oK */
+  return 0;
+}
+
+/****************************************************************************
+ * close the device
+ ****************************************************************************/
+static void rti860_release(struct inode *inode, struct file *file)
+{
+  unsigned int minor = MINOR(inode->i_rdev);
+
+  /* hardware reset */
+  rti860_init(minor);
+
+  /* clear irq */
+  rti860_free_irq(minor);
+
+  /* set device not busy */
+  rti860[INDEX(minor)].flags &= ~RTI860_F_BUSY;
+
+  /* set device closed */
+  rti860[INDEX(minor)].flags &= ~RTI860_F_OPEN;
+}
+
+static struct file_operations rti860_fops = {
+        NULL,          /* rti860_lseek */
+        rti860_read,
+        NULL,          /* rti860_write */
+        NULL,          /* rti860_readdir */
+        rti860_select,
+        rti860_ioctl,
+        NULL,          /* rti860_mmap */
+        rti860_open,
+        rti860_release
+};
+
+/****************************************************************************
+ * initialize the device
+ ****************************************************************************/
+int init_module( void )
+{
+  unsigned long address;
+  unsigned int minor, num=1;
+
+  if( register_chrdev(RTI860_MAJOR, "rti860", &rti860_fops) )
+  {
+    printk(KERN_ERR
+          "unable to get major %d for rti860 device\n", 
+          RTI860_MAJOR);
+    return -EIO;
+  }
+
+  /* find the rti860 */
+  for( minor=0,address=0x100 ; address<=0x3E0 ; address+=0x20,minor++ ) {
+
+    /* check if rti860 is present at address */
+    if( (inw(address) & 0xF000) == 0x2000 ) {
+
+      printk(KERN_INFO "rti860 found at 0x%lX\n", address );
+
+      INDEX(minor) = num++;
+
+      /*
+       * Set present flag and base address
+       */
+      rti860[INDEX(minor)].flags |= RTI860_F_PRESENT;
+      rti860[INDEX(minor)].base = address;
+
+      /* maximal number of rti860's reached */
+      if( num >= RTI860_MAX ) break;
+    }
+  }
+
+  /* calculate delay loops */
+  loops_per_msec = loops_per_sec/1000;
+
+  printk(KERN_INFO "rti860 device driver installed.\n");
+
+  return 0;  
+
+}
+
+/****************************************************************************
+ * kill the device
+ ****************************************************************************/
+void cleanup_module(void) 
+{
+  int i;
+
+  unregister_chrdev(RTI860_MAJOR, "rti860");
+  for(i=0; i<RTI860_NO; i++) {
+    if( INDEX(i)<RTI860_MAX ) {
+      rti860_free_irq(i);
+      if( timer[INDEX(i)].prev || timer[INDEX(i)].next) {
+       del_timer( &(timer[INDEX(i)]) );
+      }
+    }
+  }
+  printk(KERN_INFO "rti860: module removed.\n");
+}
diff --git a/comedi/parport.c b/comedi/parport.c
new file mode 100644 (file)
index 0000000..6eddda4
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+    module/parport.c
+    hardware driver for standard parallel port
+
+    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.
+
+*/
+
+/*
+
+   TODO:
+
+   - support bit mask ioctl
+   - EPP/ECP support
+
+   see http://www.senet.com.au/~cpeacock/parallel.htm for information.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+
+#define PARPORT_SIZE 3
+
+#define PARPORT_A 0
+#define PARPORT_B 1
+#define PARPORT_C 2
+
+static int parport_attach(comedi_device *dev,comedi_devconfig *it);
+static int parport_detach(comedi_device *dev);
+comedi_driver driver_parport={
+       driver_name:    "parport",
+       attach:         parport_attach,
+       detach:         parport_detach,
+};
+
+
+static int parport_dio_a(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       outb(it->data[0],dev->iobase+PARPORT_A);
+
+       return 1;
+}
+
+static int parport_dio_b(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       it->data[0]=(inb(dev->iobase+PARPORT_B)>>3);
+
+       return 1;
+}
+
+static int parport_dio_c(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       outb(it->data[0],dev->iobase+PARPORT_C);
+
+       return 1;
+}
+
+
+static int parport_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       int ret;
+       comedi_subdevice *s;
+
+       dev->iobase=it->options[0];
+       printk("comedi%d: parport: 0x%04x ",dev->minor,dev->iobase);
+       if(check_region(dev->iobase,PARPORT_SIZE)<0){
+               printk("I/O port conflict\n");
+               return -EIO;
+       }
+       request_region(dev->iobase,PARPORT_SIZE,"parport (comedi)");
+       dev->iosize=PARPORT_SIZE;
+       dev->irq=0;
+       dev->board_name="parport";
+
+       dev->n_subdevices=3;
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+
+       s=dev->subdevices+0;
+       s->type=COMEDI_SUBD_DO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->n_chan=8;
+       s->maxdata=1;
+       s->range_type=RANGE_digital;
+       s->trig[0]=parport_dio_a;
+
+       s=dev->subdevices+1;
+       s->type=COMEDI_SUBD_DI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=4;
+       s->maxdata=1;
+       s->range_type=RANGE_digital;
+       s->trig[0]=parport_dio_b;
+
+       s=dev->subdevices+2;
+       s->type=COMEDI_SUBD_DO;
+       s->subdev_flags=SDF_WRITEABLE;
+       s->n_chan=4;
+       s->maxdata=1;
+       s->range_type=RANGE_digital;
+       s->trig[0]=parport_dio_c;
+
+       printk("\n");
+       return 1;
+}
+
+
+static int parport_detach(comedi_device *dev)
+{
+       printk("comedi%d: parport: remove\n",dev->minor);
+       
+       release_region(dev->iobase,dev->iosize);
+
+       return 0;
+}
+
diff --git a/comedi/proc.c b/comedi/proc.c
new file mode 100644 (file)
index 0000000..5594496
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+    module/proc.c
+    /proc interface for comedi
+
+    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.
+
+*/
+
+/*
+       This is some serious bloatware.
+       
+       Taken from Dave A.'s PCL-711 driver, 'cuz I thought it
+       was cool.
+*/
+
+
+#include <comedi_module.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+
+
+#ifdef LINUX_V20
+int comedi_read_procmem(char *buf,char **start,off_t offset,int len,int unused);
+
+struct proc_dir_entry comedi_proc_entry =
+{
+       0,
+       6, "comedi",
+       S_IFREG | S_IRUGO,
+       1, 0, 0,
+       0, NULL,
+       &comedi_read_procmem,
+};
+
+#else
+int comedi_read_procmem(char *buf,char **start,off_t offset,int len,int *eof,void *data);
+
+
+
+#endif
+
+#ifdef LINUX_V20
+int comedi_read_procmem(char *buf,char **start,off_t offset,int len,int unused)
+#else
+int comedi_read_procmem(char *buf,char **start,off_t offset,int len,int *eof,void *data)
+#endif
+{
+       int i;
+       int devices_q=0;
+       int l=0;
+       
+       l+=sprintf(buf+l,
+               "comedi version " COMEDI_VERSION "\n"
+               "format string: %s\n",
+               "\"%2d: %-20s %-20s %4d\",i,driver_name,board_name,n_subdevices");
+
+       for(i=0;i<COMEDI_NDEVICES;i++){
+               if(comedi_devices[i].attached){
+                       devices_q=1;
+                       l+=sprintf(buf+l,"%2d: %-20s %-20s %4d\n",
+                               i,
+                               comedi_devices[i].driver->driver_name,
+                               comedi_devices[i].board_name,
+                               comedi_devices[i].n_subdevices
+                               );
+               }
+       }
+       if(!devices_q){
+               l+=sprintf(buf+l,"no devices\n");
+       }
+
+       return l;
+}
+
+
+void comedi_proc_init(void)
+{
+#ifdef LINUX_V20
+       proc_register_dynamic(&proc_root,&comedi_proc_entry);
+#else
+       struct proc_dir_entry *comedi_proc;
+       
+       comedi_proc = create_proc_entry("comedi",S_IFREG | S_IRUGO,0);
+       if(comedi_proc)
+               comedi_proc->read_proc = comedi_read_procmem;
+#endif
+}
+
+void comedi_proc_cleanup(void)
+{
+#ifdef LINUX_V20
+       proc_unregister(&proc_root,comedi_proc_entry.low_ino);
+#else
+       remove_proc_entry("comedi",0);
+#endif
+}
+
+
diff --git a/comedi/range.c b/comedi/range.c
new file mode 100644 (file)
index 0000000..e294409
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+    module/range.c
+    comedi 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.
+
+*/
+
+#define RANGE_C
+
+#include <comedi_module.h>
+#ifdef LINUX_V22
+#include <asm/uaccess.h>
+#endif
+
+
+
+/*
+--BEGIN-RANGE-DEFS--
+RANGE_bipolar10
+       -10     10
+RANGE_bipolar5
+       -5      5
+RANGE_unipolar10
+       0       10
+RANGE_unipolar5
+       0       5
+RANGE_unknown
+       0       1       unitless
+---END-RANGE-DEFS---
+*/
+
+/*
+       COMEDI_RANGEINFO
+       range information ioctl
+
+       arg:
+               pointer to rangeinfo structure
+
+       reads:
+               range info structure
+       
+       writes:
+               n comedi_krange structures to rangeinfo->range_ptr
+*/
+int do_rangeinfo_ioctl(comedi_device *dev,comedi_rangeinfo *arg)
+{
+       comedi_rangeinfo it;
+
+       if(copy_from_user(&it,arg,sizeof(comedi_rangeinfo)))
+               return -EFAULT;
+
+       if(RANGE_OFFSET(it.range_type)+RANGE_LENGTH(it.range_type)>comedi_max_range)
+               return -EINVAL;
+
+       if(copy_to_user(it.range_ptr,comedi_kranges+RANGE_OFFSET(it.range_type),
+               sizeof(comedi_krange)*RANGE_LENGTH(it.range_type)))
+               return -EFAULT;
+       
+       return 0;
+}
+
+
+/*
+   This function checks each element in a channel/gain list to make
+   make sure it is valid.
+*/
+int check_chanlist(comedi_subdevice *s,int n,unsigned int *chanlist)
+{
+       int i;
+       int chan;
+
+
+       if(s->range_type){
+               for(i=0;i<n;i++)
+                       if(CR_CHAN(chanlist[i])>=s->n_chan || 
+                          CR_RANGE(chanlist[i])>=RANGE_LENGTH(s->range_type)){
+                               rt_printk("bad chanlist[%d]=0x%08x n_chan=%d range length=%d\n",
+                                       i,chanlist[i],s->n_chan,RANGE_LENGTH(s->range_type));
+#if 0
+for(i=0;i<n;i++){
+       printk("[%d]=0x%08x\n",i,chanlist[i]);
+}
+#endif
+                               return -EINVAL;
+                       }
+       }else if(s->range_type_list){
+               for(i=0;i<n;i++){
+                       chan=CR_CHAN(chanlist[i]);
+                       if(chan>=s->n_chan ||
+                          CR_RANGE(chanlist[i])>=RANGE_LENGTH(s->range_type_list[chan])){
+                               rt_printk("bad chanlist[%d]=0x%08x\n",i,chanlist[i]);
+                               return -EINVAL;
+                       }
+               }
+       }else{
+               rt_printk("comedi: (bug) no range type list!\n");
+               return -EINVAL;
+       }
+       return 0;
+}
+
+
diff --git a/comedi/realtime/vd_dds.c b/comedi/realtime/vd_dds.c
new file mode 100644 (file)
index 0000000..17fc896
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+    module/vd_dds.c
+    virtual driver for direct digital signal synthesis
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1999 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 <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+#ifdef CONFIG_COMEDI_RTL_V1
+#include <rtlinux/rtl_sched.h>
+#endif
+#ifdef CONFIG_COMEDI_RTL
+#include <linux/rtl.h>
+#include <rtlinux/rtl_sched.h>
+#include <rtlinux/rtl_compat.h>
+#endif
+
+static int dds_attach(comedi_device *dev,comedi_devconfig *it);
+static int dds_detach(comedi_device *dev);
+comedi_driver driver_={
+       driver_name:    "dds",
+       attach:         dds_attach,
+       detach:         dds_detach,
+};
+
+static void dds_interrupt(int irq,void *dev,struct pt_regs * regs);
+
+typedef struct{
+       int device;
+       int subd;
+       comedi_device *dev;
+       comedi_subdevice *s;
+       RT_TASK rt_task;
+
+       unsigned int accumulator;
+       unsigned int args[3];
+
+       sampl_t data[2];
+
+       comedi_trig trig;
+       int soft_irq;
+}dds_private;
+#define devpriv ((dds_private *)dev->private)
+
+
+static comedi_device *broken_rtl_dev;
+
+static void dds_interrupt(int irq,void *d,struct pt_regs * regs)
+{
+       comedi_device *dev=broken_rtl_dev;
+
+       comedi_done(dev,dev->subdevices+0);
+
+       comedi_unlock_ioctl(devpriv->device,devpriv->subd);
+}
+
+static inline void buf_add(comedi_device *dev,comedi_subdevice *s,sampl_t x)
+{
+       *(sampl_t *)(((void *)(s->cur_trig.data))+s->buf_int_ptr)=x&0xfff;
+       s->buf_int_ptr+=sizeof(sampl_t);
+       if(s->buf_int_ptr>=s->cur_trig.data_len){
+               s->buf_int_ptr=0;
+               comedi_eobuf(dev,s);
+       }
+       s->buf_int_count+=sizeof(sampl_t);
+}
+
+
+static void dds_ao_task_func(int d)
+{
+       comedi_device *dev=(comedi_device *)d;
+       comedi_subdevice *s=dev->subdevices+0;
+       comedi_trig *it=&devpriv->trig;
+       comedi_trig *my_trig=&s->cur_trig;
+       int ret;
+
+       it->n_chan=1;
+       it->data=devpriv->data;
+       it->chanlist=my_trig->chanlist;
+       while(1){
+               ret=comedi_trig_ioctl(devpriv->device,devpriv->subd,it);
+
+               if(ret<0){
+                       /* eek! */
+               }
+               rt_task_wait();
+       }
+}
+
+static int dds_cntrl_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int i,chan;
+
+       for(i=0;i<it->n_chan;i++){
+               chan=CR_CHAN(it->chanlist[i]);
+
+               devpriv->args[chan]=it->data[i];
+       }
+       return i;
+}
+
+
+static int dds_ao_mode2(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int ret;
+       RTIME now,period;
+       struct timespec ts;
+
+       //if(it->trigvar1!=0)return -EINVAL;
+       if(it->trigvar<100000)return -EINVAL;   /* 10 khz */
+       
+       ret=comedi_lock_ioctl(devpriv->device,devpriv->subd);
+       if(ret<0)goto out;
+
+#if 0
+       struct timespec ts;
+
+       ts.tv_sec=0;
+       ts.tv_nsec=it->trigvar;
+#endif
+
+       devpriv->trig.subdev=devpriv->subd;
+       devpriv->trig.mode=TRIG_WRITE;
+       devpriv->trig.flags=0;
+       devpriv->trig.n=1;
+
+       ts.tv_sec=0;
+       ts.tv_nsec=it->trigvar;
+       period=timespec_to_RTIME(ts);
+
+       rt_task_init(&devpriv->rt_task,dds_ai_task_func,(int)dev,3000,4);
+
+       now=rt_get_time();
+       rt_task_make_periodic(&devpriv->rt_task,now+period,period);
+
+       return 0;
+
+unlock:
+       comedi_unlock_ioctl(devpriv->device,devpriv->subd);
+out:
+       return ret;
+}
+
+int dds_ao_cancel(comedi_device *dev,comedi_subdevice *s)
+{
+       rt_task_delete(&devpriv->rt_task);
+
+       comedi_unlock_ioctl(devpriv->device,devpriv->subd);
+
+       return 0;
+}
+
+int dds_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       int ret;
+       comedi_subdevice *s;
+
+       if(strcmp("dds",it->board_name))
+               return 0;
+
+       printk("comedi%d: dds: ",dev->minor);
+       dev->board_name="dds";
+       dev->driver_name="dds";
+
+       dev->n_subdevices=1;
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+       if((ret=alloc_private(dev,sizeof(dds_private)))<0)
+               return ret;
+
+       devpriv->device=it->options[0];
+       devpriv->subd=it->options[1];
+
+       devpriv->dev=comedi_devices+devpriv->device;
+       devpriv->s=devpriv->dev->subdevices+devpriv->subd;
+
+       s=dev->subdevices+0;
+       s->type=COMEDI_SUBD_AO;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=devpriv->s->n_chan;
+       s->len_chanlist=1024;
+       s->trig[2]=dds_ao_mode2;
+       s->cancel=dds_ao_cancel;
+       s->maxdata=devpriv->s->maxdata;
+       s->range_type=devpriv->s->range_type;
+       s->timer_type=TIMER_nanosec;
+
+       s=dev->subdevices+0;
+       s->type=COMEDI_SUBD_AO;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=devpriv->s->n_chan;
+       s->len_chanlist=1024;
+       s->trig[2]=dds_ao_mode2;
+       s->cancel=dds_ao_cancel;
+       s->maxdata=devpriv->s->maxdata;
+       s->range_type=devpriv->s->range_type;
+
+       devpriv->soft_irq=rtl_get_soft_irq(dds_interrupt,"dds");
+       broken_rtl_dev=dev;
+
+       printk("\n");
+
+       return 1;
+}
+
+
+static int dds_detach(comedi_device *dev)
+{
+       printk("comedi%d: dds: remove\n",dev->minor);
+       
+       free_irq(devpriv->soft_irq,NULL);
+
+       return 0;
+}
+
+
diff --git a/comedi/realtime/vd_timer.c b/comedi/realtime/vd_timer.c
new file mode 100644 (file)
index 0000000..517d2d5
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+    module/vd_timer.c
+    virtual driver for using RTL timing sources
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1999 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 <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <comedi_module.h>
+
+#ifdef CONFIG_COMEDI_RTL_V1
+#include <rtlinux/rtl_sched.h>
+#include <asm/rt_irq.h>
+#endif
+
+#ifdef CONFIG_COMEDI_RTL
+#include <linux/rtl.h>
+#include <rtlinux/rtl_sched.h>
+#include <rtlinux/rtl_compat.h>
+#endif
+
+static int timer_attach(comedi_device *dev,comedi_devconfig *it);
+static int timer_detach(comedi_device *dev);
+comedi_driver driver_timer={
+       driver_name:    "timer",
+       attach:         timer_attach,
+       detach:         timer_detach,
+};
+
+
+static void timer_interrupt(int irq,void *dev,struct pt_regs * regs);
+
+typedef struct{
+       int device;
+       int subd;
+       comedi_device *dev;
+       comedi_subdevice *s;
+       RT_TASK rt_task;
+       sampl_t *data;
+       comedi_trig trig;
+       int soft_irq;
+}timer_private;
+#define devpriv ((timer_private *)dev->private)
+
+
+static comedi_device *broken_rtl_dev;
+
+static void timer_interrupt(int irq,void *d,struct pt_regs * regs)
+{
+       comedi_device *dev=broken_rtl_dev;
+
+       comedi_done(dev,dev->subdevices+0);
+
+       comedi_unlock_ioctl(devpriv->device,devpriv->subd);
+}
+
+static inline void buf_add(comedi_device *dev,comedi_subdevice *s,sampl_t x)
+{
+       *(sampl_t *)(((void *)(s->cur_trig.data))+s->buf_int_ptr)=x&0xfff;
+       s->buf_int_ptr+=sizeof(sampl_t);
+       if(s->buf_int_ptr>=s->cur_trig.data_len){
+               s->buf_int_ptr=0;
+               comedi_eobuf(dev,s);
+       }
+       s->buf_int_count+=sizeof(sampl_t);
+}
+
+
+static void timer_ai_task_func(int d)
+{
+       comedi_device *dev=(comedi_device *)d;
+       comedi_subdevice *s=dev->subdevices+0;
+       comedi_trig *it=&devpriv->trig;
+       comedi_trig *my_trig=&s->cur_trig;
+       int i,n,ret;
+       int n_chan;
+
+       n_chan=s->cur_trig.n_chan;
+
+       for(n=0;n<my_trig->n;n++){
+               for(i=0;i<n_chan;i++){
+                       it->n_chan=1;
+                       it->data=devpriv->data+i;
+                       it->chanlist=my_trig->chanlist+i;
+
+                       ret=comedi_trig_ioctl(devpriv->device,devpriv->subd,it);
+
+                       if(ret<0){
+                               /* eek! */
+                       }
+               }
+               for(i=0;i<n_chan;i++){
+                       buf_add(dev,s,devpriv->data[i]);
+               }
+               rt_task_wait();
+       }
+       rtl_global_pend_irq(devpriv->soft_irq);
+
+       rt_task_delete(&devpriv->rt_task);
+
+       /* eek! */
+}
+
+static int timer_ai_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       return comedi_trig_ioctl(devpriv->device,devpriv->subd,it);
+}
+
+static int timer_ai_mode1(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       return -EINVAL;
+}
+
+
+static int timer_ai_mode2(comedi_device *dev,comedi_subdevice *s,comedi_trig *it)
+{
+       int ret;
+       RTIME now,period;
+       struct timespec ts;
+
+       //if(it->trigvar1!=0)return -EINVAL;
+       if(it->trigvar<100000)return -EINVAL;   /* 10 khz */
+       
+       ret=comedi_lock_ioctl(devpriv->device,devpriv->subd);
+       if(ret<0)goto out;
+
+#if 0
+       struct timespec ts;
+
+       ts.tv_sec=0;
+       ts.tv_nsec=it->trigvar;
+#endif
+
+       /* XXX this does not get freed */
+       devpriv->data=kmalloc(sizeof(sampl_t)*it->n_chan,GFP_KERNEL);
+       if(!devpriv->data){
+               ret=-ENOMEM;
+               goto unlock;
+       }
+
+       devpriv->trig.subdev=devpriv->subd;
+       devpriv->trig.mode=0;
+       devpriv->trig.flags=0;
+       devpriv->trig.n=1;
+
+       ts.tv_sec=0;
+       ts.tv_nsec=it->trigvar;
+       period=timespec_to_RTIME(ts);
+
+       rt_task_init(&devpriv->rt_task,timer_ai_task_func,(int)dev,3000,4);
+
+       now=rt_get_time();
+       rt_task_make_periodic(&devpriv->rt_task,now+period,period);
+
+       return 0;
+
+unlock:
+       comedi_unlock_ioctl(devpriv->device,devpriv->subd);
+out:
+       return ret;
+}
+
+int timer_cancel(comedi_device *dev,comedi_subdevice *s)
+{
+       rt_task_delete(&devpriv->rt_task);
+
+       comedi_unlock_ioctl(devpriv->device,devpriv->subd);
+
+       return 0;
+}
+
+static int timer_attach(comedi_device *dev,comedi_devconfig *it)
+{
+       int ret;
+       comedi_subdevice *s;
+
+       printk("comedi%d: timer: ",dev->minor);
+       dev->board_name="timer";
+
+       dev->n_subdevices=1;
+       if((ret=alloc_subdevices(dev))<0)
+               return ret;
+       if((ret=alloc_private(dev,sizeof(timer_private)))<0)
+               return ret;
+
+       devpriv->device=it->options[0];
+       devpriv->subd=it->options[1];
+
+       devpriv->dev=comedi_devices+devpriv->device;
+       devpriv->s=devpriv->dev->subdevices+devpriv->subd;
+
+       s=dev->subdevices+0;
+       s->type=COMEDI_SUBD_AI;
+       s->subdev_flags=SDF_READABLE;
+       s->n_chan=devpriv->s->n_chan;
+       s->len_chanlist=1024;
+       s->trig[0]=timer_ai_mode0;
+       s->trig[1]=timer_ai_mode1;
+       s->trig[2]=timer_ai_mode2;
+       s->cancel=timer_cancel;
+       s->maxdata=devpriv->s->maxdata;
+       s->range_type=devpriv->s->range_type;
+       s->timer_type=TIMER_nanosec;
+
+       devpriv->soft_irq=rtl_get_soft_irq(timer_interrupt,"timer");
+       broken_rtl_dev=dev;
+
+       printk("\n");
+
+       return 1;
+}
+
+
+static int timer_detach(comedi_device *dev)
+{
+       printk("comedi%d: timer: remove\n",dev->minor);
+       
+       free_irq(devpriv->soft_irq,NULL);
+
+       return 0;
+}
+
+
diff --git a/comedi/rtai.c b/comedi/rtai.c
new file mode 100644 (file)
index 0000000..3fcbe4d
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * RTAI support rooutines
+ *
+ */
+
+#include <comedi_module.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <asm/irq.h>
+#include <asm/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+
+extern void rt_unmask_irq(unsigned int irq);
+
+static struct comedi_irq_struct *rtai_irq;
+
+static void handle_rtai_irq(void)
+{
+       struct comedi_irq_struct *it=rtai_irq;
+
+       if(it)
+               it->handler(it->irq,it->dev_id,NULL);
+
+       rt_unmask_irq(it->irq);
+}
+
+int get_priority_irq(struct comedi_irq_struct *it)
+{
+       rtai_irq=it;
+
+       //free_irq(it->irq,it->dev_id);
+       rt_request_global_irq(it->irq,handle_rtai_irq);
+       rt_enable_irq(it->irq);
+
+       return 0;
+}
+
+int free_priority_irq(struct comedi_irq_struct *it)
+{
+       rt_free_global_irq(it->irq);
+       //request_irq(it->irq,it->handler,it->flags&~SA_PRIORITY,"fixme",it->dev_id);
+
+       return 0;
+}
+
+
+void comedi_rtai_init(void)
+{
+
+}
+
+void comedi_rtai_cleanup(void)
+{
+
+}
+
diff --git a/comedi/rtai.h b/comedi/rtai.h
new file mode 100644 (file)
index 0000000..144c1df
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ *  RTL compatibility,, version 1
+ *
+ */
+
+#ifndef __COMEDI_RTAI_H
+#define __COMEDI_RTAI_H
+
+#include <rtai/rtai.h>
+
+#if 0
+int rt_printk(const char *fmt, ...);
+
+void rt_printk_cleanup(void);
+int rt_printk_init(void);
+#endif
+
+void comedi_rtai_init(void);
+void comedi_rtai_cleanup(void);
+
+
+#endif
+
diff --git a/comedi/rtl.c b/comedi/rtl.c
new file mode 100644 (file)
index 0000000..da8c222
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * RTLinux support rooutines
+ *
+ */
+
+#include <comedi_module.h>
+#include <rtl.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <asm/irq.h>
+#include <asm/ptrace.h>
+#include <linux/rtl.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#if 0
+#include <rtlinux/rtl_posixio.h>
+#endif
+
+
+
+/* rt_printk() section */
+
+#define BUF_LEN        (16384)
+
+static char rt_printk_buf[BUF_LEN];
+static char buf[1024];
+
+static int buf_front,buf_back;
+
+static int rt_printk_irq;
+
+/* this is rather bogus, but it will catch most errors */
+static volatile int rt_printk_lock;
+
+static void rt_printk_interrupt(int irq,void *junk,struct pt_regs *regs);
+
+int rt_printk(const char *fmt, ...)
+{
+       va_list args;
+       int len,i;
+
+       if(rtl_rt_system_is_idle()){
+               va_start(args, fmt);
+               len=printk(fmt,args);
+               va_end(args);
+               return len;
+       }
+
+       if(rt_printk_lock){
+               /* force a panic */
+               *(int *)0=0;
+       }
+       rt_printk_lock=1;
+
+       va_start(args, fmt);
+       len = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf)-1 */
+       va_end(args);
+
+       if(buf_front+len>=BUF_LEN){
+               i=BUF_LEN-buf_front;
+               memcpy(rt_printk_buf+buf_front,buf,i);
+               memcpy(rt_printk_buf,buf+i,len-i);
+               buf_front=len-i;
+       }else{
+               memcpy(rt_printk_buf+buf_front,buf,len);
+               buf_front+=len;
+       }
+
+       rt_printk_lock=0;
+
+       rtl_global_pend_irq(rt_printk_irq);
+
+       return len;
+}
+
+
+void rt_printk_interrupt(int irq,void *junk,struct pt_regs *regs)
+{
+       int tmp;
+
+       for(;;){
+               tmp=buf_front;
+               if(buf_back>tmp){
+                       printk("%.*s",BUF_LEN-buf_back,rt_printk_buf+buf_back);
+                       buf_back=0;
+               }
+               if(buf_back==tmp)break;
+               printk("%.*s",tmp-buf_back,rt_printk_buf+buf_back);
+               buf_back=tmp;
+       }
+}
+
+void rt_printk_cleanup(void)
+{
+       free_irq(rt_printk_irq,NULL);
+       rt_printk_irq=0;
+}
+
+int rt_printk_init(void)
+{
+       rt_printk_irq=rtl_get_soft_irq(rt_printk_interrupt,"rt_printk");
+       if(rt_printk_irq<0){
+               printk("rt_printk: can't allocate soft irq\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+
+#if 0
+
+static ssize_t comedi_rtl_read(struct rtl_file *file,char *buf,
+       size_t len,loff_t *p)
+{
+       return -EIO;
+}
+
+static ssize_t comedi_rtl_write(struct rtl_file *file,const char *buf,
+       size_t len,loff_t *p)
+{
+       return -EIO;
+}
+
+static int comedi_rtl_ioctl(struct rtl_file *file,unsigned int cmd,
+       unsigned long arg)
+{
+       switch(cmd){
+       case COMEDI_TRIG:
+         {
+               comedi_trig *it=(comedi_trig *)arg;
+               int minor=file->f_minor;
+               int subdev=it->subdev;
+
+               return comedi_trig_ioctl(minor,subdev,it);
+         }
+       case COMEDI_LOCK:
+         {
+               int minor=file->f_minor;
+               int subdev=(int)arg;
+
+               return comedi_lock_ioctl(minor,subdev);
+         }
+       case COMEDI_UNLOCK:
+         {
+               int minor=file->f_minor;
+               int subdev=(int)arg;
+
+               return comedi_lock_ioctl(minor,subdev);
+         }
+       case COMEDI_CANCEL:
+         {
+               int minor=file->f_minor;
+               int subdev=(int)arg;
+
+               return comedi_cancel_ioctl(minor,subdev);
+         }
+       }
+
+       return -EIO;
+}
+
+static int comedi_rtl_open(struct rtl_file *file)
+{
+       return 0;
+}
+
+static int comedi_rtl_release(struct rtl_file *file)
+{
+       return 0;
+}
+
+
+struct rtl_file_operations comedi_rtl_fops={
+       read:           comedi_rtl_read,
+       write:          comedi_rtl_write,
+       ioctl:          comedi_rtl_ioctl,
+       open:           comedi_rtl_open,
+       release:        comedi_rtl_release,
+};
+
+#endif
+
+
+static unsigned int handle_rtl_irq(unsigned int irq,struct pt_regs *regs)
+{
+       struct comedi_irq_struct *it;
+
+       it=get_irq_struct(irq);
+       if(it)
+               it->handler(irq,it->dev_id,regs);
+       rtl_hard_enable_irq(irq);
+       return 0;
+}
+
+int get_priority_irq(struct comedi_irq_struct *it)
+{
+       rtl_request_global_irq(it->irq,handle_rtl_irq);
+
+       return 0;
+}
+
+int free_priority_irq(struct comedi_irq_struct *it)
+{
+       rtl_free_irq(it->irq);
+
+       return 0;
+}
+
+
+void comedi_rtl_init(void)
+{
+       rt_printk_init();
+       //rtl_register_chardev(COMEDI_MAJOR,"comedi",&comedi_rtl_fops);
+}
+
+void comedi_rtl_cleanup(void)
+{
+       rt_printk_cleanup();
+       //rtl_unregister_chardev(COMEDI_MAJOR);
+}
+
diff --git a/comedi/rtl.h b/comedi/rtl.h
new file mode 100644 (file)
index 0000000..30b2c82
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ *  rt_printk.c, hacked from linux/kernel/printk.c
+ *
+ * Modified for RT support, David Schleef
+ */
+
+#ifndef __COMEDI_RTL_H
+#define __COMEDI_RTL_H
+
+int rt_printk(const char *fmt, ...);
+
+void rt_printk_cleanup(void);
+int rt_printk_init(void);
+
+void comedi_rtl_init(void);
+void comedi_rtl_cleanup(void);
+
+
+#endif
+
diff --git a/comedi/rtl_v1.c b/comedi/rtl_v1.c
new file mode 100644 (file)
index 0000000..b0a3753
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * RTLinux v1 support rooutines
+ *
+ */
+
+#include <comedi_module.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <asm/irq.h>
+#include <asm/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+
+static struct comedi_irq_struct *rtlv1_irq;
+
+static void handle_rtlv1_irq(void)
+{
+       struct comedi_irq_struct *it=rtlv1_irq;
+
+       if(it)
+               it->handler(it->irq,it->dev_id,NULL);
+}
+
+int get_priority_irq(struct comedi_irq_struct *it)
+{
+       rtlv1_irq=it;
+       request_RTirq(it->irq,handle_rtlv1_irq);
+
+       return 0;
+}
+
+int free_priority_irq(struct comedi_irq_struct *it)
+{
+       free_RTirq(it->irq);
+       rtlv1_irq=NULL;
+
+       return 0;
+}
+
+
+
+void comedi_rtl_v1_init(void)
+{
+
+}
+
+void comedi_rtl_v1_cleanup(void)
+{
+
+}
+
+
+
diff --git a/comedi/rtl_v1.h b/comedi/rtl_v1.h
new file mode 100644 (file)
index 0000000..97cceef
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ *  RTL compatibility,, version 1
+ *
+ */
+
+#ifndef __COMEDI_RTL_V1_H
+#define __COMEDI_RTL_V1_H
+
+#include <asm/rt_irq.h>
+
+#if 0
+int rt_printk(const char *fmt, ...);
+
+void rt_printk_cleanup(void);
+int rt_printk_init(void);
+#endif
+
+void comedi_rtl_v1_init(void);
+void comedi_rtl_v1_cleanup(void);
+
+
+#endif
+
diff --git a/comedi_config/Makefile b/comedi_config/Makefile
new file mode 100644 (file)
index 0000000..1fe9eee
--- /dev/null
@@ -0,0 +1,12 @@
+
+CFLAGS=-Wall -O2 -I../include
+#LDFLAGS=-L../lib/ -lcomedi
+LDFLAGS=
+
+OBJS=comedi_config.o
+
+comedi_config: $(OBJS)
+       $(CC) -o comedi_config $(OBJS) $(LDFLAGS)
+
+clean:
+       rm -f comedi_config *.o
diff --git a/comedi_config/comedi_config.c b/comedi_config/comedi_config.c
new file mode 100644 (file)
index 0000000..da4e90b
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+    comedi_config/comedi_config.c
+    configuration program for comedi kernel module
+
+    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.
+
+*/
+
+#define CC_VERSION     "0.6.13"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <comedi.h>
+
+int quiet=0,verbose=0,script=0;
+
+void do_script(void);
+
+void do_help(int i)
+{
+       fputs(
+"comedi_config version " CC_VERSION "\n"
+"usage:  comedi_config [-[vVq]] <device file> <driver> <option1>,<option2>,...\n"
+"where <optionN> are integers (or blank) whose interpretation depends on\n"
+"the driver.  In general, however, option1 refers to the I/O port address\n"
+"and option2 refers to IRQ number to be used by the driver\n"
+               ,stderr);
+       exit(i);
+}
+
+int main(int argc,char *argv[])
+{
+       comedi_devconfig it;
+       int fd;
+       int c,i,num,k;
+       char *opts;
+       char *fn,*driver;
+       struct stat statbuf;
+       int ret;
+       int remove=0;
+       
+       while(1){
+               c=getopt(argc,argv,"rvVq");
+               if(c==-1)break;
+               switch(c){
+               case 'v':
+                       verbose=1;
+                       break;
+               case 'V':
+                       fputs("comedi_config version " CC_VERSION "\n",stderr);
+                       exit(0);
+                       break;
+               case 'q':
+                       quiet=1;
+                       break;
+               case 'a':
+                       script=1;
+                       break;
+               case 'r':
+                       remove=1;
+                       break;
+               default:
+                       do_help(1);
+               }
+       }
+
+       if(script){
+               if( (argc-optind)>0 ){
+                       do_help(1);
+               }else{
+                       do_script();
+               }
+       }
+       
+       if((argc-optind)!=2 && (argc-optind)!=3){
+               do_help(1);
+       }
+               
+       fn=argv[optind];
+       
+       driver=argv[optind+1];
+       strncpy(it.board_name,driver,COMEDI_NAMELEN-1);
+
+       for(i=0;i<COMEDI_NDEVCONFOPTS;i++)it.options[i]=0;
+
+       if((argc-optind)==3){
+               opts=argv[optind+2];
+               i=0;
+               while(*opts){
+                       if((*opts)==','){
+                               i++;
+                               opts++;
+                               if(i>=COMEDI_NDEVCONFOPTS)
+                                       do_help(1);
+                               continue;
+                       }
+                       if(sscanf(opts,"%i%n",&num,&k)>0){
+                               it.options[i]=num;
+                               opts+=k;
+                               continue;
+                       }
+                       do_help(1);
+               }
+       }
+       
+       ret=stat(fn,&statbuf);
+       if(ret<0){
+               perror(fn);
+               exit(1);
+       }
+#if 0
+       /* this appears to be broken */
+       if(  !(S_ISCHR(statbuf.st_mode)) ||
+            major(statbuf.st_dev)!=COMEDI_MAJOR){
+               if(!quiet)
+                       fprintf(stderr,"warning: %s might not be a comedi device\n",fn);
+       }
+#endif
+       
+       fd=open(fn,O_RDWR);
+       if(fd<0){
+               perror(fn);
+               exit(1);
+       }
+       
+       /* add: sanity check for device */
+       
+       if(verbose){
+               printf("configuring driver=%s ",it.board_name);
+               for(i=0;i<COMEDI_NDEVCONFOPTS;i++)printf("%d,",it.options[i]);
+               printf("\n");
+       }
+       if(ioctl(fd,COMEDI_DEVCONFIG,remove?NULL:&it)<0){
+               perror("Configure failed!");
+               exit(1);
+       }
+       exit(0);
+}
+
+void do_script(void)
+{
+       printf("comedi_config: -a option not supported (yet).\n");
+       exit(0);
+}
+
diff --git a/etc/comedi.conf b/etc/comedi.conf
new file mode 100755 (executable)
index 0000000..48fbac7
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/bash
+#
+#  This script is run right after the comedi module is
+#  loaded.
+#
+
+# Example for a National Instruments AT-MIO E series board
+#/usr/sbin/comedi_config /dev/comedi0 atmio-E 0x260,5
+
+
+# Example for a Data Translation DT2821 series board
+# uses file dt282x.conf
+/etc/dt282x.conf
+
+
diff --git a/etc/conf.modules b/etc/conf.modules
new file mode 100644 (file)
index 0000000..1a99145
--- /dev/null
@@ -0,0 +1,8 @@
+
+#
+# add these lines to /etc/conf.modules
+#
+
+alias char-major-98 comedi
+post-install comedi /etc/comedi.conf
+
diff --git a/etc/das1600.conf b/etc/das1600.conf
new file mode 100755 (executable)
index 0000000..7732c7b
--- /dev/null
@@ -0,0 +1,89 @@
+#!/bin/sh
+# Configuration for das1600 driver
+# Thomas Henkel and ds
+
+### Device file
+DEVICE=/dev/comedi0
+
+
+### Type of board
+###   Choose one
+BOARD=das1601/12
+#BOARD=das1602/12
+#BOARD=das1602/16
+
+
+### Option 0:  Board base address
+###   Depends on jumper settings
+IO_BASE=0x320
+
+
+### Option 1:  IRQ
+###   Choose one
+#IRQ=0         # don't use interrupts
+#IRQ=3
+#IRQ=4
+#IRQ=5
+#IRQ=6
+IRQ=7
+#IRQ=9
+
+
+### Option 2:  DMA channel
+###   Depends on jumper settings
+###   Choose one
+DMA=0          # don't use DMA
+#DMA=1
+#DMA=2
+#DMA=3
+
+
+### Option 3:  Crystal selection
+###   Depends on jumper settings
+###   Choose one
+CRYSTAL=0      # 10 Mhz
+#CRYSTAL=1     # 1 Mhz
+
+
+### Option 4:  Analog Input reference
+###   Depends on jumper settings
+###   Choose one
+A_INP_TYPE=0   # differential
+#A_INP_TYPE=1  # single ended
+
+
+### Option 5:  Analog Input range
+###   Depends on jumper settings
+###   Choose one
+#A_INP_RANGE=0 # bipolar (-10 V -- +10 V)
+#A_INP_RANGE=1 # unipolar (0 V -- +10 V)
+
+
+### Option 6:  Analog Output 0 Range
+###   Depends on jumper settings
+###   Choose one
+#A_OUT_0=0     # bipolar +/- 10 V
+#A_OUT_0=1     # bipolar +/- 5 V
+#A_OUT_0=2     # bipolar external reference
+#A_OUT_0=3     # unipolar 0-10 V
+#A_OUT_0=4     # unipolar 0-5 V
+#A_OUT_0=5     # unipolar external reference
+
+
+### Option 7:  Analog Output 1 Range
+###   Depends on jumper settings
+###   Choose one
+#A_OUT_1=0     # bipolar +/- 10 V
+#A_OUT_1=1     # bipolar +/- 5 V
+#A_OUT_1=2     # bipolar external reference
+#A_OUT_1=3     # unipolar 0-10 V
+#A_OUT_1=4     # unipolar 0-5 V
+#A_OUT_1=5     # unipolar external reference
+
+#-----------------------------------------------------------
+# End of Configuration
+#-----------------------------------------------------------
+
+
+/usr/sbin/comedi_config $DEVICE $BOARD $IO_BASE,$IRQ,$DMA,$CRYSTAL,$A_INP_TYPE,$A_INP_RANGE,$A_OUT_0,$A_OUT_1
+
diff --git a/etc/dt282x.conf b/etc/dt282x.conf
new file mode 100755 (executable)
index 0000000..03e3556
--- /dev/null
@@ -0,0 +1,119 @@
+#!/bin/sh
+# Configuration for dt282x driver
+# ds
+
+### Device file
+DEVICE=/dev/comedi0
+
+
+### Type of board
+###   Choose one
+BOARD=dt2821           # also for -f-16se, -g-16se, etc.
+#BOARD=dt2823
+#BOARD=dt2824-pgh
+#BOARD=dt2824-pgl
+#BOARD=dt2825
+#BOARD=dt2827
+#BOARD=dt2828
+#BOARD=dt21-ez
+#BOARD=dt23-ez
+#BOARD=dt24-ez
+#BOARD=dt24-ez-pgl
+
+
+### Option 0:  Board base address
+###   Depends on jumper settings
+IOBASE=0x240
+
+
+### Option 1:  IRQ
+###   Depends on jumper settings
+###   Choose one
+#IRQ=0         # don't use interrupts
+#IRQ=3
+#IRQ=5
+IRQ=7
+#IRQ=10
+#IRQ=15
+
+
+### Option 2:  first DMA channel
+###   Depends on jumper settings
+###   Choose one
+DMA1=0         # don't use DMA
+#DMA1=5
+#DMA1=6
+
+
+### Option 3:  second DMA channel
+###   Depends on jumper settings
+###   Choose one
+DMA2=0         # don't use secondary DMA
+#DMA2=6
+#DMA2=7
+
+
+### Option 4:  Analog Input reference
+###   Depends on jumper settings or type of board
+###   Choose one
+AI_REF=0       # differential
+#AI_REF=1      # single ended
+
+
+### Option 5:  Analog Input encoding
+###   Depends on jumper settings
+###   Choose one
+AI_ENCODE=0    # straight binary
+#AI_ENCODE=1   # two's complement
+
+
+### Option 6:  Analog Output 0 encoding
+###   Depends on jumper settings
+###   Choose one
+A00_ENCODE=0   # straight binary
+#AO0_ENCODE=1  # two's complement
+
+
+### Option 7:  Analog Output 1 encoding
+###   Depends on jumper settings
+###   Choose one
+AO1_ENCODE=0   # straight binary
+#AO1_ENCODE=1  # two's complement
+
+
+### Option 8:  Analog Input Range
+###   Depends on jumper settings
+###   Choose one
+#AI_RANGE=0    # bipolar +/- 10 V
+#AI_RANGE=1    # unipolar 0-10 V
+#AI_RANGE=2    # bipolar +/- 5 V
+#AI_RANGE=3    # unipolar 0-5 V
+
+
+### Option 9:  Analog Output 0 Range
+###   Depends on jumper settings
+###   Choose one
+#AO0_RANGE=0   # bipolar +/- 10 V
+#AO0_RANGE=1   # unipolar 0-10 V
+#AO0_RANGE=2   # bipolar +/- 5 V
+#AO0_RANGE=3   # unipolar 0-5 V
+#AO0_RANGE=4   # bipolar +/- 2.5 V
+
+### Option 10: Analog Output 1 Range
+###   Depends on jumper settings
+###   Choose one
+#AO1_RANGE=0   # bipolar +/- 10 V
+#AO1_RANGE=1   # unipolar 0-10 V
+#AO1_RANGE=2   # bipolar +/- 5 V
+#AO1_RANGE=3   # unipolar 0-5 V
+#AO1_RANGE=4   # bipolar +/- 2.5 V
+
+#-----------------------------------------------------------
+# End of Configuration
+#-----------------------------------------------------------
+
+
+/usr/sbin/comedi_config $DEVICE $BOARD $IOBASE,$IRQ,$DMA1,$DMA2,$AI_REF,$AI_ENCODE,$AO0_ENCODE,$AO1_ENCODE,$AI_REF,$AO0_REF,$AI1_REF
+
diff --git a/etc/isapnp.conf b/etc/isapnp.conf
new file mode 100644 (file)
index 0000000..18c7720
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# This is a sample isapnp.conf file for an NI AT-MIO E series card.
+#
+# You should generate your own with pnpdump.
+#
+
+
+# (DEBUG)
+(READPORT 0x0203)
+(ISOLATE)
+(IDENTIFY *)
+
+# Card 1: (serial identifier c4 00 00 03 3a 00 19 23 39)
+# NIC1900 Serial No 826 [checksum c4]
+# Version 1.0, Vendor version 0.0
+# ANSI string -->AT-MIO-16E-2, National Instruments, High Speed 12-bit MIO DAQ Board<--
+#
+# Logical device id NIC0000
+#     Device support I/O range check register
+#
+# Edit the entries below to uncomment out the configuration required.
+# Note that only the first value of any range is given, this may be changed if required
+# Don't forget to uncomment the activate (ACT Y) when happy
+
+(CONFIGURE NIC1900/826 (LD 0
+ (IO 0 (BASE 0x0260))
+ (ACT Y)
+))
+
+(WAITFORKEY)
+
diff --git a/include/comedi.h b/include/comedi.h
new file mode 100644 (file)
index 0000000..06b3a32
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+    include/comedi.h (installed as /usr/include/comedi.h)
+    header file for comedi
+
+    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 _COMEDI_H
+#define _COMEDI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* comedi's major device number */
+#define COMEDI_MAJOR 98
+
+/*
+   maximum number of minor devices.  This can be increased, although
+   kernel structures are currently statically allocated, thus you
+   don't want this to be much more than you actually use.
+ */
+#define COMEDI_NDEVICES 4
+
+/* number of config options in the config structure */
+#define COMEDI_NDEVCONFOPTS 32
+
+/* max length of device and driver names */
+#define COMEDI_NAMELEN 20
+
+
+typedef unsigned int lsampl_t;
+typedef unsigned short sampl_t;
+
+/* packs and unpacks a channel/range number */
+
+#define CR_PACK(chan,rng,aref)         ( (((aref)&0x3)<<24) | (((rng)&0xff)<<16) | ((chan)&0xffff) )
+
+#define CR_CHAN(a)     ((a)&0xffff)
+#define CR_RANGE(a)    (((a)>>16)&0xff)
+#define CR_AREF(a)     (((a)>>24)&0x03)
+
+#define AREF_GROUND    0x00            /* analog ref = analog ground */
+#define AREF_COMMON    0x01            /* analog ref = analog common */
+#define AREF_DIFF      0x02            /* analog ref = differential */
+#define AREF_OTHER     0x03            /* analog ref = other (undefined) */
+
+/* trigger flags */
+
+#define TRIG_BOGUS     0x0001          /* do the motions */
+#define TRIG_DITHER    0x0002          /* enable dithering */
+#define TRIG_DEGLITCH  0x0004          /* enable deglitching */
+#define TRIG_RT                0x0008          /* perform op in real time */
+#define TRIG_CONFIG    0x0010          /* perform configuration, not triggering */
+#define TRIG_WAKE_EOS  0x0020          /* wake up on end-of-scan events */
+#define TRIG_WRITE     0x0040          /* write to bidirectional devices */
+
+/* trigger sources */
+
+#define TRIG_ANY       0               
+#define TRIG_NONE      0               /* never trigger */
+#define TRIG_NOW       1               /* trigger now + N ns */
+#define TRIG_FOLLOW    2               /* trigger on next lower level trig */
+#define TRIG_TIME      3               /* trigger at time N ns */
+#define TRIG_TIMER     4               /* trigger at rate N ns */
+#define TRIG_COUNT     5               /* trigger when count reaches N */
+#define TRIG_EXT       6               /* trigger on external signal N */
+#define TRIG_INT       7               /* trigger on comedi-internal signal N */
+
+#define TRIG_INVAL     8               /* choice was invalid */
+
+/* subdevice flags */
+
+#define SDF_BUSY       0x0001          /* device is busy */
+#define SDF_BUSY_OWNER 0x0002          /* device is busy with your job */
+#define SDF_LOCKED     0x0004          /* subdevice is locked */
+#define SDF_LOCK_OWNER 0x0008          /* you own lock */
+#define SDF_MAXDATA    0x0010          /* maxdata depends on channel */
+#define SDF_FLAGS      0x0020          /* flags depend on channel */
+#define SDF_RANGETYPE  0x0040          /* range type depends on channel */
+#define SDF_MODE0      0x0080          /* can do mode 0 */
+#define SDF_MODE1      0x0100          /* can do mode 1 */
+#define SDF_MODE2      0x0200          /* can do mode 2 */
+#define SDF_MODE3      0x0400          /* can do mode 3 */
+#define SDF_MODE4      0x0800          /* can do mode 4 */
+
+#define SDF_READABLE   0x00010000      /* subdevice can be read (e.g. analog input) */
+#define SDF_WRITEABLE  0x00020000      /* subdevice can be written (e.g. analog output) */
+#define SDF_INTERNAL   0x00040000      /* subdevice does not have externally visible lines */
+#define SDF_RT         0x00080000      /* subdevice is RT capable */
+#define SDF_GROUND     0x00100000      /* can do aref=ground */
+#define SDF_COMMON     0x00200000      /* can do aref=common */
+#define SDF_DIFF       0x00400000      /* can do aref=diff */
+#define SDF_OTHER      0x00800000      /* can do aref=other */
+#define SDF_DITHER     0x01000000      /* can do dithering */
+#define SDF_DEGLITCH   0x02000000      /* can do deglitching */
+#define SDF_MMAP       0x04000000      /* can do mmap() */
+#define SDF_RUNNING    0x08000000      /* subdevice is acquiring data */
+#define SDF_LSAMPL     0x10000000      /* subdevice uses 32-bit samples */
+
+
+/* subdevice types */
+
+#define COMEDI_SUBD_UNUSED              0      /* unused */
+#define COMEDI_SUBD_AI                  1      /* analog input */
+#define COMEDI_SUBD_AO                  2      /* analog output */
+#define COMEDI_SUBD_DI                  3      /* digital input */
+#define COMEDI_SUBD_DO                  4      /* digital output */
+#define COMEDI_SUBD_DIO                 5      /* digital input/output */
+#define COMEDI_SUBD_COUNTER             6      /* counter */
+#define COMEDI_SUBD_TIMER               7      /* timer */
+#define COMEDI_SUBD_MEMORY              8      /* memory, EEPROM, DPRAM */
+#define COMEDI_SUBD_CALIB               9      /* calibration DACs */
+#define COMEDI_SUBD_PROC                10     /* processor, DSP */
+
+
+#define COMEDI_INPUT                   0
+#define COMEDI_OUTPUT                  1
+
+/* ioctls */
+
+#define CIO 'd'
+#define COMEDI_DEVCONFIG _IOW(CIO,0,comedi_devconfig)
+#define COMEDI_DEVINFO _IOR(CIO,1,comedi_devinfo)
+#define COMEDI_SUBDINFO _IOR(CIO,2,comedi_subdinfo)
+#define COMEDI_CHANINFO _IOR(CIO,3,comedi_chaninfo)
+#define COMEDI_TRIG _IOWR(CIO,4,comedi_trig)
+#define COMEDI_LOCK _IO(CIO,5)
+#define COMEDI_UNLOCK _IO(CIO,6)
+#define COMEDI_CANCEL _IO(CIO,7)
+#define COMEDI_RANGEINFO _IOR(CIO,8,comedi_rangeinfo)
+#define COMEDI_CMD _IOR(CIO,9,comedi_cmd)
+
+
+
+/* structures */
+
+typedef struct comedi_trig_struct comedi_trig;
+typedef struct comedi_cmd_struct comedi_cmd;
+typedef struct comedi_chaninfo_struct comedi_chaninfo;
+typedef struct comedi_subdinfo_struct comedi_subdinfo;
+typedef struct comedi_devinfo_struct comedi_devinfo;
+typedef struct comedi_devconfig_struct comedi_devconfig;
+typedef struct comedi_rangeinfo_struct comedi_rangeinfo;
+typedef struct comedi_krange_struct comedi_krange;
+
+struct comedi_trig_struct{
+       unsigned int subdev;            /* subdevice */
+       unsigned int mode;              /* mode */
+       unsigned int flags;
+       unsigned int n_chan;            /* number of channels */
+       unsigned int *chanlist;         /* channel/range list */
+       sampl_t *data;                  /* data list, size depends on subd flags */
+       unsigned int n;                 /* number of scans */
+       unsigned int trigsrc;
+       unsigned int trigvar;
+       unsigned int trigvar1;
+       unsigned int data_len;
+       unsigned int unused[3];
+};
+
+struct comedi_cmd_struct{
+       unsigned int subdev;
+       unsigned int flags;
+
+       unsigned int start_src;
+       unsigned int start_arg;
+
+       unsigned int scan_begin_src;
+       unsigned int scan_begin_arg;
+
+       unsigned int convert_src;
+       unsigned int convert_arg;
+
+       unsigned int scan_end_src;
+       unsigned int scan_end_arg;
+
+       unsigned int stop_src;
+       unsigned int stop_arg;
+
+       unsigned int *chanlist;         /* channel/range list */
+       unsigned int chanlist_len;
+
+       sampl_t *data;                  /* data list, size depends on subd flags */
+       unsigned int data_len;
+};
+
+struct comedi_chaninfo_struct{
+       unsigned int subdev;
+       lsampl_t *maxdata_list;
+       unsigned int *flaglist;
+       unsigned int *rangelist;
+       unsigned int unused[4];
+};
+
+struct comedi_rangeinfo_struct{
+       unsigned int range_type;
+       void *range_ptr;
+};
+
+struct comedi_krange_struct{
+       int min;        /* fixed point, multiply by 1e-6 */
+       int max;        /* fixed point, multiply by 1e-6 */
+       unsigned int flags;
+};
+
+struct comedi_subdinfo_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;          /* channel flags */
+       unsigned int    range_type;     /* lookup in kernel */
+       unsigned int unused[10];
+};
+
+struct comedi_devinfo_struct{
+       unsigned int version_code;
+       unsigned int n_subdevs;
+       char driver_name[COMEDI_NAMELEN];
+       char board_name[COMEDI_NAMELEN];
+       int options[COMEDI_NDEVCONFOPTS];
+};
+
+struct comedi_devconfig_struct{
+       char board_name[COMEDI_NAMELEN];
+       int options[COMEDI_NDEVCONFOPTS];
+};
+
+
+/* range stuff */
+
+#define __RANGE(a,b)   ((((a)&0xffff)<<16)|((b)&0xffff))
+
+#define RANGE_OFFSET(a)                (((a)>>16)&0xffff)
+#define RANGE_LENGTH(b)                ((b)&0xffff)
+
+#define RF_UNIT(flags)         ((flags)&0xff)
+#define RF_EXTERNAL            (1<<8)
+
+#define UNIT_volt              0
+#define UNIT_mA                        1
+#define UNIT_none              2
+
+
+/* Kernel internal stuff.  Needed by RTLinux modules and such. */
+
+#ifdef __KERNEL__
+
+/* callback stuff */
+
+#define COMEDI_CB_EOS          1       /* end of scan */
+#define COMEDI_CB_EOA          2       /* end of acquisition */
+#define COMEDI_CB_BLOCK                4       /* convenient block size */
+#define COMEDI_CB_EOBUF                8       /* end of buffer */
+
+/* exported functions */
+
+int comedi_trig_ioctl(unsigned int minor,unsigned int subdev,comedi_trig *it);
+int __comedi_trig_ioctl(unsigned int minor,unsigned int subdev,comedi_trig *it);
+int comedi_lock_ioctl(unsigned int minor,unsigned int subdev);
+int comedi_unlock_ioctl(unsigned int minor,unsigned int subdev);
+int comedi_cancel_ioctl(unsigned int minor,unsigned int subdev);
+int comedi_register_callback(unsigned int minor,unsigned int subdev,
+               unsigned int mask,int (*cb)(unsigned int,void *),void *arg);
+
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _COMEDI_H */
+
diff --git a/man/comedi.7 b/man/comedi.7
new file mode 100644 (file)
index 0000000..9be638b
--- /dev/null
@@ -0,0 +1,59 @@
+.TH comedi 7 ""
+.SH NAME
+\fBcomedi\fR - the Linux Control and Measurement Device Interface
+.SH SYNOPSIS
+\fB#include <comedi.h>\fR
+.br
+.SH DESCRIPTION
+The \fBcomedi\fR package is a collection of hardware drivers and
+library routines that can be used to communicate with a variety of
+data acquisition boards, including A/D converters, D/A converters,
+digital I/O and timers.
+
+The hardware drivers that are installed on your system may include
+one or more of the following:
+
+       \fB8255\fR - Generic 8255 support
+.br
+       \fBdas08\fR - Keithley DAS08 and compatibles
+.br
+       \fBdt2811\fR - Data Translation DT2811
+.br
+       \fBdt2814\fR - Data Translation DT2814
+.br
+       \fBdt2817\fR - Data Translation DT2817
+.br
+       \fBdt282x\fR - Data Translation DT2821 series
+.br
+       \fBni_E\fR - National Instruments AT-MIO E series
+.br
+       \fBni_E\fR - National Instruments AT-MIO (Am9513 based)
+.br
+       \fBparport\fR - Standard PC parallel port digital I/O
+.br
+       \fBpcl711\fR - PC-LabCard PCL-711, 711B
+.br
+       \fBpcl711\fR - PC-LabCard PCL-725
+.br
+       \fBpcl711\fR - PC-LabCard PCL-726
+.br
+       \fBrti800\fR - Analog Devices RTI-800/815
+
+.SH OTHER DOCUMENTATION
+
+The following man pages may be useful:
+
+\fBcomedi_config\fR(8), 
+
+Additional text documentation is located in /usr/doc/comedi-0.5.0.
+
+.SH VERSION
+
+0.5.0
+
+The current version can be found at ftp://stm.lbl.gov/pub/comedi.
+
+.SH AUTHOR
+
+David Schleef, <ds@stm.lbl.gov>
+
diff --git a/man/comedi_config.8 b/man/comedi_config.8
new file mode 100644 (file)
index 0000000..9296475
--- /dev/null
@@ -0,0 +1,177 @@
+.TH comedi_config 8 ""
+.SH NAME
+\fBcomedi_config\fR - COMEDI configuration utility
+.SH SYNOPSIS
+\fB/usr/sbin/comedi_config\fR [-vVq] /dev/comediN <driver>
+<option1>[,<option2>...]
+.br
+.SH DESCRIPTION
+\fBcomedi_config\fR is a utility designed to configure
+control and measurement hardware associated with a particular
+\fBcomedi\fR device.  You must have the \fBcomedi\fR module installed
+in the kernel to use this utility.
+
+Each control and measurement card in your computer is associated
+with an individual comedi device; to a user, these appear as the
+files \fB/dev/comedi\fRN, with N being 0,1,2,3,etc.  To configure
+\fBcomedi\fR to use a particular hardware driver for your card,
+you would use this utility with the device file, driver name, and
+other vital parameters (I/O base address, IRQ, etc.) on the
+command line.
+
+As an example, the command line used to tell the comedi module that
+you want to access a National Instruments AT-MIO E series board as
+\fB/dev/comedi0\fR would be:
+
+       /usr/sbin/comedi_config /dev/comedi0 atmio-E 0x220,3
+
+This tells the driver that the board is configured
+for I/O base 0x220 and IRQ 3.  In general, I/O base is listed first
+and IRQ, if necessary, is listed second.  Additional parameters
+vary, so see the information below for the particular driver.
+
+Parameters can be expressed in either decimal or hexadecimal, with
+a 0x prefix.
+
+
+.SH OPTIONS
+
+\fBcomedi_config\fR recognizes the following options:
+
+\fB-a\fR       obtain configuration information from the file
+/fB/etc/comedi.conf/fR.
+
+\fB-q\fR       don't print output while running.
+
+\fB-v\fR       print verbose output while running.
+
+\fB-V\fR       print version number and exit.
+
+.SH HARDWARE DRIVERS
+
+Additional information pertaining to each hardware driver is
+available in \fB/usr/doc/comedi-0.5.0\fR.  Unless noted,
+the names below are the same as the driver names given on
+the command line.
+
+\fB8255\fR     Generic 8255 support
+.br
+       <I/O base>
+
+\fBdas08\fR - Keithley DAS08 and compatibles
+.br
+       <I/O base>
+
+\fBdt2811\fR - Data Translation DT2811
+.br
+       <I/O base>
+.br
+       Interrupts are not used with this board.
+
+\fBdt2814\fR - Data Translation DT2814
+.br
+       <I/O base>,<IRQ>
+.br
+       Set IRQ to -1 to probe for IRQ.
+
+\fBdt2817\fR - Data Translation DT2817
+.br
+       <I/O base>
+
+\fBdt282x\fR - Data Translation DT2821 series
+.br
+       <I/O base>,<IRQ>,<DMA 1>,<DMA 2>,
+<analog reference>,
+<encoding, analog input>,
+<encoding, analog output 0>,
+<encoding, analog output 1>,
+<voltage range, analog input>,
+<voltage range, analog output 0>,
+<voltage range, analog output 1>.
+.br
+       Valid values for analog reference are 0=single ended,
+1=differential.
+Valid values for encoding are
+[0=straight or offset binary, 1=two's complement].
+Valid values for voltage range are
+0=(-10,10), 1=(0,10), 2=(-5,+5), 3=(0,5),
+4=(-2.5,2.5).
+Options must agree with how your board is
+configured.
+.br
+       The driver recognizes the following names:
+\fBdt2821\fR,
+\fBdt2823\fR,
+\fBdt2824-pgh\fR,
+\fBdt2824-pgl\fR,
+\fBdt2825\fR,
+\fBdt2827\fR,
+\fBdt2828\fR.
+Use the name that best represents your board.
+
+\fBatmio-E\fR - National Instruments AT-MIO E series
+.br
+       <I/O base>,<IRQ>
+.br
+       This driver automatically detects which board is installed
+in your computer.  The E-series boards are plug-and-play, so use
+\fBisapnptools\fR to tell the board which I/O port to use before
+running \fBcomedi_config\fR.  In a random fit of stupidity, the
+E-series boards ignore the IRQ which PnP assigns to it.
+
+\fBparport\fR - Standard PC parallel port digital I/O
+.br
+       <I/O base>
+.br
+       This driver does not work with the new parallel port sharing
+capabilities of Linux.
+
+\fBpcl711\fR - PC-LabCard PCL-711, 711B, ACL-8112
+.br
+       <I/O base>
+.br
+       Use the driver name \fBpcl711b\fR to configure for a 711B board.
+
+\fBpcl725\fR - PC-LabCard PCL-725
+.br
+       <I/O base>
+.br
+
+\fBpcl726\fR - PC-LabCard PCL-726
+.br
+       <I/O base>
+.br
+
+\fBrti800\fR - Analog Devices RTI-800/815
+.br
+       <I/O base>
+
+.SH CONFIGURATION FILE
+
+[This section has not been implemented yet.]
+
+A list of device configurations can be put into the file
+\fB/etc/comedi.conf\fR.  This file takes the form
+
+       <device> <driver> <param1>,<param2>,...
+
+These configurations will be read and performed when the
+switch \fB-a\fR is used.  This is potentially useful when
+run from an initialization script.
+
+.SH OTHER DOCUMENTATION
+
+\fBcomedi\fR(7), 
+
+Additional text documentation is located in /usr/doc/comedi-0.5.0.
+
+.SH VERSION
+
+0.5.0
+
+The current version can be obtained from ftp://stm.lbl.gov/pub/comedi.
+
+.SH AUTHOR
+
+David Schleef, <ds@stm.lbl.gov>
+
diff --git a/scripts/Configure b/scripts/Configure
new file mode 100755 (executable)
index 0000000..c1dae68
--- /dev/null
@@ -0,0 +1,478 @@
+#!/bin/sh
+#
+# This script was hacked from the one used to configure the linux kernel.
+#
+
+#
+# check running kernel vs. /usr/src/linux and warn if necessary
+#
+read dummy dummy dummy2 <$LINUXDIR/include/linux/version.h
+UTS_VERSION=`echo $dummy2|sed 's/"//g'`
+
+echo UTS_VERSION=$UTS_VERSION >.uts_version
+
+. $LINUXDIR/.config
+
+if [ "$CONFIG_MODULES" = "n" ]
+then
+       cat <<EOF
+ *****
+ *****    WARNING!!!
+ *****
+ *****    Your kernel is configured to not allow loadable modules.
+ *****    You are attempting to compile a loadable module for this
+ *****    kernel.  This is a problem.  Please correct it.
+ *****
+EOF
+exit
+fi
+
+if [ "$(uname -r)" != "$UTS_VERSION" ]
+then
+       cat <<EOF
+ *****
+ *****    WARNING!!!
+ *****
+ *****    The kernel that is currently running is a different
+ *****    version than the source in /usr/src/linux.  The current
+ *****    compile will create a module that is *incompatible*
+ *****    with the running kernel.
+ *****
+EOF
+fi
+
+#
+# Make sure we're really running bash.
+#
+# I would really have preferred to write this script in a language with
+# better string handling, but alas, bash is the only scripting language
+# that I can be reasonable sure everybody has on their linux machine.
+#
+[ -z "$BASH" ] && { echo "Configure requires bash" 1>&2; exit 1; }
+
+# Disable filename globbing once and for all.
+# Enable function cacheing.
+set -f -h
+
+#
+# Dummy functions for use with a config.in modified for menuconf
+#
+function mainmenu_option () {
+       :
+}
+function mainmenu_name () {
+       :
+}
+function endmenu () {
+       :
+}
+
+#
+# help prints the corresponding help text from Configure.help to stdout
+#
+#       help variable
+#
+function help () {
+  if [ -f scripts/Configure.help ]
+  then
+     #first escape regexp special characters in the argument:
+     var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
+     #now pick out the right help text:
+     text=$(sed -n "/^$var[    ]*\$/,\${
+                       /^$var[         ]*\$/b
+                       /^#.*/b
+                       /^[     ]*\$/q
+                       p
+                   }" scripts/Configure.help)
+     if [ -z "$text" ]
+     then
+         echo; echo "  Sorry, no help available for this option yet.";echo
+     else
+         (echo; echo "$text"; echo) | ${PAGER:-more}
+     fi
+  else
+     echo;
+     echo "  Can't access the file scripts/Configure.help which"
+     echo "  should contain the help texts."
+     echo
+  fi
+}
+
+
+#
+# readln reads a line into $ans.
+#
+#      readln prompt default oldval
+#
+function readln () {
+       if [ "$DEFAULT" = "-d" -a -n "$3" ]; then
+               echo "$1"
+               ans=$2
+       else
+               echo -n "$1"
+               [ -z "$3" ] && echo -n "(NEW) "
+               IFS='@' read ans </dev/tty || exit 1
+               [ -z "$ans" ] && ans=$2
+       fi
+}
+
+#
+# comment does some pretty-printing
+#
+#      comment 'xxx'
+# 
+function comment () {
+       echo "*"; echo "* $1" ; echo "*"
+       (echo "" ; echo "#"; echo "# $1" ; echo "#") >>$CONFIG
+       (echo "" ; echo "/*"; echo " * $1" ; echo " */") >>$CONFIG_H
+}
+
+#
+# define_bool sets the value of a boolean argument
+#
+#      define_bool define value
+#
+function define_bool () {
+       case "$2" in
+        "y")
+               echo "$1=y" >>$CONFIG
+               echo "#define $1 1" >>$CONFIG_H
+               ;;
+
+        "m")
+               echo "$1=m" >>$CONFIG
+               echo "#undef  $1" >>$CONFIG_H
+               echo "#define $1_MODULE 1" >>$CONFIG_H
+               ;;
+
+        "n")
+               echo "# $1 is not set" >>$CONFIG
+               echo "#undef  $1" >>$CONFIG_H
+               ;;
+       esac
+       eval "$1=$2"
+}
+
+#
+# bool processes a boolean argument
+#
+#      bool question define
+#
+function bool () {
+       old=$(eval echo "\${$2}")
+       def=${old:-'n'}
+       case "$def" in
+        "y" | "m") defprompt="Y/n/?"
+             def="y"
+             ;;
+        "n") defprompt="N/y/?"
+             ;;
+       esac
+       while :; do
+         readln "$1 ($2) [$defprompt] " "$def" "$old"
+         case "$ans" in
+           [yY] | [yY]es ) define_bool "$2" "y"
+                           break;;
+           [nN] | [nN]o )  define_bool "$2" "n"
+                           break;;
+           * )             help "$2"
+                           ;;
+         esac
+       done
+}
+
+#
+# tristate processes a tristate argument
+#
+#      tristate question define
+#
+function tristate () {
+       if [ "$CONFIG_MODULES" != "y" ]; then
+         bool "$1" "$2"
+       else 
+         old=$(eval echo "\${$2}")
+         def=${old:-'n'}
+         case "$def" in
+          "y") defprompt="Y/m/n/?"
+               ;;
+          "m") defprompt="M/n/y/?"
+               ;;
+          "n") defprompt="N/y/m/?"
+               ;;
+         esac
+         while :; do
+           readln "$1 ($2) [$defprompt] " "$def" "$old"
+           case "$ans" in
+             [yY] | [yY]es ) define_bool "$2" "y"
+                             break ;;
+             [nN] | [nN]o )  define_bool "$2" "n"
+                             break ;;
+             [mM] )          define_bool "$2" "m"
+                             break ;;
+             * )             help "$2"
+                             ;;
+           esac
+         done
+       fi
+}
+
+#
+# dep_tristate processes a tristate argument that depends upon
+# another option.  If the option we depend upon is a module,
+# then the only allowable options are M or N.  If Y, then
+# this is a normal tristate.  This is used in cases where modules
+# are nested, and one module requires the presence of something
+# else in the kernel.
+#
+#      tristate question define default
+#
+function dep_tristate () {
+       old=$(eval echo "\${$2}")
+       def=${old:-'n'}
+       if [ "$3" = "n" ]; then
+               define_bool "$2" "n"
+       elif [ "$3" = "y" ]; then
+               tristate "$1" "$2"
+       else
+          if [ "$CONFIG_MODULES" = "y" ]; then
+               case "$def" in
+                "y" | "m") defprompt="M/n/?"
+                     def="m"
+                     ;;
+                "n") defprompt="N/m/?"
+                     ;;
+               esac
+               while :; do
+                 readln "$1 ($2) [$defprompt] " "$def" "$old"
+                 case "$ans" in
+                     [nN] | [nN]o )  define_bool "$2" "n"
+                                     break ;;
+                     [mM] )          define_bool "$2" "m"
+                                     break ;;
+                     [yY] | [yY]es ) echo 
+   echo "  This answer is not allowed, because it is not consistent with"
+   echo "  your other choices."
+   echo "  This driver depends on another one which you chose to compile"
+   echo "  as a module. This means that you can either compile this one"
+   echo "  as a module as well (with M) or leave it out altogether (N)."
+                                     echo
+                                     ;;
+                     * )             help "$2"
+                                     ;;
+                 esac
+               done
+          fi
+       fi
+}
+
+#
+# define_int sets the value of a integer argument
+#
+#      define_int define value
+#
+function define_int () {
+       echo "$1=$2" >>$CONFIG
+       echo "#define $1 ($2)" >>$CONFIG_H
+       eval "$1=$2"
+}
+
+#
+# int processes an integer argument
+#
+#      int question define default
+# GNU expr changed handling of ?.  In older versions you need ?,
+# in newer you need \?
+OLD_EXPR=`expr "0" : '0\?'`
+if [ $OLD_EXPR -eq 1 ]; then
+    INT_EXPR='0$\|-\?[1-9][0-9]*$'
+else
+    INT_EXPR='0$\|-?[1-9][0-9]*$'
+fi
+function int () {
+       old=$(eval echo "\${$2}")
+       def=${old:-$3}
+       while :; do
+         readln "$1 ($2) [$def] " "$def" "$old"
+         if expr "$ans" : $INT_EXPR > /dev/null; then
+           define_int "$2" "$ans"
+           break
+         else
+           help "$2"
+         fi
+       done
+}
+#
+# define_hex sets the value of a hexadecimal argument
+#
+#      define_hex define value
+#
+function define_hex () {
+       echo "$1=$2" >>$CONFIG
+       echo "#define $1 0x${2#*[x,X]}" >>$CONFIG_H
+       eval "$1=$2"
+}
+
+#
+# hex processes an hexadecimal argument
+#
+#      hex question define default
+#
+function hex () {
+       old=$(eval echo "\${$2}")
+       def=${old:-$3}
+       def=${def#*[x,X]}
+       while :; do
+         readln "$1 ($2) [$def] " "$def" "$old"
+         ans=${ans#*[x,X]}
+         if expr "$ans" : '[0-9a-fA-F][0-9a-fA-F]*$' > /dev/null; then
+           define_hex "$2" "$ans"
+           break
+         else
+           help "$2"
+         fi
+       done
+}
+
+#
+# choice processes a choice list (1-out-of-n)
+#
+#      choice question choice-list default
+#
+# The choice list has a syntax of:
+#      NAME WHITESPACE VALUE { WHITESPACE NAME WHITESPACE VALUE }
+# The user may enter any unique prefix of one of the NAMEs and
+# choice will define VALUE as if it were a boolean option.
+# VALUE must be in all uppercase.  Normally, VALUE is of the
+# form CONFIG_<something>.  Thus, if the user selects <something>,
+# the CPP symbol CONFIG_<something> will be defined and the
+# shell variable CONFIG_<something> will be set to "y".
+#
+function choice () {
+       question="$1"
+       choices="$2"
+       old=
+       def=$3
+
+       # determine default answer:
+       names=""
+       set -- $choices
+       firstvar=$2
+       while [ -n "$2" ]; do
+               if [ -n "$names" ]; then
+                       names="$names, $1"
+               else
+                       names="$1"
+               fi
+               if [ "$(eval echo \"\${$2}\")" = "y" ]; then
+                       old=$1
+                       def=$1
+               fi
+               shift; shift
+       done
+
+       val=""
+       while [ -z "$val" ]; do
+               ambg=n
+               readln "$question ($names) [$def] " "$def" "$old"
+               ans=$(echo $ans | tr a-z A-Z)
+               set -- $choices
+               while [ -n "$1" ]; do
+                       name=$(echo $1 | tr a-z A-Z)
+                       case "$name" in
+                               "$ans"* )
+                                       if [ "$name" = "$ans" ]; then
+                                               val="$2"
+                                               break   # stop on exact match
+                                       fi
+                                       if [ -n "$val" ]; then
+                                               echo;echo \
+               "  Sorry, \"$ans\" is ambiguous; please enter a longer string."
+                                               echo
+                                               val=""
+                                               ambg=y
+                                               break
+                                       else
+                                               val="$2"
+                                       fi;;
+                       esac
+                       shift; shift
+               done
+               if [ "$val" = "" -a "$ambg" = "n" ]; then
+                       help "$firstvar"
+               fi
+       done
+       set -- $choices
+       while [ -n "$2" ]; do
+               if [ "$2" = "$val" ]; then
+                       echo "  defined $val"
+                       define_bool "$2" "y"
+               else
+                       define_bool "$2" "n"
+               fi
+               shift; shift
+       done
+}
+
+CONFIG=.tmpconfig
+CONFIG_H=.tmpconfig.h
+trap "rm -f $CONFIG $CONFIG_H ; exit 1" 1 2
+
+#
+# Make sure we start out with a clean slate.
+#
+echo "#" > $CONFIG
+echo "# Automatically generated make config: don't edit" >> $CONFIG
+echo "#" >> $CONFIG
+
+echo "/*" > $CONFIG_H
+echo " * Automatically generated C config: don't edit" >> $CONFIG_H
+echo " */" >> $CONFIG_H
+
+echo '#define COMEDI_VERSION "'$VERS1.$VERS2.$VERS3\" >> $CONFIG_H
+echo '#define COMEDI_VERSION_CODE ('0x1000*$VERS1+0x100*$VERS2+$VERS3')' >> $CONFIG_H
+
+DEFAULT=""
+if [ "$1" = "-d" ] ; then
+       DEFAULT="-d"
+       shift
+fi
+
+CONFIG_IN=./scripts/config.in
+if [ "$1" != "" ] ; then
+       CONFIG_IN=$1
+fi
+
+DEFAULTS=./scripts/config.dist
+if [ -f .config ]; then
+  DEFAULTS=.config
+fi
+
+if [ -f $DEFAULTS ]; then
+  echo "#"
+  echo "# Using defaults found in" $DEFAULTS
+  echo "#"
+  . $DEFAULTS
+  sed -e 's/# \(.*\) is not.*/\1=n/' < $DEFAULTS > /tmp/conf.$$
+  . /tmp/conf.$$
+  rm /tmp/conf.$$
+else
+  echo "#"
+  echo "# No defaults found"
+  echo "#"
+fi
+
+. $CONFIG_IN
+
+rm -f .config.old
+if [ -f .config ]; then
+       mv .config .config.old
+fi
+mv .tmpconfig .config
+mv .tmpconfig.h include/config.h
+
+echo 
+echo "Makefiles for comedi should now be configured.  Run 'make' to compile,"
+echo "and then 'make install' to install."
+echo 
+
+exit 0
diff --git a/scripts/Configure.help b/scripts/Configure.help
new file mode 100644 (file)
index 0000000..de46677
--- /dev/null
@@ -0,0 +1,117 @@
+
+
+Prompt for development and/or incomplete code/drivers
+CONFIG_EXPERIMENTAL
+  Some of the various things that Linux supports (such as network 
+  drivers, filesystems, network protocols, etc.) can be in a state 
+  of development where the functionality, stability, or the level of 
+  testing is not yet high enough for general use. This is usually
+  known as the "alpha-test" phase amongst developers. If a feature is
+  currently in alpha-test, then the developers usually discourage 
+  uninformed widespread use of this feature by the general public to
+  avoid "Why doesn't this work?" type mail messages. However, active
+
+Exported functions
+CONFIG_EXPORT
+  If you want to use comedi from a kernel module, select yes.
+  RTLinux tasks and comedi value-added devices are both kernel
+  modules, so select yes if you want to use either of these.
+
+Real Time Support
+CONFIG_COMEDI_RT
+  This selection requires that you have an RTLinux kernel, and will
+  not compile without the correct header files.  If this option is
+  selected, all the exported functions are RT-safe, i.e., they can
+  be called from an RTLinux task or interrupt.  
+
+Verbose Debugging
+CONFIG_DEBUG
+  Selecting yes here will cause comedi to print error messages to
+  syslog at every opportunity.  This can be useful when you are
+  writing programs that use comedi.
+
+Data Translation DT2811 driver
+CONFIG_DT2811
+  Includes support for DT2811.
+
+Data Translation DT2814 driver
+CONFIG_DT2814
+  Includes support for DT2814.
+
+Data Translation DT2815 driver
+CONFIG_DT2815
+  Includes support for DT2815.
+
+Data Translation DT2817 driver
+CONFIG_DT2817
+  Includes support for DT2817.
+
+Data Translation DT2821 series driver
+CONFIG_DT282x
+  Includes support for DT2821 series hardware.
+
+National Instruments AT-MIO E series driver
+CONFIG_ATMIO_E
+  Includes support for National Instruments AT-MIO E series hardware.
+  This driver should also work with DAQcard-MIO E series PCMCIA
+  hardware.  If you have an E series board with an 8255 chip, you
+  also need to select the 8255 driver below.  To use this driver,
+  you will also need the isapnptools package, which is included
+  with many current Linux distributions.
+  
+  Support for the PCI-MIO E series is in a separate driver -- you
+  should select N here.
+
+National Instruments PCI-MIO E series driver
+CONFIG_PCIMIO_E
+  Includes support for National Instruments PCI-MIO E series hardware.
+  If you have an E series board with an 8255 chip, you also need to
+  select the 8255 driver below.
+
+Keithley Metrabyte DAS08 driver
+CONFIG_DAS08
+  Includes support for Keithley Metrabye DAS08 and compatibles.
+
+Support for 8255
+CONFIG_8255
+  Includes support for 8255 chip.  This is necessary for boards
+  that use an 8255 for digital I/O.
+
+Support for comedi controlling a standard PC parallel port
+CONFIG_COMEDI_PARPORT
+  If you want to use your PC's parallel port for additional
+  digital I/O channels, select Y here.  This driver is not activated
+  automatically, so you don't have to fear that comedi will
+  cause your printer to stop working.  This driver is not
+  compatible with the generic Linux parallel port interface.
+
+Analog Devices RI800 driver
+CONFIG_RTI800
+  Includes support for RTI800 and RTI815 boards.
+
+Analog Devices RI802 driver
+CONFIG_RTI802
+  Includes support for RTI802.
+
+PCL 711 driver
+CONFIG_PCL711
+  Includes support for PCL 711, 711b and ACL 8112.
+
+PCL 725 driver
+CONFIG_PCL725
+  Includes support for PCL 725.
+
+PCL 726 driver
+CONFIG_PCL726
+  Includes support for PCL 726.
+
+Keithley Metrabyte DAS1600 driver
+CONFIG_DAS1600
+  Includes support for Keithley Metrabyte DAS1600, DAS1608 and
+  compatibles.  To use the digital I/O provided by the 8255 chip
+  on these boards, you need to include the 8255 driver above.
+
+Quanser Consulting MultiQ-3 driver
+CONFIG_MULTIQ3
+  Includes support for Quanser Consulting MultiQ-3 boards.
+
diff --git a/scripts/check_kernel b/scripts/check_kernel
new file mode 100755 (executable)
index 0000000..72ed648
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+
+
+if [ $(grep -c "$(uname -r)" /usr/include/linux/version.h) != 0 ]
+then
+       echo <<EOF
+ *****
+ *****    WARNING!!!
+ *****
+ *****    The kernel that is currently running is a different
+ *****    version than the source in /usr/src/linux.  The current
+ *****    compile will create a module that is *incompatible*
+ *****    with the running kernel.
+ *****
+EOF
+fi
+
diff --git a/scripts/config.dist b/scripts/config.dist
new file mode 100644 (file)
index 0000000..1d4e3ed
--- /dev/null
@@ -0,0 +1,45 @@
+#
+# Automatically generated make config: don't edit
+#
+
+#
+# Features
+#
+CONFIG_EXPORT=y
+# CONFIG_COMEDI_RT is not set
+CONFIG_DEBUG=y
+
+#
+# Hardware device drivers
+#
+CONFIG_DT=y
+# CONFIG_DT2801 is not set
+# CONFIG_DT2811 is not set
+# CONFIG_DT2814 is not set
+# CONFIG_DT2815 is not set
+# CONFIG_DT2817 is not set
+# CONFIG_DT282x is not set
+CONFIG_NI=y
+# CONFIG_ATMIO_E is not set
+# CONFIG_PCIMIO_E is not set
+# CONFIG_NI_PCIDIO is not set
+# CONFIG_DAS08 is not set
+# CONFIG_DAS08JR is not set
+# CONFIG_DAS1600 is not set
+# CONFIG_8255 is not set
+# CONFIG_COMEDI_PARPORT is not set
+# CONFIG_RTI800 is not set
+# CONFIG_RTI802 is not set
+# CONFIG_PCL711 is not set
+# CONFIG_PCL725 is not set
+# CONFIG_PCL726 is not set
+# CONFIG_MULTIQ3 is not set
+
+#
+# Experimental extras (might not compile and/or work)
+#
+# CONFIG_ATMIO16F is not set
+# CONFIG_DAS6402 is not set
+# CONFIG_RTI860 is not set
+# CONFIG_DT3000 is not set
+# CONFIG_DAS16 is not set
diff --git a/scripts/config.h.dist b/scripts/config.h.dist
new file mode 100644 (file)
index 0000000..b62cc73
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Automatically generated C config: don't edit
+ */
+
+/*
+ * Installation directory
+ */
+#undef  CONFIG_USRLOCAL
+
+/*
+ * Hardware device drivers
+ */
+#undef  CONFIG_DT
+#define CONFIG_NI 1
+
+/*
+ * Please read the note about sending an EEPROM dump in doc/ni_E
+ */
+#define CONFIG_ATMIO_E 1
+#define CONFIG_PCIMIO_E 1
+#undef  CONFIG_DAS08
+#undef  CONFIG_8255
+#undef  CONFIG_COMEDI_PARPORT
+#undef  CONFIG_RTI800
+#undef  CONFIG_RTI802
+#undef  CONFIG_PCL711
+#undef  CONFIG_PCL725
+#undef  CONFIG_PCL726
+
+/*
+ * Experimental extras (might not compile and/or work)
+ */
diff --git a/scripts/config.in b/scripts/config.in
new file mode 100644 (file)
index 0000000..5a658e3
--- /dev/null
@@ -0,0 +1,3 @@
+
+source comedi/Config.in
+
diff --git a/scripts/pathdown.sh b/scripts/pathdown.sh
new file mode 100644 (file)
index 0000000..16c17b2
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/sh
+UP=
+DN=${PWD:?}
+TP=${TOPDIR:?}
+
+while [ ! $TP/$UP/. -ef $DN ] ;do
+       UP=`basename $PWD`/$UP
+       cd ..
+       if [ "$PWD" = "/" ]; then echo "Lost"; exit 1; fi
+done
+
+echo $UP
+exit 0