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, . 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 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