--- /dev/null
+
+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
+
--- /dev/null
+
+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.
+
+
--- /dev/null
+
+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.
+
+
+
+
--- /dev/null
+
+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.
+
--- /dev/null
+
+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
+
+
+
+
--- /dev/null
+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.
+
--- /dev/null
+
+These are some notes probably only decipherable by me
+
--- /dev/null
+
+
+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
+
--- /dev/null
+
+
+/* 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
+
+
--- /dev/null
+
+*_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.
+
--- /dev/null
+
+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.
+
--- /dev/null
+
+# 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:
--- /dev/null
+
+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.
+
+
+
--- /dev/null
+
+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.
+
--- /dev/null
+#
+# 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
--- /dev/null
+
+das6402 needs cleanup
+
+check each driver init for stuff
+
+grep XXX
+
+register_symtab
+
+allocate subdevice
+allocate private
+
+fix PCI code, use pci_find_device()
+
--- /dev/null
+
+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
+
+
--- /dev/null
+
+
+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
+
+
--- /dev/null
+/*
+ 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
+
--- /dev/null
+/*
+ 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
+
+
+
--- /dev/null
+/*
+ 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 */
+
+
+
+
--- /dev/null
+/*
+ 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
+}
+
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
+
--- /dev/null
+
+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
+
+
--- /dev/null
+/*
+
+ 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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;
+}
+
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
+
--- /dev/null
+/*
+ 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
+
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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);
+ }
+}
+
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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 */
+
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+
+ALL_SUB_DIRS :=
+MOD_SUB_DIRS :=
+SUB_DIRS :=
+MOD_LIST_NAME := MISC_MODULES
+
+EXTRA_CFLAGS := -I ../
+
+M_OBJS := kcomedilib.o
+
+
+include $(TOPDIR)/Rules.make
+
--- /dev/null
+/*
+ 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;
+}
+
+
--- /dev/null
+/*
+ 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 */
+
+
+
+
--- /dev/null
+#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;
+}
--- /dev/null
+/*******************************/
+/* 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
--- /dev/null
+
+
+#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;
+}
+
--- /dev/null
+/*
+ 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
+
--- /dev/null
+/*
+ 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
+
+
+
+
+
+
--- /dev/null
+/*
+ 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;
+}
+
--- /dev/null
+/*
+ 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
+}
+
+
+
+
--- /dev/null
+/*****************************************************************
+ 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");
+}
--- /dev/null
+/*
+ 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;
+}
+
--- /dev/null
+/*
+ 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
+}
+
+
--- /dev/null
+/*
+ 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;
+}
+
+
--- /dev/null
+/*
+ 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;
+}
+
+
--- /dev/null
+/*
+ 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;
+}
+
+
--- /dev/null
+/*
+ * 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)
+{
+
+}
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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)
+{
+
+}
+
+
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+
+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
--- /dev/null
+/*
+ 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);
+}
+
--- /dev/null
+#!/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
+
+
--- /dev/null
+
+#
+# add these lines to /etc/conf.modules
+#
+
+alias char-major-98 comedi
+post-install comedi /etc/comedi.conf
+
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
+
--- /dev/null
+#
+# 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)
+
--- /dev/null
+/*
+ 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 */
+
--- /dev/null
+.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>
+
--- /dev/null
+.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>
+
--- /dev/null
+#!/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
--- /dev/null
+
+
+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.
+
--- /dev/null
+#!/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
+
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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)
+ */
--- /dev/null
+
+source comedi/Config.in
+
--- /dev/null
+#!/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