This is a Linux kernel driver for Keithley Data Acquisition DAS-1200
boards, version 1, 25 July 1995. This is a kernel module which,
when installed, will allow you to read A/D samples, read and write
values to the digital I/O pins, set the MUX scan register, and 
read the two STATUS registers on the DAS-1200 board. It's maintained
by Matt Welsh, <mdw@cs.cornell.edu>.

Please consult the DAS1200 User Guide (especially Chapter 9, Register
Level I/O Maps) for all details. Use of this driver assumes that you
are somewhat familiar with the architecture of this board.

The driver supports software triggering only (that is, a write
to the DAS1200_AD0 register to trigger a single or burst acquisition).
Future revisions will support external triggers (asynchronous with
respect to an actual read() operation) as well as triggering from
the on-board timers.

I have only tested this in a single-ended configuration; I assume
that it will be fine with respect to differential inputs. Also, I
do not believe that this driver is compatible with the EXP-16 expansion
mux/amp available for the board. Little programming, if any, will be
required to support this.

This driver is a stand-alone loadable kernel module; no patches to
the kernel proper are required. DMA space is allocated with 
kmalloc(GFP_DMA).  It should work with any kernel 1.0 or later; 
I am testing under 1.2.11.

See `dastest.c' and `dasgraph.c' for some simple example programs
which use this driver.

INSTALLATION:
	1) Unpack the sources.

	2) Edit das1200.h and modify the following lines, if necessary:

/* Define the following for your system */
#define DAS1200_MAJOR 31                /* Device major number */
#define DAS1200_BASE 0x300              /* I/O base address of card */
#define DAS1200_IRQ 5                   /* IRQ of card */
#define DAS1200_DMA 3                   /* DMA channel of card */

	   I use major number 31 on my system; change this to another
	   (unused) device number if 31 is in use.

	   The other three parameters (base addr, IRQ, and DMA channel)
	   are configurable by setting switches on the board itself. 
	   Just make sure that das1200.h corresponds to your hardware
	   configuration.

	3) Create the DAS1200 device, /dev/das1200, by issuing the
	   command
	   	mknod /dev/das1200 c <major> 0
	   as root. In the case of major number 31, you would use:
	   	mknod /dev/das1200 c 31

	4) Issue 'make' to build the module, das1200.o, and the
	   example programs dastest, dasgraph, and dastime.

	5) As root, issue 'insmod das1200.o' to load the module
	   into the running kernel. Use 'rmmod das1200' to unload
	   the module.

	If you ever rebuild your kernel you'll need to rebuild
	das1200.o; otherwise, insmod will complain about mismatched
	versions. I have not yet tested the methods provided by the
	module utilities to prevent this.

USE: 
The programming interface is very simple. The only defined functions are:

	* read() on /dev/das1200 will return A/D samples, two bytes per 
	  sample. (A read of an odd number of bytes will only return 
	  (count - 1)/2 samples.) The samples are returned in the format
	  described in the DAS1200 user guide, that is:

          bit:   15                4 3      0 
                +-------------------+--------+
		|       sample      |  chan  | 
                +-------------------+--------+

	  That is, 12 bits containing the sample and 4 bits containing
	  the channel number. Each subsequent sample will read from the
	  next channel as defined by the MUX scan register (see below).
	  A simple conversion (described in the DAS1200 guide and
	  'dasgraph.c') can be used to determine voltage (single-ended
	  or differential) from the sample. 

	* ioctl( fd, DAS1200_IO_SETCHAN, struct das1200_chan * )
	  Set the MUX scan register, that is, the start and end
	  channels for A/D conversion. Struct das1200_chan is 
	  defined in das1200.h as:

            struct das1200_chan {
              int das_schan;   /* Start channel for conversion */
              int das_echan;   /* End channel for conversion */
            };

	  Channel numbers range from 0..15, so only the lower 4 bits
	  of each value are significant. When the module is loaded,
	  the MUX scan register is set to 0 for the start channel,
	  15 for the end channel. These values are only reset on 
	  module load. 

	* ioctl( fd, DAS1200_IO_GETCHAN, struct das1200_chan * )
	  Read the contents of the MUX scan register into the 
	  given structure. 

	* ioctl( fd, DAS1200_IO_SETDIO, unsigned char )
	  Set the values of the digital output pins on the DAS1200.
	  Only the lower four bits are significant.

	* ioctl( fd, DAS1200_IO_GETDIO, unsigned char * )
          Read the values of the digital input puts on the DAS1200
          into the lower four bits of the given unsigned char.

	* ioctl( fd, DAS1200_IO_STATUSA, unsigned char * )
	  Read the value of the DAS1200 STATUSA register into the
	  given unsigned char. The format of this register is
	  defined in the DAS1200 User Guide.

	* ioctl( fd, DAS1200_IO_STATUSB, unsigned char * )
	  Read the value of the DAS1200 STATUSB register into the
	  given unsigned char. The format of this register is
	  defined in the DAS1200 User Guide.

The program 'dastest.c' is an example of most of the operations
you can perform with the driver; it's all self-explanatory, I hope.
Also check out das1200.h for definitions and prototypes. 'dasgraph.c'
reads 16 samples at a time from the board, picks the first, converts to
volts, and prints the results to stdout. You can, for example, pipe
the output of this and display using 'gnuplot' to observe a single
channel on the board (but not in realtime, alas).


NOTES:
The I/O method used for reading samples into memory is defined in das1200.c. 
USE_INTERRUPTS uses interrupt-based I/O (read one sample at a time, interrupt 
after each); USE_POLLING polls the board's STATUSA register after each 
sample; USE_DMA uses 16-sample bursts which are DMA'd to main memory. 
USE_DMA is defined by default; edit das1200.c if you want to change this. 

You can use the 'dastime' program to time reads from the board.  Keep in 
mind that this does not gauge 'raw' performance, as system call, copying, 
and other O/S overheads must be taken into account. Not surprisingly, DMA 
provides the fastest transfer speeds, but have greater variability due 
to system activity; USE_POLLING is only slightly slower than DMA, but 
more stable; and USE_INTERRUPTS is slower and more prone to variance. 

I am sure that there are many good ways to optimize this driver. I have
seen very good performance so far, however, and I'm not aiming for
perfection...

There are a few lingering bugs. First, when using DMA samples are always
read in 16-sample bursts. If you are not reading samples in a multiple
of 16, the remainder will be thrown away. This means that the channel 
numbers as you read may "wrap" unexpectedly; if this is undesirable
behavior I'll change it in the next release.

Also, there seems to be a race somewhere which causes the driver to 
sleep but never get an interrupt after a DMA transfer. I can only
get this to happen under heavy system load. I'd be happy if someone
could take a look at my sleep/wakeup and interrupt code to see if
they can find the problem. I use interruptible sleeps in the driver,
so you can always kill the program, or set a timeout on read()s from
the driver. Interrupt-driven and polling I/O seems to be very stable,
however---if you have problems try using one of those.

You can enable some verbose printk()'s in the driver by defining
DAS1200_DEBUG at the top of das1200.c.

I have been working on some graphical user tools to display samples
read from the board, but haven't had time to finish them up...
perhaps one day they'll be included in the driver.

I don't have the hardware with me to test all of the functions. 
If you see bugs just let me know, or send patches. I hope that you
find this useful!

Cheers--
M. Welsh, mdw@cs.cornell.edu
