From: David Schleef Date: Wed, 2 Feb 2000 03:21:59 +0000 (+0000) Subject: Initial revision X-Git-Tag: r0_7_36~1 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=a5f1a0a2b6d24275bcbf095296f82516792c80b2;p=comedi.git Initial revision --- a5f1a0a2b6d24275bcbf095296f82516792c80b2 diff --git a/Changelog b/Changelog new file mode 100644 index 00000000..dd313fc2 --- /dev/null +++ b/Changelog @@ -0,0 +1,20 @@ + +0.7.36 + + - Rearranged most files + - changed Makefiles to work the same as Linux-2.2.13 + - moved all drivers to attach/detach methods + - made all drivers modular + - changed exp_ioctl to kcomedilib + - fixed NI pcidio 96 probing problems + +0.7.31 + + - Added etc/das1600.conf, etc/dt282x.conf + - Start of new attach/detach methods + - dt282x: uses new attach/detach methods + - das1600: changed comments to reflect actual code + - dt282x: DMA code rewritten + - dt282x: ao mode2 support added, with DMA + - ni_E: removed non-functional code + diff --git a/Documentation/comedi/FAQ b/Documentation/comedi/FAQ new file mode 100644 index 00000000..611d6869 --- /dev/null +++ b/Documentation/comedi/FAQ @@ -0,0 +1,36 @@ + +1. Should I use CONFIG_MODVERSIONS when compiling a Linux kernel? + +Short answer: No. Long answer: MODVERSIONS allows you to safely +use a module on multiple kernel versions. However, making comedi +work with CONFIG_MODVERSIONS is a real pain. Sometimes it works, +sometimes it doesn't. + + +2. I get the error message: + +Makefile:14: /usr/src/linux/.config: No such file or directory +make: *** No rule to make target `/usr/src/linux/.config'. Stop. + + +It means you don't have the file /usr/src/linux/.config. This file +contains all the configuration information about your linux kernel, +which comedi needs in order to compile correctly. Some distributions +don't contain this file, so you will need to create your own, by +compiling your own kernel. This file is automatically created +when you run 'make config', the first step in compiling a kernel. + + +3. When compiling programs that use libcomedi, I get error message: + +/usr/lib/libcomedi.a(range.o): In function `comedi_from_phys': +range.o(.text+0x169): undefined reference to `floor' + + +This error messsage indicates that the linker cannot resolve the +reference to floor(), which is a function in the math library +that is used in the function comedi_from_phys(). This only +happens when you link statically. Add '-lm' to your linking +command. + + diff --git a/Documentation/comedi/Hardware_Driver.HOWTO b/Documentation/comedi/Hardware_Driver.HOWTO new file mode 100644 index 00000000..8472e83f --- /dev/null +++ b/Documentation/comedi/Hardware_Driver.HOWTO @@ -0,0 +1,206 @@ + +Hardware driver interface + + +[ this is a little outdated -ds ] + + +1. Introduction + +This comedi hardware driver writing HOWTO is written to help you +write a driver for your particular choice of hardware. You should +be familiar with how comedi works from the user's point of view, +i.e., from a process that is utilizing the comedi driver. + +This guide does not explain the details of things like reading +and writing I/O ports, Linux kernel internals, or interrupt +processing. These issues are covered in other documents, +specifically, the IO-Port-Programming mini-HOWTO, the Kernel +Hacker's Guide, and the Linux source. + + +2. The source tree + +As of comedi-0.5.0, hardware drivers need to be part of the +source tree and be compiled with the rest of the comedi driver +into the module comedi.o. Later versions will hopefully support +separate compiling/separate modules, etc. + +The source for the comedi module is located in module/, including +the device independent part and each hardware driver. A hardware +driver, such as the "dt282x" driver, is typically located in the +C source file of the same name. + +The hardware driver "dummy" is a stripped-down example that may be +a good starting point for new hardware drivers. + + +3. comedi_config and *_init() + +The file "module/comeditypes.c" has a list of the initialization +functions for all the drivers. You should add your init function +to this list. The top level Makefile and scripts/configure +take care of all the defines. + +The purpose of comeditypes.c is to keep a list of available +hardware drivers by keeping a list of available initialization +functions. Thus, when comedi_config is run, it issues the +COMEDI_CONFIG ioctl() with the name of the driver and +configuration parameters (I/O port address, irq, dma, etc.). +The comedi driver then calls the initialization function of +each hardware driver on the list. + +Inside the initialization function, you should perform the +following tasks: + + o Check to see if you are the correct hardware driver + for the name specified in the comedi_devconfig structure. + Your hardware driver may consider several names as + "correct", and even behave differently depending on + the name given. The idea here is to support different + cards in a series using different names, but using the + same hardware driver. If you are not the correct + hardware driver, return a 0. + + o Announce that the hardware driver has begun initialization + by a printk("comedi%d: driver: ",minor); + + o Check and request the I/O port region, IRQ, DMA, and other + hardware resources. It is convenient here if you verify the + existence of the hardware and the correctness of the other + information given. Sometimes, unfortunately, this cannot + be done. + + o Fill in the comedi_device structure. + + o Allocate your private data structure and space for channel + configuration. + + o Configure each channel. Each channel has three function + pointers: one for triggering a single conversion (itrig), + one for triggering general conversions (trig), and one + for setting channel parameters (sp). Each channel also has + a parameter linked list. + Parameters are added to this list via addparam(). All the + parameter routines are in param.c. They are currently not + very efficient, so if you know of a better algorithm... + Look at the header files for parameter types that you may + wish to include. + + o Initialize the read buffer via comedi_initbuf(). The buffering + routines are in buffer.c, and support multiple processes + reading the same device. The buffers are also dynamic, so + you don't have to worry about the hardware driver collecting + 1M of data before the controlling process reads it. I've been + using these routines for a while--they appear to be pretty + stable. Most of the error messages you get from the buffer + routines indicate memory leaks. + + o Set mtrig in the comedi_device structure to the function that + is called for a channel scan trigger. + + o Tell the comedi driver the function to call when your driver + needs to be removed. + + o Return a 1. If there were any errors along the way, you + should return the appropriate error number, but don't forget + to release any resources that you allocated. (It will only + take one time to realize that if you don't release an I/O region, + you have to reboot to get it back. (Or write a hack, as I did.)) + + +4. Set Parameter routines + +When the COMEDI_SETPARAM ioctl() is called, the comedi driver +calls the setparameter routine of the channel involved. Common +settable parameters are input gain (input range) and setting bits +on digital I/O lines as input or output. The setparameter +function should check the validity of the parameter type and +value, and then call changeparam() to update the parameter +linked lists. If necessary, calls to update the hardware +should be made. + + +5. Triggering routines + +Like the setparameter routines, each channel has a trigger +function that is called for a COMEDI_TRIG ioctl(). The trigger +function is called with the comedi_trig structure, which +includes the trigger type, number of samples, and one sample value +(which may be read or written, depending on context). + +As of 0.5.0, there are two triggering routines per channel, one +for TRIGNOW, and one for all the rest. The reason this was done +is because the TRIGNOW functions are exported to the rest of the +kernel (i.e., for RTLinux). + +Trigger types are accompanied by the trigger variable, which +is interpreted based on the trigger type. The types of triggers +(currently) available are: + + o COMEDI_TRIGNOW - causes one sample conversion to be + completed immediately. The trigger variable and number of + samples are ignored. + + o COMEDI_TRIGCLK - causes conversions timed by a clock on + the hardware. The trigger variable determines the clock + speed. + + o COMEDI_TRIGEXT - causes conversions to be triggered by + an external trigger provided by the hardware. The trigger + variable determines the particular choice, if there is + more than one choice. + +Other trigger sources that may be defined in the future are +analog triggers and comedi triggers. Comedi triggers could include +the 100 Hz interrupt, the rtc interrupt, general timing triggers, +signals generated by other boards, etc. + +Except for COMEDI_TRIGNOW, your triggering routine should return +after telling the hardware to perform the requested task. For +COMEDI_TRIGNOW on input, you should wait for the conversion to +finish and return it in the ioctl structure, but do *NOT* report +the sample via report_sample(). + +Typically, you will poll the hardware or the hardware will +generate interrupts to tell you when samples are ready. The +samples should be reported via report_sample(). + + +6. The remove function + +A driver is removed from service via a call to dev->rem(). The driver +is expected to halt all conversions, put the hardware in a sane, +off-line state, delete all channel parameters, free all allocated memory, +and release any irq's and port addresses held. If these things are +not all done, there will be memory leaks, and more importantly, the +driver will not configure properly if reused. + + +A. Goals + +A few things to strive for: + + o Your hardware driver should be functional appropriate to + the resources allocated. I.e., if the driver is fully + functional when configured with an IRQ and DMA, it should + still function moderately well with just an IRQ, or still + do minor tasks without IRQ or DMA. Does your driver really + require an IRQ to do digital I/O? Maybe someone will want + to use your driver *just* to do digital I/O and has no + interrupts available. + + o Drivers are to have absolutely *NO* global variables, mainly + because the existence of global variables immediately negates + any possibility of using the driver for two devices. The + pointer dev->private should be used to point to a structure + containing any additional variables needed by a driver/device + combination. + + o Drivers should report errors and warnings via a printk line + that starts with "comedi%d: driver_name:" where %d is the + minor number of the device. + + + + diff --git a/Documentation/comedi/command b/Documentation/comedi/command new file mode 100644 index 00000000..43d7ac58 --- /dev/null +++ b/Documentation/comedi/command @@ -0,0 +1,150 @@ + +Version 0.8.x will feature a replacement for the comedi_trig +structure that allows much more flexible acquisition with +applicable hardware. Gone will be the days of "mode2" and +friends. + +The replacement for mode0, i.e., immediate acquisition, is a +new ioctl that is very similar to the old trigger ioctl, but +just implements mode0 features. + +The other modes are replaced by the comedi "command" ioctl. +The command structure allows you to specify what causes each +action that is taken during acquisition. The actions currently +understood by comedi are: + + - Start acquisition + - Stop acquisition + - Begin a scan + - End a scan + - Convert a sample + +A "scan" is a repeated measurement of N different channels. + +The different types of "triggers" or "trigger signals" that can +cause events to occur are: + + - TRIG_NONE - don't ever trigger + - TRIG_NOW - cause trigger to occur right now (or really soon) + - TRIG_FOLLOW - (see below) + - TRIG_TIME - trigger at specific time + - TRIG_TIMER - trigger at specific rate + - TRIG_COUNT - trigger when count reaches specific value + - TRIG_EXT - trigger on external signal + - TRIG_INT - trigger on an internal comedi signal + +Not all triggers are applicable to all events. Supported triggers +for specific events depends significantly on your particular +device. In addition, for every trigger type, there is a cooresponding +argument that specifies the rate, the count, which external signal, +etc. + +In particular, scan_end events will almost always be triggered on +TRIG_COUNT, with the argument being the number of channels in the +scan. (Actually, samples in the scan, since on most boards you can +measure a single channel multiple times in a scan.) Also, until +otherwise supported, start events can only be TRIG_NOW. + +TRIG_FOLLOW is a special type of trigger for scan_begin events that +triggers on the next lower level trigger, in this case, the trigger +for convert events. It may or may not be supported. Later, it may +also be used for start events if you want to chain multiple commands. +I might also consider using it for convert events, to indicate the +board maximum rate. + +The command strucure is as follows: + +struct comedi_cmd_struct{ + unsigned int subdev; + unsigned int flags; + + unsigned int start_src; + unsigned int start_arg; + + unsigned int scan_begin_src; + unsigned int scan_begin_arg; + + unsigned int convert_src; + unsigned int convert_arg; + + unsigned int scan_end_src; + unsigned int scan_end_arg; + + unsigned int stop_src; + unsigned int stop_arg; + + unsigned int *chanlist; /* channel/range list */ + unsigned int chanlist_len; + + sampl_t *data; /* data list, size depends on subd flags */ + unsigned int data_len; +}; + +subdev is the target subdevice. flags are flags, similar to the +old trigger structure. chanlist, chanlist_len, data, and data_len +are similar to the old trigger structure, although chanlist_len has +been changed from n_chan. data and data_len are used only by users +who write kernel-space code, i.e., virtual comedi devices or RTLinux. + + +Some examples for commands: + +Suppose you want to measure channels 1,2,3,4 at a rate of 10 khz, with +the inter-sample time at 10 us (100 khz), and that you want to measure +10000 scans. This is analogous to the old mode2 acquisition. +Initialization of the command structure to do this would include: + + start_src = TRIG_NOW; + start_arg = 0; // doesn't matter + scan_begin_src = TRIG_TIMER; + scan_begin_arg = 100000; // == 100e3 nanoseconds + convert_src = TRIG_TIMER; + convert_arg = 10000; // == 10e3 nanoseconds + scan_end_src = TRIG_COUNTER; + scan_end_arg = 4; // == number of channels + stop_src = TRIG_COUNTER; + stop_arg = 10000; // == 10000 scans + +If, instead, you wish to start continuous acquisition, and stop it with +a comedi_cancel(), you would change the stop event trigger to: + + stop_src = TRIG_NONE; + stop_arg = 0; + +Suppose you want to use an external signal to trigger scan_begin events. +Then you would change the scan_begin event trigger to: + + scan_begin_src = TRIG_EXT; + scan_begin_arg = 2; // external trigger #2 + +The number of possible external trigger lines is board dependent. Your +board may only have one non-configurable digital external trigger, or +it may support complicated selection of external triggering, including +analog triggers at multiple levels, edge or level digital triggers, +synchonization signals with other boards, etc. Eventually, boards +that support complicated triggering will have a triggering subdevice +with which you can configure all the different features. + +Here's some pseudo-code that describes how the device should interpret +your command: + +do_command() +{ + while(!start_trigger) + do_nothing(); + + while(!stop_trigger){ + if(!scan_begin_trigger) + continue; + + while(!scan_end_trigger){ + while(!convert_trigger) + do_nothing(); + do_conversion(); + } + } +} + +The fencepost issues, i.e., what happens when you have conflicting +triggers, is device-dependent. + diff --git a/Documentation/comedi/drivers.txt b/Documentation/comedi/drivers.txt new file mode 100644 index 00000000..808a62aa --- /dev/null +++ b/Documentation/comedi/drivers.txt @@ -0,0 +1,405 @@ + +This file contains information about specific drivers, their +features, and limitation. + +8255.o: + +Author: ds +Status: only mode 0 supported + +The classic in digital I/O. Three channels of 8 bit digital I/O, +each channel is I/O configurable, channels 0 and 1 in 8 bit units, +channel 2 in 4 bit units. The driver does not support modes 1 or 2 +yet, since I don't really understand how they would potentially be used. +(Send me email if you want to use these modes.) If and when +modes 1 and 2 are supported, there is a strong possibility that the +3rd channel will be split into two 4-bit channels. (Refer to the +8255 spec for clues as to why.) + +You should configure this driver if you plan to use a board that +has an 8255 chip. For multifunction boards, the main driver will +configure the 8255 subdevice automatically. + + + +ni-atmio.o: National Instruments AT-MIO-E series (all boards) + +Author: ds +Status: mainly limited by Comedi infrastructure + +The isapnptools package is required to use this board. Use isapnp to +configure the I/O base for the board, and then pass the same value as +a parameter in comedi_config. A sample isapnp.conf file is included +in the etc/ directory. + +Assuming that the NI spec is correct, the driver should correctly +identify every board in the series. Each channel should have the +appropriate parameters, i.e., input/output ranges, number of bits, +etc. If the driver fails to recognize your card or does not have +the correct parameters, please contact me. + +Comedilib includes a utility to autocalibrate these boards. The +boards seem to boot into a state where the all calibration DACs +are at one extreme of their range, thus the default calibration +is terrible. Calibration at boot is strongly encouraged. + + + +das08.o: ComputerBoards, Keithley Metrabyte DAS-08 + +Authors: Warren Jasper, ds +Status: unknown + + + +das08jr.o: ComputerBoards DAS-08jr-AO + +Authors: Jochen Küpper , 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 + +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 +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 +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 , Eric Bunn +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 +Status: works + + + + diff --git a/Documentation/comedi/mode-info b/Documentation/comedi/mode-info new file mode 100644 index 00000000..e9109ab0 --- /dev/null +++ b/Documentation/comedi/mode-info @@ -0,0 +1,168 @@ +From owner-comedi@stm.lbl.gov Tue Jun 29 00:56:15 1999 +Received: (from majordom@localhost) + by stm.lbl.gov (8.8.7/8.8.7) id AAA29747 + for comedi-list; Tue, 29 Jun 1999 00:56:15 -0700 +Received: (from ds@localhost) + by stm.lbl.gov (8.8.7/8.8.7) id AAA29742; + Tue, 29 Jun 1999 00:56:14 -0700 +Message-ID: <19990629005613.A29725@stm.lbl.gov> +Date: Tue, 29 Jun 1999 00:56:13 -0700 +From: David Schleef +To: Tomasz Motylewski +Cc: comedi@stm.lbl.gov +Subject: Re: pcl-711 driver ISR. +References: +Mime-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +X-Mailer: Mutt 0.91.1 +In-Reply-To: ; 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 - To unsubscribe, +send mail to with "unsubscribe" in the body. + diff --git a/Documentation/comedi/notes/README b/Documentation/comedi/notes/README new file mode 100644 index 00000000..23209365 --- /dev/null +++ b/Documentation/comedi/notes/README @@ -0,0 +1,3 @@ + +These are some notes probably only decipherable by me + diff --git a/Documentation/comedi/notes/atmio_notes b/Documentation/comedi/notes/atmio_notes new file mode 100644 index 00000000..62c5823b --- /dev/null +++ b/Documentation/comedi/notes/atmio_notes @@ -0,0 +1,60 @@ + + +at-mio-64f-5 320487.pdf has info +at-mio-16x 320640b.pdf has info +at-mio-16d 320489.pdf has info +at-mio-16f-5 320266.pdf has info +at-mio-16 320476.pdf no info + + +E series: + +see ni_E.c + + + + +E series: + +issues: + +- extra interrupt when changing from NE to HF FIFO mode + +ai reset registers: + + AI_Command_1_Register + AI_Command_2_Register + + AI_Mode_1_Register + AI_Mode_2_Register + AI_Mode_3_Register + AI_START_STOP_Select_Register + + AI_Output_Control_Register + AI_Personal_Register + AI_Trigger_Select_Register + + +ai mode registers: + + 2: + + AI_START_STOP_Select_Register + AI_Mode_2_Register + AI_Mode_3_Register + AI_Command_1_Register + AI_Command_2_Register + + 4: + + AI_START_STOP_Select_Register + AI_Mode_2_Register + AI_Command_1_Register + + +ai_reset(): + + AI_Personal_Register + AI_Output_Control_Register + AI_Trigger_Select_Register + diff --git a/Documentation/comedi/notes/das b/Documentation/comedi/notes/das new file mode 100644 index 00000000..e3334e6e --- /dev/null +++ b/Documentation/comedi/notes/das @@ -0,0 +1,146 @@ + + +/* cio-dac08 */ +/* cio-dac16 */ + +/* + 08 has 8 channels, 16 has 16 + can do simultaneous update + */ + +#define DAC08_LSB(a) (2*(a)+0) +#define DAC08_MSB(a) (2*(a)+1) + + + + +/* cio-das16jr */ + +/* + has a 8254 +*/ + +read + +0 a/d low +1 a/d hi +2 mux settings +3 digital 4 in +4-7 unused +8 a/d status +9 control settings +10 unused +11 gain and range +12 counter 0 +13 counter 1 +14 counter 2 +15 unused + +write + +0 start a/d +1 unused +2 mux scan ctrl +3 digital 4 out +4-7 unused +8 unused +9 control +10 counter source control +11 gain and range +12 ctr 0 load +13 ctr 1 load +14 ctr 2 load +15 8254 counter control + + + + + +computer boards current list of products: + +ISA: + +(dio) +CIO-DI48 +CIO-DI96 +CIO-DI192 +CIO-DIO24 +CIO-DIO24H +CIO-DIO/CTR3 +CIO-DIO48 +CIO-DIO48H +CIO-DIO96 +CIO-DIO192 +CIO-DO24DD +CIO-DO48DD +CIO-DO48H +CIO-DO96H +CIO-DO192H +CIO-PDMA16 + +(ao) +CIO-DAC02 +CIO-DAC02/16 +CIO-DAC08 +CIO-DAC16 +CIO-DDA06/16 +CIO-DDA06/JR +CIO-DDA06/JR/16 + +(ai) +CIO-DAS08 * +CIO-DAS08-AO +CIO-DAS08/JR/16-AO +CIO-DAS08PGH * +CIO-DAS08PGL * +CIO-DAS802/16 +CIO-DAS1402/16 +CIO-DAS16 +CIO-DAS16F +CIO-DAS16/330 +CIO-DAS16/JR +CIO-DAS16/M1 +CIO-DAS6402/12 +CIO-DAS6402/16 + + + +PCI: + +(dio) +PCI-DIO24 +PCI-DIO24H +PCI-DIO24H/CTR3 +PCI-DIO48H/CTR15 +PCI-DIO48H +PCI-DIO96H +PCI-PDISO8 +PCI-PDISO16 + +(ao) +PCI-DDA08/12 +PCI-DDA04/12 +PCI-DDA02/12 + +(ai) +PCI-DAS1200 +PCI-DAS1001 +PCI-DAS1002 +PCI-DAS1200/JR +PCI-DAS1602/12 +PCI-DAS1602/16 +PCI-DAS1602/16/JR +PCI-DDA02/12 +PCI-DDA04/12 +PCI-DDA08/12 +PCI-DAS64/M3/16 +PCI-DAS64/M3/16/JR +PCI-DAS64/M2/16 +PCI-DAS64/M2/16/JR +PCI-DAS64/M1/16 +PCI-DAS64/M1/16/JR +PCI-DAS6402/16 +PCI-DAS6402/16/JR +PCI-DAS08 + + diff --git a/Documentation/comedi/notes/testing b/Documentation/comedi/notes/testing new file mode 100644 index 00000000..a6c51d50 --- /dev/null +++ b/Documentation/comedi/notes/testing @@ -0,0 +1,27 @@ + +*_init(): + +each subdev should init fields: + type + n_chan + subdev_flags + maxdata || maxdata_list + flags || flag_list + range_type || range_type_list + trig + + (optional) + timer_type + len_chanlist + maxbufsz + cancel + + +check subdev_flags, RT, etc. + +check for mem leaks + +check that each synchronous function returns 1 + +check that comedi_done is eventually called. + diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..8a3ff641 --- /dev/null +++ b/INSTALL @@ -0,0 +1,90 @@ + +Prerequisites: + - a machine capable of compiling Linux modules + - a correctly installed kernel source tree. Specifically, + you need to have the file /usr/src/linux/.config and + /usr/src/linux/include/linux/autoconf.h. If these are + not present, you can create them by compiling a new + kernel. Some distributions don't provide these by + default. + - a 2.0, 2.2, or 2.3 series kernel. Some development + kernels may not work, since I don't keep very good + track of them. + +Configure using 'make'. The first time you run make, it will take +you through configuration options similar to compiling a linux +kernel. If you need to run the configuration again, use 'make config'. + +Compile using 'make'. If this doesn't work, make sure you have the +basic tools installed to compile. If you can successfully compile +other things, consult the author, as he has probably made a mistake. + +Install using 'make install' as root. This installs the files: + /lib/modules/<>/misc/comedi.o + /lib/modules/<>/misc/<>.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 <>.o + +If your module dependencies are set up correctly, this will load +both comedi.o and your driver. + +In this version, the default behavior when the module is loaded is +to _not_ configure it automatically. This may change soon. For +many boards, you need to supply additional information, such as +I/O address, IRQ, and possibly DMA channels. The following commands +are examples: + + /usr/sbin/comedi_config /dev/comedi0 dt282x 0x240,3 + /usr/sbin/comedi_config /dev/comedi1 atmio-E 0x260,4 + /usr/sbin/comedi_config /dev/comedi2 dt2817 0x228 + /usr/sbin/comedi_config /dev/comedi0 pcimio-E + +Try a 'man comedi_config' for information on how to use +this utility. IRQs can be automatically detected for some hardware, +although this is not recommended, for the same reasons as other +hardware. + +If you like to autoload your modules, put the lines + + alias char-major-98 comedi + post-install comedi /etc/comedi.conf + +and create a script /etc/comedi.conf, which can be a list of commands +to configure the board. If you have a National Instruments AT-MIO or +PCI-MIO board, you probably will want to run the autocalibration tool +that is part of comedilib in this script. + +To write programs that use comedi, there are demo programs included +with comedilib. + + +Upgrading: + +From versions prior to 0.6.0, you will need to edit and recompile +all programs that use comedi or comedilib, since the names of +functions and ioctls have changed. + +From versions prior to 0.5.0, you will need to recompile all programs +that use comedi or comedilib, since the interface to both of these has +changed. No changes should need to be made to the source of the +programs. The format for parameters of comedi_config has changed. + +From versions prior to 0.4.0, you will need to run 'make dev' again +to recreate /dev/comedi*, since the major number has changed. + diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..627e3c44 --- /dev/null +++ b/Makefile @@ -0,0 +1,100 @@ + +# Makefile for comedi + +VERS1 = 0 +VERS2 = 7 +VERS3 = 36 + +INSTALLDIR=/usr + +LINUXDIR = /usr/src/linux + +TOPDIR := $(shell if [ "$$PWD" != "" ]; then echo $$PWD; else pwd; fi) + +.EXPORT_ALL_VARIABLES: + +CFLAGS = -Wall -O2 -Wstrict-prototypes +CFLAGS += -D__KERNEL__ -I $(LINUXDIR)/include -I $(TOPDIR)/include -I . + +ifeq ($(CONFIG_SMP),y) +CFLAGS += -D__SMP__ +endif + +CONFIG_SHELL := sh + +ifeq (.config,$(wildcard .config)) +include .config +include .uts_version +include $(LINUXDIR)/.config +all2: modules comedi_config +else +all2: config +endif + +SUBDIRS := comedi + +DOCFILES= README INSTALL drivers `find doc -type f` + +comedi_config: dummy + $(MAKE) -C comedi_config + +config: dummy + scripts/Configure + +install: dummy +ifeq (/lib/modules/${UTS_VERSION},$(wildcard /lib/modules/${UTS_VERSION})) + install -d /lib/modules/${UTS_VERSION}/misc + install modules/*.o /lib/modules/${UTS_VERSION}/misc + depmod -a ${UTS_VERSION} +else + # *** + # *** Could not install comedi.o into /lib/modules/${UTS_VERSION}/misc + # *** Please install by hand. + # *** +endif + install -m 755 comedi_config/comedi_config ${INSTALLDIR}/sbin + install -d ${INSTALLDIR}/include + (cd include;install -m 644 comedi.h ${INSTALLDIR}/include) + install man/comedi.7 ${INSTALLDIR}/man/man7 + install man/comedi_config.8 ${INSTALLDIR}/man/man8 +# install -d ${INSTALLDIR}/doc/comedi +# install ${DOCFILES} ${INSTALLDIR}/doc/comedi + +lpr: dummy + find . -name '*.[chs]'|xargs enscript -2r -pit.ps + +dev: dummy + -rm /dev/comedi* + /bin/mknod /dev/comedi0 c 98 0 + /bin/mknod /dev/comedi1 c 98 1 + /bin/mknod /dev/comedi2 c 98 2 + /bin/mknod /dev/comedi3 c 98 3 + chown root.root /dev/comedi* + chmod 666 /dev/comedi* + + +MODFLAGS += -DMODULE + +modules: $(patsubst %, _mod_%, $(SUBDIRS)) + +$(patsubst %, _mod_%, $(SUBDIRS)) : dummy + $(MAKE) -C $(patsubst _mod_%, %, $@) CFLAGS="$(CFLAGS) $(MODFLAGS)" MAKING_MODULES=1 modules + +clean: + rm -f core `find . -name '*.[oas]'` + rm -f core `find . -name '.*.flags' -print` + rm -f comedi/range.h comedi/mk_range comedi/range.def + rm -f comedi_config/comedi_config + +distclean: clean + rm -f .depend `find . -name .depend -print` + rm -f core `find . \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ + -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ + -o -name '.*.rej' -o -name '.SUMS' -o -size 0 \) -print` TAGS + rm -f .config .uts_version include/config.h + rm -f modules/* + +include $(TOPDIR)/Rules.make + + +dummy: diff --git a/NOTES b/NOTES new file mode 100644 index 00000000..2b21d064 --- /dev/null +++ b/NOTES @@ -0,0 +1,18 @@ + +0.7.36 + +I *really* changed everything around. Basically, I changed all the +Makefiles to fit in with the Linux kernel Makefiles. Ideally, you +could now move the comedi directory to /usr/src/linux/drivers/char, +change a few files, and compile comedi into your kernel. + +One of the side effects of changing the makefiles is that all +the drivers are compiled as separate modules. Another side effect +is that I'd bet comedi doesn't compile correctly for 2.0.x kernels +and possibly RTL and/or RTAI. + +I almost certainly broke a bunch of drivers. Let me know which +ones. + + + diff --git a/README b/README new file mode 100644 index 00000000..a0d768c7 --- /dev/null +++ b/README @@ -0,0 +1,23 @@ + +For installation instructions, look at the file INSTALL. For notes +of what is happening/changing in recent comedi releases, see the +file NOTES. + +This distribution contains just the Comedi kernel module. You will +almost certainly also want to download Comedilib, which is a user +space library, a few utilities, and some example programs. + +Comedi has a mailing list. Send "subscribe comedi" to +majordomo@stm.lbl.gov to subscribe. Traffic is light, and mainly +questions/answers about comedi installation, bugs, and programming. +General questions about data acquisition are also welcome. + +Comedi also has a web page, at http://stm.lbl.gov/comedi. New versions +of comedi can be found on the ftp site ftp://stm.lbl.gov/pub/comedi. + +Comedi may be freely distibuted and modified in accordance with the +GNU General Public License. + +The person behind all this misspelled humor is David Schleef +, or http://stm.lbl.gov/~ds. + diff --git a/Rules.make b/Rules.make new file mode 100644 index 00000000..f55ea43d --- /dev/null +++ b/Rules.make @@ -0,0 +1,301 @@ +# +# This file contains rules which are shared between multiple Makefiles. +# + +# +# False targets. +# +.PHONY: dummy + +# +# Special variables which should not be exported +# +unexport EXTRA_ASFLAGS +unexport EXTRA_CFLAGS +unexport EXTRA_LDFLAGS +unexport EXTRA_ARFLAGS +unexport SUBDIRS +unexport SUB_DIRS +unexport ALL_SUB_DIRS +unexport MOD_SUB_DIRS +unexport O_TARGET +unexport O_OBJS +unexport L_OBJS +unexport M_OBJS +# intermediate objects that form part of a module +unexport MI_OBJS +unexport ALL_MOBJS +# objects that export symbol tables +unexport OX_OBJS +unexport LX_OBJS +unexport MX_OBJS +unexport MIX_OBJS +unexport SYMTAB_OBJS + +unexport MOD_LIST_NAME + +# +# Get things started. +# +first_rule: sub_dirs + $(MAKE) all_targets + +# +# Common rules +# + +%.s: %.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -S $< -o $@ + +%.i: %.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -E $< > $@ + +%.o: %.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -c -o $@ $< + @ ( \ + echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@))),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@))))' ; \ + echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \ + echo 'endif' \ + ) > $(dir $@)/.$(notdir $@).flags + +%.o: %.s + $(AS) $(ASFLAGS) $(EXTRA_CFLAGS) -o $@ $< + +# +# +# +all_targets: $(O_TARGET) $(L_TARGET) + +# +# Rule to compile a set of .o files into one .o file +# +ifdef O_TARGET +ALL_O = $(OX_OBJS) $(O_OBJS) +$(O_TARGET): $(ALL_O) + rm -f $@ +ifneq "$(strip $(ALL_O))" "" + $(LD) $(EXTRA_LDFLAGS) -r -o $@ $(ALL_O) +else + $(AR) rcs $@ +endif + @ ( \ + echo 'ifeq ($(strip $(subst $(comma),:,$(EXTRA_LDFLAGS) $(ALL_O))),$$(strip $$(subst $$(comma),:,$$(EXTRA_LDFLAGS) $$(ALL_O))))' ; \ + echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \ + echo 'endif' \ + ) > $(dir $@)/.$(notdir $@).flags +endif # O_TARGET + +# +# Rule to compile a set of .o files into one .a file +# +ifdef L_TARGET +$(L_TARGET): $(LX_OBJS) $(L_OBJS) + rm -f $@ + $(AR) $(EXTRA_ARFLAGS) rcs $@ $(LX_OBJS) $(L_OBJS) + @ ( \ + echo 'ifeq ($(strip $(subst $(comma),:,$(EXTRA_ARFLAGS) $(LX_OBJS) $(L_OBJS))),$$(strip $$(subst $$(comma),:,$$(EXTRA_ARFLAGS) $$(LX_OBJS) $$(L_OBJS))))' ; \ + echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \ + echo 'endif' \ + ) > $(dir $@)/.$(notdir $@).flags +endif + +# +# This make dependencies quickly +# +fastdep: dummy + $(TOPDIR)/scripts/mkdep $(wildcard *.[chS] local.h.master) > .depend +ifdef ALL_SUB_DIRS + $(MAKE) $(patsubst %,_sfdep_%,$(ALL_SUB_DIRS)) _FASTDEP_ALL_SUB_DIRS="$(ALL_SUB_DIRS)" +endif + +ifdef _FASTDEP_ALL_SUB_DIRS +$(patsubst %,_sfdep_%,$(_FASTDEP_ALL_SUB_DIRS)): + $(MAKE) -C $(patsubst _sfdep_%,%,$@) fastdep +endif + + +# +# A rule to make subdirectories +# +sub_dirs: dummy $(patsubst %,_subdir_%,$(SUB_DIRS)) + +ifdef SUB_DIRS +$(patsubst %,_subdir_%,$(SUB_DIRS)) : dummy + $(MAKE) -C $(patsubst _subdir_%,%,$@) +endif + +# +# A rule to make modules +# +ALL_MOBJS = $(MX_OBJS) $(M_OBJS) +ifneq "$(strip $(ALL_MOBJS))" "" +PDWN=$(shell $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh) +endif + +ifdef MOD_SUB_DIRS +$(patsubst %,_modsubdir_%,$(MOD_SUB_DIRS)) : dummy + $(MAKE) -C $(patsubst _modsubdir_%,%,$@) modules +endif + +ifdef MOD_IN_SUB_DIRS +$(patsubst %,_modinsubdir_%,$(MOD_IN_SUB_DIRS)) : dummy + $(MAKE) -C $(patsubst _modinsubdir_%,%,$@) modules +endif + +modules: $(ALL_MOBJS) $(MIX_OBJS) $(MI_OBJS) dummy \ + $(patsubst %,_modsubdir_%,$(MOD_SUB_DIRS)) \ + $(patsubst %,_modinsubdir_%,$(MOD_IN_SUB_DIRS)) +ifneq "$(strip $(MOD_LIST_NAME))" "" + rm -f $$TOPDIR/modules/$(MOD_LIST_NAME) +ifdef MOD_SUB_DIRS + for i in $(MOD_SUB_DIRS); do \ + echo `basename $$i`.o >> $$TOPDIR/modules/$(MOD_LIST_NAME); done +endif +ifneq "$(strip $(ALL_MOBJS))" "" + echo $(ALL_MOBJS) >> $$TOPDIR/modules/$(MOD_LIST_NAME) +endif +ifneq "$(strip $(MOD_TO_LIST))" "" + echo $(MOD_TO_LIST) >> $$TOPDIR/modules/$(MOD_LIST_NAME) +endif +endif +ifneq "$(strip $(ALL_MOBJS))" "" + echo $(PDWN) + cd $$TOPDIR/modules; for i in $(ALL_MOBJS); do \ + ln -sf ../$(PDWN)/$$i $$i; done +endif + +# +# A rule to do nothing +# +dummy: + +# +# This is useful for testing +# +script: + $(SCRIPT) + +# +# This sets version suffixes on exported symbols +# Uses SYMTAB_OBJS +# Separate the object into "normal" objects and "exporting" objects +# Exporting objects are: all objects that define symbol tables +# +ifdef CONFIG_MODULES + +SYMTAB_OBJS = $(LX_OBJS) $(OX_OBJS) $(MX_OBJS) $(MIX_OBJS) + +ifdef CONFIG_MODVERSIONS +ifneq "$(strip $(SYMTAB_OBJS))" "" + +MODINCL = $(LINUXDIR)/include/linux/modules + +# The -w option (enable warnings) for genksyms will return here in 2.1 +# So where has it gone? +# +# Added the SMP separator to stop module accidents between uniprocessor +# and SMP Intel boxes - AC - from bits by Michael Chastain +# + +ifdef CONFIG_SMP + genksyms_smp_prefix := -p smp_ +else + genksyms_smp_prefix := +endif + +$(MODINCL)/%.ver: %.c + @if [ ! -r $(MODINCL)/$*.stamp -o $(MODINCL)/$*.stamp -ot $< ]; then \ + echo '$(CC) $(CFLAGS) -E -D__GENKSYMS__ $<'; \ + echo '| $(GENKSYMS) $(genksyms_smp_prefix) -k $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) > $@.tmp'; \ + $(CC) $(CFLAGS) -E -D__GENKSYMS__ $< \ + | $(GENKSYMS) $(genksyms_smp_prefix) -k $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) > $@.tmp; \ + if [ -r $@ ] && cmp -s $@ $@.tmp; then echo $@ is unchanged; rm -f $@.tmp; \ + else echo mv $@.tmp $@; mv -f $@.tmp $@; fi; \ + fi; touch $(MODINCL)/$*.stamp + +$(addprefix $(MODINCL)/,$(SYMTAB_OBJS:.o=.ver)): $(LINUXDIR)/include/linux/autoconf.h + +$(LINUXDIR)/include/linux/modversions.h: $(addprefix $(MODINCL)/,$(SYMTAB_OBJS:.o=.ver)) + @echo updating $(LINUXDIR)/include/linux/modversions.h + @(echo "#ifndef _LINUX_MODVERSIONS_H";\ + echo "#define _LINUX_MODVERSIONS_H"; \ + echo "#include "; \ + cd $(LINUXDIR)/include/linux/modules; \ + for f in *.ver; do \ + if [ -f $$f ]; then echo "#include "; 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 " > $@ + +endif # CONFIG_MODVERSIONS + +ifneq "$(strip $(SYMTAB_OBJS))" "" +$(SYMTAB_OBJS): $(LINUXDIR)/include/linux/modversions.h $(SYMTAB_OBJS:.o=.c) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c) + @ ( \ + echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB)),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@) -DEXPORT_SYMTAB)))' ; \ + echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \ + echo 'endif' \ + ) > $(dir $@)/.$(notdir $@).flags +endif + +endif # CONFIG_MODULES + + +# +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif + +ifneq ($(wildcard $(TOPDIR)/.hdepend),) +include $(TOPDIR)/.hdepend +endif + +# +# Find files whose flags have changed and force recompilation. +# For safety, this works in the converse direction: +# every file is forced, except those whose flags are positively up-to-date. +# +FILES_FLAGS_UP_TO_DATE := + +# For use in expunging commas from flags, which mung our checking. +comma = , + +FILES_FLAGS_EXIST := $(wildcard .*.flags) +ifneq ($(FILES_FLAGS_EXIST),) +include $(FILES_FLAGS_EXIST) +endif + +FILES_FLAGS_CHANGED := $(strip \ + $(filter-out $(FILES_FLAGS_UP_TO_DATE), \ + $(O_TARGET) $(O_OBJS) $(OX_OBJS) \ + $(L_TARGET) $(L_OBJS) $(LX_OBJS) \ + $(M_OBJS) $(MX_OBJS) \ + $(MI_OBJS) $(MIX_OBJS) \ + )) + +# A kludge: .S files don't get flag dependencies (yet), +# because that will involve changing a lot of Makefiles. +FILES_FLAGS_CHANGED := $(strip \ + $(filter-out $(patsubst %.S, %.o, $(wildcard *.S)), \ + $(FILES_FLAGS_CHANGED))) + +ifneq ($(FILES_FLAGS_CHANGED),) +$(FILES_FLAGS_CHANGED): dummy +endif diff --git a/TODO b/TODO new file mode 100644 index 00000000..bb5b2ccd --- /dev/null +++ b/TODO @@ -0,0 +1,14 @@ + +das6402 needs cleanup + +check each driver init for stuff + +grep XXX + +register_symtab + +allocate subdevice +allocate private + +fix PCI code, use pci_find_device() + diff --git a/comedi/Config.in b/comedi/Config.in new file mode 100644 index 00000000..88e8b2f6 --- /dev/null +++ b/comedi/Config.in @@ -0,0 +1,64 @@ + +define_bool CONFIG_COMEDI m + +comment 'Comedi Features' + +#bool 'Backwards compatibility' CONFIG_BACKWARDS + +tristate 'Kernel Comedilib' CONFIG_COMEDI_KLIB +if [ "$CONFIG_RTL" = "y" ];then + define_bool CONFIG_COMEDI_RT y + define_bool CONFIG_COMEDI_RTL y +fi +if [ "$CONFIG_RTAI" = "y" ];then + bool 'Real-time support' CONFIG_COMEDI_RT + if [ "$CONFIG_RTAI" = "y" ];then + define_bool CONFIG_COMEDI_RTAI y + fi +fi + +bool 'Verbose Debugging' CONFIG_COMEDI_DEBUG + +comment 'Hardware device drivers' + + +bool 'Data Translation boards' CONFIG_COMEDI_DT +if [ "$CONFIG_DT" = "y" ];then + tristate 'DT 2801' CONFIG_COMEDI_DT2801 + tristate 'DT 2811' CONFIG_COMEDI_DT2811 + tristate 'DT 2814' CONFIG_COMEDI_DT2814 + tristate 'DT 2815' CONFIG_COMEDI_DT2815 + tristate 'DT 2817' CONFIG_COMEDI_DT2817 + tristate 'DT 2821 series' CONFIG_COMEDI_DT282x + if [ "$CONFIG_EXPERIMENTAL" = "y" ];then + tristate 'DT3000' CONFIG_COMEDI_DT3000 + fi +fi + +bool 'National Instruments boards' CONFIG_COMEDI_NI +if [ "$CONFIG_NI" = "y" ];then + tristate 'AT-MIO E series' CONFIG_COMEDI_NI_ATMIO + if [ "$CONFIG_PCI" = "y" ];then + tristate 'PCI-MIO E series' CONFIG_COMEDI_NI_PCIMIO + tristate 'NI PCI-DIO series' CONFIG_COMEDI_NI_PCIDIO + if [ "$CONFIG_COMEDI_NI_PCIMIO" = "y" -o "$CONFIG_COMEDI_NI_PCIDIO" = "y" ];then + define_bool CONFIG_COMEDI_MITE y + fi + fi +fi + +tristate 'Computer Boards DAS-08 series' CONFIG_COMEDI_DAS08 +tristate 'Computer Boards DAS-08jr' CONFIG_COMEDI_DAS08JR +tristate 'Keithley Metrabyte DAS-1600 and compatibles' CONFIG_COMEDI_DAS1600 +tristate 'DAS16' CONFIG_COMEDI_DAS16 +tristate 'DAS-6402 and compatibles' CONFIG_COMEDI_DAS6402 +tristate 'Generic 8255 support' CONFIG_COMEDI_8255 +tristate 'Quanser Consulting MultiQ-3' CONFIG_COMEDI_MULTIQ3 +tristate 'Generic parallel port support' CONFIG_COMEDI_PARPORT +tristate 'PCL-711, PCL-711b, ACL-8112, and compatibles' CONFIG_COMEDI_PCL711 +tristate 'PCL-725' CONFIG_COMEDI_PCL725 +tristate 'PCL-726' CONFIG_COMEDI_PCL726 +tristate 'Analog Devices RTI-800/815' CONFIG_COMEDI_RTI800 +tristate 'Analog Devices RTI-802' CONFIG_COMEDI_RTI802 + + diff --git a/comedi/Makefile b/comedi/Makefile new file mode 100644 index 00000000..ad92c156 --- /dev/null +++ b/comedi/Makefile @@ -0,0 +1,73 @@ + + +SUB_DIRS := drivers +ALL_SUB_DIRS := kcomedilib drivers +MOD_SUB_DIRS := drivers +MOD_IN_SUB_DIRS := + +ifneq ($(CONFIG_COMEDI_KLIB),n) + MOD_SUB_DIRS += kcomedilib + SUB_DIRS += kcomedilib +endif + +MOD_LIST_NAME := MISC_MODULES + +M_OBJS := comedi.o +MI_OBJS := module.o dummy.o proc.o range.o drivers.o kvmem.o +MIX_OBJS := comedi_ksyms.o + + + +ifeq ($(CONFIG_COMEDI_RTL),y) +MI_OBJS += rtl.o +CFLAGS += -D__RTL__ +endif + +ifeq ($(CONFIG_COMEDI_RTAI),y) +MI_OBJS += rtai.o +endif + +ifeq ($(CONFIG_COMEDI_RTL_V1),y) +MI_OBJS += rtl_v1.o +CFLAGS += -D__RTL__ +endif + +range-y := range.c +range-$(CONFIG_COMEDI_DT2811) += drivers/dt2811.c +range-$(CONFIG_COMEDI_DT2815) += drivers/dt2815.c +range-$(CONFIG_COMEDI_DT282x) += drivers/dt282x.c +range-$(CONFIG_COMEDI_DT3000) += drivers/dt3000.c +range-$(CONFIG_COMEDI_NI_ATMIO) += drivers/ni_mio_common.c +ifeq ($(CONFIG_COMEDI_NI_ATMIO),n) +range-$(CONFIG_COMEDI_NI_PCIMIO) += drivers/ni_mio_common.c +endif + +range-$(CONFIG_COMEDI_PCL711) += drivers/pcl711.c +range-$(CONFIG_COMEDI_RTI800) += drivers/rti800.c +range-$(CONFIG_COMEDI_DAS1600) += drivers/das1600.c + + + + + +include $(TOPDIR)/Rules.make + + +comedi.o: $(MI_OBJS) + $(LD) -r -o $@ $(MI_OBJS) + +range.h: mk_range $(TOPDIR)/.config + -rm -f range.h + set -e ; for i in $(range-y) $(range-m) ; do \ + grep -A 99999 -e '^--BEGIN-RANGE-DEFS--' $$i | \ + grep -B 99999 -e '^---END-RANGE-DEFS---' | \ + grep -v -e 'RANGE-DEFS' ; done >range.def + ./mk_range source range.h + ./mk_range include >range.h + +mk_range: mk_range.c + $(CC) -Wall mk_range.c -o mk_range + +$(MIX_OBJS) $(MI_OBJS): range.h + + diff --git a/comedi/am9513.h b/comedi/am9513.h new file mode 100644 index 00000000..284fb2d6 --- /dev/null +++ b/comedi/am9513.h @@ -0,0 +1,80 @@ +/* + module/am9513.h + value added preprocessor definitions for Am9513 timer chip + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef _AM9513_H_ +#define _AM9513_H_ + +#if 0 + +/* + * Before including this file, the following need to be defined: + */ +#define Am9513_8BITBUS xxx +/* or */ +#define Am9513_16BITBUS xxx + +#define Am9513_output_control(a) xxx +#define Am9513_input_status() xxx +#define Am9513_output_data(a) xxx +#define Am9513_input_data() xxx + +#endif + +/* + * + */ + +#ifdef Am9513_8BITBUS + +#define Am9513_write_register(reg,val) \ + do{ \ + Am9513_output_control(reg); \ + Am9513_output_data(val>>8); \ + Am9513_output_data(val&0xff); \ + }while(0) + +#define Am9513_read_register(reg,val) \ + do{ \ + Am9513_output_control(reg); \ + val=Am9513_input_data()<<8; \ + val|=Am9513_input_data(); \ + }while(0) + +#else /* Am9513_16BITBUS */ + +#define Am9513_write_register(reg,val) \ + do{ \ + Am9513_output_control(reg); \ + Am9513_output_data(val); \ + }while(0) + +#define Am9513_read_register(reg,val) \ + do{ \ + Am9513_output_control(reg); \ + val=Am9513_input_data(); \ + }while(0) + +#endif + +#endif + diff --git a/comedi/comedi_ksyms.c b/comedi/comedi_ksyms.c new file mode 100644 index 00000000..f5d45960 --- /dev/null +++ b/comedi/comedi_ksyms.c @@ -0,0 +1,67 @@ +/* + module/exp_ioctl.c + exported comedi functions + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + 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 + +#include +#include +#include + + + +#ifdef LINUX_V20 + +struct symbol_table comedi_syms = { +#include + 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 +}; + +#endif + +#ifdef LINUX_V22 + +#if 0 +EXPORT_SYMBOL(comedi_trig_ioctl); +EXPORT_SYMBOL(__comedi_trig_ioctl); +EXPORT_SYMBOL(comedi_lock_ioctl); +EXPORT_SYMBOL(comedi_unlock_ioctl); +EXPORT_SYMBOL(comedi_cancel_ioctl); +EXPORT_SYMBOL(comedi_register_callback); +#endif +EXPORT_SYMBOL(comedi_driver_register); +EXPORT_SYMBOL(comedi_driver_unregister); +EXPORT_SYMBOL(comedi_bufcheck); +EXPORT_SYMBOL(comedi_done); +EXPORT_SYMBOL(comedi_error); + +#endif + + + diff --git a/comedi/comedi_module.h b/comedi/comedi_module.h new file mode 100644 index 00000000..b65dfb56 --- /dev/null +++ b/comedi/comedi_module.h @@ -0,0 +1,289 @@ +/* + module/comedi_module.h + header file for kernel-only structures, variables, and constants + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include + + +#include + +#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 +#endif + +#ifdef CONFIG_COMEDI_RTAI +#include +#define rt_printk(format,args...) printk(format,##args) +#define rt_printk_init() +#define rt_printk_cleanup() + +#endif + +#ifdef CONFIG_COMEDI_RTL_V1 +#include +#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 + +#define RANGE_digital RANGE_unipolar5 + +extern unsigned int comedi_max_range; +extern comedi_krange comedi_kranges[]; + + +/* timer types */ +/* these *must* agree with lib/timer.c */ + +#define TIMER_dt282x 1 +#define TIMER_dt2814 2 +#define TIMER_atmio 3 +#define TIMER_acl8112 4 +#define TIMER_nanosec 5 + + +/* some silly little inline functions */ + +static inline int alloc_subdevices(comedi_device *dev) +{ + int size=sizeof(comedi_subdevice)*dev->n_subdevices; + + dev->subdevices=kmalloc(size,GFP_KERNEL); + if(!dev->subdevices) + return -ENOMEM; + memset(dev->subdevices,0,size); + return 0; +} + +static inline int alloc_private(comedi_device *dev,int size) +{ + dev->private=kmalloc(size,GFP_KERNEL); + if(!dev->private) + return -ENOMEM; + memset(dev->private,0,size); + return 0; +} + + +#ifdef LINUX_V20 +#ifdef CONFIG_EXPORT +extern struct symbol_table comedi_syms; +#endif +#endif + + +#endif /* _COMEDI_MODULE_H */ + + + + diff --git a/comedi/drivers.c b/comedi/drivers.c new file mode 100644 index 00000000..b123ea48 --- /dev/null +++ b/comedi/drivers.c @@ -0,0 +1,279 @@ +/* + module/drivers.c + functions for manipulating drivers + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + 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 +#include + +#define __NO_VERSION__ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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;in_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;iattached && 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;in_subdevices;i++){ + s=dev->subdevices+i; + + if(s->type==COMEDI_SUBD_UNUSED) + continue; + + if(s->len_chanlist==0) + s->len_chanlist=1; + + /* XXX */ + if(s->trig[1] || s->trig[2] || s->trig[3] ||s->trig[4]){ + s->prealloc_bufsz=1024*128; + }else{ + s->prealloc_bufsz=0; + } + + if(s->prealloc_bufsz){ + /* XXX */ + s->prealloc_buf=rvmalloc(s->prealloc_bufsz*sizeof(sampl_t)); + if(!s->prealloc_buf){ + printk("ENOMEM\n"); + } + } + + if(!s->range_type && !s->range_type_list) + s->range_type=RANGE_unknown; + } + +} + + +#define REG(x) {extern comedi_driver (x);comedi_driver_register(&(x));} + +void init_drivers(void) +{ + REG(driver_dummy); +#if 0 +#ifdef CONFIG_COMEDI_DT282x + REG(driver_dt282x); +#endif +#ifdef CONFIG_COMEDI_NI_PCIMIO + REG(driver_pcimio); +#endif +#ifdef CONFIG_COMEDI_NI_ATMIO + REG(driver_atmio); +#endif +#ifdef CONFIG_COMEDI_DT2801 + REG(driver_dt2801); +#endif +#ifdef CONFIG_COMEDI_DT2811 + REG(driver_dt2811); +#endif +#ifdef CONFIG_COMEDI_DT2814 + REG(driver_dt2814); +#endif +#ifdef CONFIG_COMEDI_DT2817 + REG(driver_dt2817); +#endif +#ifdef CONFIG_COMEDI_DT3000 + REG(driver_dt3000); +#endif +#ifdef CONFIG_COMEDI_8255 + REG(driver_8255); +#endif +#ifdef CONFIG_NI_PCIDIO + REG(driver_nidio); +#endif +#ifdef CONFIG_COMEDI_DAS08 + REG(driver_das08); +#endif +#ifdef CONFIG_COMEDI_PCL711 + REG(driver_pcl711); +#endif +#ifdef CONFIG_COMEDI_PCL725 + REG(driver_pcl725); +#endif +#ifdef CONFIG_COMEDI_PCL726 + REG(driver_pcl726); +#endif +#ifdef CONFIG_COMEDI_RTI800 + REG(driver_rti800); +#endif +#ifdef CONFIG_COMEDI_RTI802 + REG(driver_rti802); +#endif +#ifdef CONFIG_COMEDI_RTI860 + REG(driver_rti860); +#endif +#ifdef CONFIG_COMEDI_PARPORT + REG(driver_parport); +#endif +#ifdef CONFIG_COMEDI_DAS08JR + REG(driver_das08jr); +#endif +#ifdef CONFIG_COMEDI_DAS1600 + REG(driver_das1600); +#endif +#ifdef CONFIG_COMEDI_DAS6402 + REG(driver_das6402); +#endif +#ifdef CONFIG_COMEDI_MULTIQ3 + REG(driver_multiq3); +#endif +#ifdef CONFIG_COMEDI_DT2815 + REG(driver_dt2815); +#endif +#ifdef CONFIG_COMEDI_DAS16 + REG(driver_das16); +#endif +#endif +} + diff --git a/comedi/drivers/8255.c b/comedi/drivers/8255.c new file mode 100644 index 00000000..2c217550 --- /dev/null +++ b/comedi/drivers/8255.c @@ -0,0 +1,256 @@ +/* + module/8255.c + Driver for 8255 + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#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;in_chan;i++){ + mask=1<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;ioptions[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;in_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;in_subdevices;i++){ + s=dev->subdevices+i; + if(s->type!=COMEDI_SUBD_UNUSED){ + iobase=(int)CALLBACK_ARG; + release_region(iobase,_8255_SIZE); + } + } + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_8255); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_8255); +} +#endif diff --git a/comedi/drivers/8255.h b/comedi/drivers/8255.h new file mode 100644 index 00000000..19c42c7c --- /dev/null +++ b/comedi/drivers/8255.h @@ -0,0 +1,46 @@ +/* + module/8255.h + Header file for 8255 + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + 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 + +#ifdef CONFIG_8255 + +int subdev_8255_init(comedi_device *dev,comedi_subdevice *s,int (*cb)(int,int,int,void *),void *arg); + +#else + +static inline int subdev_8255_init(comedi_device *dev,comedi_subdevice *s,void *x,void *y) +{ + s->type=COMEDI_SUBD_UNUSED; + + return 0; +} + + +#endif + +#endif + diff --git a/comedi/drivers/Makefile b/comedi/drivers/Makefile new file mode 100644 index 00000000..2cdd2939 --- /dev/null +++ b/comedi/drivers/Makefile @@ -0,0 +1,66 @@ + +ALL_SUB_DIRS := +MOD_SUB_DIRS := +SUB_DIRS := +MOD_LIST_NAME := MISC_MODULES + +EXTRA_CFLAGS := -I ../ + +export-objs := mite.o 8255.o + +obj-y := +obj-m := +obj-n := +obj- := + +obj-$(CONFIG_COMEDI_8255) += 8255.o + +obj-$(CONFIG_COMEDI_DAS08) += das08.o +obj-$(CONFIG_COMEDI_DAS08JR) += das08jr.o +obj-$(CONFIG_COMEDI_DAS16) += das16.o +obj-$(CONFIG_COMEDI_DAS1600) += das1600.o +obj-$(CONFIG_COMEDI_DAS6402) += das6402.o + +obj-$(CONFIG_COMEDI_DT2801) += dt2801.o +obj-$(CONFIG_COMEDI_DT2811) += dt2811.o +obj-$(CONFIG_COMEDI_DT2814) += dt2814.o +obj-$(CONFIG_COMEDI_DT2815) += dt2815.o +obj-$(CONFIG_COMEDI_DT2817) += dt2817.o +obj-$(CONFIG_COMEDI_DT282x) += dt282x.o +obj-$(CONFIG_COMEDI_DT3000) += dt3000.o + +obj-$(CONFIG_COMEDI_MULTIQ3) += multiq3.o + +obj-$(CONFIG_COMEDI_NI_ATMIO) += ni_atmio.o +obj-$(CONFIG_COMEDI_NI_PCIMIO) += ni_pcimio.o +obj-$(CONFIG_COMEDI_NI_PCIDIO) += ni_pcidio.o +obj-$(CONFIG_COMEDI_MITE) += mite.o + +obj-$(CONFIG_COMEDI_PCL711) += pcl711.o +obj-$(CONFIG_COMEDI_PCL725) += pcl725.o +obj-$(CONFIG_COMEDI_PCL726) += pcl726.o + +obj-$(CONFIG_COMEDI_RTI800) += rti800.o +obj-$(CONFIG_COMEDI_RTI802) += rti802.o + +obj-m += $(obj-y) + +#L_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) +#LX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +#MI_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) +#MIX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) + + + +include $(TOPDIR)/Rules.make + + +drivers.o: $(obj-y) + $(LD) -r -o $@ $(obj-y) + +ni_pcimio.c: ni_mio_common.c +ni_atmio.c: ni_mio_common.c + + diff --git a/comedi/drivers/das08.c b/comedi/drivers/das08.c new file mode 100644 index 00000000..2be3bf21 --- /dev/null +++ b/comedi/drivers/das08.c @@ -0,0 +1,410 @@ +/* + + DAS-08 adapter + + hack by David Schleef + + mostly borrowed from Warren J. Jasper + + 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 +#include +#include +#include +#include +#include +#include +#include +#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;iiobase=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;iiobase+i)); + } +#endif + dev->n_subdevices=5; + if((ret=alloc_subdevices(dev))<0) + return ret; + if((ret=alloc_private(dev,sizeof(das08_private)))<0) + return ret; + + request_region(dev->iobase,DAS08_SIZE,"das08"); + + devpriv->boardtype=0; + + s=dev->subdevices+0; + /* ai */ + s->type=COMEDI_SUBD_AI; + s->subdev_flags=SDF_READABLE; + s->n_chan=8; + s->maxdata=0xfff; + s->range_type=RANGE_unknown; /* XXX */ + s->trig[0]=das08_ai; + + s=dev->subdevices+1; + /* ao */ + if(this_board.ao_chans>0){ + s->type=COMEDI_SUBD_AO; + s->subdev_flags=SDF_WRITEABLE; + s->n_chan=2; + s->maxdata=0xfff; + s->range_type=RANGE_unknown; /* XXX */ + }else{ + s->type=COMEDI_SUBD_UNUSED; + } + s->trig[0]=das08_ao; + + s=dev->subdevices+2; + subdev_8255_init(dev,s,NULL,(void *)(dev->iobase+DIO_PORTA)); + + s=dev->subdevices+3; + /* ai */ + s->type=COMEDI_SUBD_DI; + s->subdev_flags=SDF_READABLE; + s->n_chan=3; + s->maxdata=1; + s->range_type=RANGE_digital; + s->trig[0]=das08_di; + + s=dev->subdevices+4; + /* ai */ + s->type=COMEDI_SUBD_DO; + s->subdev_flags=SDF_WRITEABLE; + s->n_chan=4; + s->maxdata=1; + s->range_type=RANGE_digital; + s->trig[0]=das08_do; + + devpriv->dio=0; + + + if(this_board.ao_chans>0){ + /* zero AO */ + outb(0,dev->iobase+DA_CHAN0_LSB); + outb(0,dev->iobase+DA_CHAN0_MSB); + outb(0,dev->iobase+DA_CHAN1_LSB); + outb(0,dev->iobase+DA_CHAN1_MSB); + } + + outb_p(0,dev->iobase+STATUS_REG); + +#if 0 + outb_p(0,dev->iobase+DIO_PORTA); + outb_p(0,dev->iobase+DIO_PORTB); + outb_p(0,dev->iobase+DIO_PORTC); +#endif + + printk("\n"); + + return 0; +} + +static int das08_detach(comedi_device *dev) +{ + printk("comedi%d: das08: remove\n",dev->minor); + + release_region(dev->iobase,dev->iosize); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_das08); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_das08); +} +#endif diff --git a/comedi/drivers/das08jr.c b/comedi/drivers/das08jr.c new file mode 100644 index 00000000..f2b1ce1e --- /dev/null +++ b/comedi/drivers/das08jr.c @@ -0,0 +1,329 @@ +/* + The comedi version of this driver was modified from the + original by ds. + + * das08jr + * ======= + * + * Linux device driver for Computer Boards CIO-DAS08/Jr-AO board. + * + * This board has 8 analog input lines and 2 analog output lines. Additionally + * there are 8 digital outputs. + * These lines are mapped onto minor 0 (digital output), 1 - 8 for analog + * input, and 1 + 2 for analog output. + * + * Copyright (C) 1998 Jochen Küpper + * + * $Id$ + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* 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;in_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;in_chan;i++){ + chan=CR_CHAN(it->chanlist[i]); + mask=1<data[i]) + data|=mask; + } + devpriv->last_do=data; + + outb(data, dev->iobase + DAS08JR_DIGITAL_OUT); + + return i; +} + + + +/** + * Read from analog line + * + * @author Jochen Küpper + * @version $Id$ + */ +static int das08jr_ai(comedi_device * dev, comedi_subdevice *s, comedi_trig * it) +{ + unsigned char lsb, msb; + + /* we alway read one short value */ + + /* start daq */ + + /* XXX do something here to trigger it */ + + /* busy waiting for board to finish */ + udelay( DAS08JR_DELAY ); + + /* read data and put it into user space */ + + lsb = inb(DAS08JR_ANALOG_IN_LSB); + msb = inb(DAS08JR_ANALOG_IN_MSB); + + it->data[0] = (((int) msb) << 4) + (lsb >> 4); + + return 1; +} + + +/*------------------------------*/ + +/** + * Write to writeable analog line. + * + * We do not need to check wether this really is an writeable line, since open + * cares about that an we only get called on lines 1 or 2. + * + * We simply put one value to the output ports, start D/A conversion, and return. + * + * @return On success the number of written bytes ( always 2 ), + * a negative error code otherwise. + * + * @author Jochen Küpper + * @version $Id$ + */ +static int das08jr_ao(comedi_device * dev, comedi_subdevice *s, comedi_trig * it) +{ + unsigned short val = it->data[0]; + int chan=CR_CHAN(it->chanlist[0]); + + if (chan){ + outb((unsigned char) val >> 4, DAS08JR_ANALOG_OUT_MSB1); + outb((unsigned char) val << 4, DAS08JR_ANALOG_OUT_LSB1); + } else { + outb((unsigned char) val >> 4, DAS08JR_ANALOG_OUT_MSB0); + outb((unsigned char) val << 4, DAS08JR_ANALOG_OUT_LSB0); + } + + /* what does this do? */ + outb(0xff, DAS08JR_START); + + return 1; +} + + + + +/** + * Register ports, + * + * @author Jochen Küpper + * @version $Id$ + */ +static int das08jr_attach(comedi_device * dev, comedi_devconfig * it) +{ + int result = 0; + int io; + comedi_subdevice *s; + + printk("comedi%d: das08jr: ", dev->minor); + + io = it->options[0]; +#if 0 + if (0 == io) { + /* should autoprobe here */ + io = DAS08JR_PORT; + } +#endif + if ((result = check_region(io, DAS08JR_SIZE)) < 0) { + printk("Cannot register port memory at %x.\n", io); + return result; + } + request_region(io, DAS08JR_SIZE, DAS08JR_NAME); + + dev->board_name = DAS08JR_NAME; + dev->iobase = io; + + printk("Copyright (C) 1998 Jochen K\"upper.\n"); + + dev->n_subdevices = 3; + if((result=alloc_subdevices(dev))<0) + return result; + if((result=alloc_private(dev,sizeof(das08jr_priv)))<0) + return result; + + s = dev->subdevices + 0; + /* ai subdevice */ + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 8; + s->maxdata = 0xfff; + s->range_type = RANGE_unknown; + s->trig[0] = das08jr_ai; + + s = dev->subdevices + 1; + /* ao subdevice */ + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = 2; + s->maxdata = 0xfff; + s->range_type = RANGE_unknown; + s->trig[0] = das08jr_ao; + + s = dev->subdevices + 2; + /* di subdevice */ + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 8; + s->maxdata = 1; + s->range_type = RANGE_digital; + s->trig[0] = das08jr_di; + + s = dev->subdevices + 3; + /* do subdevice */ + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = 8; + s->maxdata = 1; + s->range_type = RANGE_digital; + s->trig[0] = das08jr_do; + + return 0; +} + + +static void release_resources(comedi_device *dev) +{ + if (dev->iobase) + release_region(dev->iobase, DAS08JR_SIZE); +} + +/** + * @author Jochen Küpper + * @version $Id$ + */ +static int das08jr_detach(comedi_device *dev) +{ + printk("comedi%d: das08jr: remove\n", dev->minor); + + release_resources(dev); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_das08jr); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_das08jr); +} +#endif diff --git a/comedi/drivers/das16.c b/comedi/drivers/das16.c new file mode 100644 index 00000000..a4c75861 --- /dev/null +++ b/comedi/drivers/das16.c @@ -0,0 +1,1188 @@ +/* + * Copyright (C) 1995,1996 Sam Moore, Warren Jasper + * 1999 David Schleef + * All rights reserved. + * + * This software may be freely copied, modified, and redistributed + * provided that this copyright notice is preserved on all copies. + * + * There is no warranty or other guarantee of fitness of this software + * for any purpose. It is provided solely "as is". + * + */ +/* + * The comedi version of this driver was modified from the original + * by David Schleef. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +/* + * Note: Interrupts, when enabled, are generated differently depending + * upon the mode of the board: + * Compatibility mode: Interrupts generated every A/D conversion + * from either pacer clock or soft trigger. + * Enhanced mode: Interrupts generated when FIFO half full + * or when Total_Count = 0. + */ + +/* DT Connection is not supported */ + +#define DT_CONNECT FALSE + + +#define AD_CHANNELS 16 /* 16 Single Ended or 8 Diff. */ +#define DIO_CHANNEL 16 /* dio minor channel number */ + +#define PORT_SIZE 0x14 /* uses 20 bytes of registers */ + + +#define LSB_AND_CHNLS 0 /* A/D Data & Channel Register */ +#define MSB_DATA_BYTE 1 +#define MUX_SCAN_LIMITS 2 /* Channel Mux Scan Limits Register */ +#define DIO_REG 3 /* 4 Bit Digital I/O Register */ +#define STATUS_REG 8 /* Status Register */ +#define CNTRL_REG 9 /* DMA, Interrupt & Trigger Control */ +#define PACER_CLOCK_REG 10 /* Pacer Clock Control Register */ +#define MISC_REG 11 /* Analog Input Range Reg., ETC */ + +#define COUNTERA_0_DATA 12 /* Pacer Clock A Counter 0 Register */ +#define COUNTERA_1_DATA 13 /* Pacer Clock A Counter 1 Register */ +#define COUNTERA_2_DATA 14 /* Pacer Clock A Counter 2 Register */ +#define COUNTERA_CONTROL 15 /* Pacer Clock A Control Register */ + +#define COUNTERB_0_DATA 16 /* Pacer Clock B Counter 0 Register */ +#define COUNTERB_1_DATA 17 /* Pacer Clock B Counter 1 Register */ +#define COUNTERB_2_DATA 18 /* Pacer Clock B Counter 2 Register */ +#define COUNTERB_CONTROL 19 /* Pacer Clock B Control Register */ + + +/************************************************************************* +* STATUS_REG base_reg+8 Status Register * +**************************************************************************/ +#define ADSR_INT 0x10 /* INT=1 External pulse received, INT=0 waiting */ +#define ADSR_MUX 0x20 /* MUX=1 16 Single Ended, MUX=0 8 Differential */ +#define ADSR_UB 0x40 /* U/B=1 Unipolar mode, U/B=0 Bipolar mode */ +#define ADSR_EOC 0x80 /* EOC=1, A/D is busy, EOC=0 free */ + +/************************************************************************* +* CNTRL_REG base_reg+9 DMA, Interrupt & Trigger Control * +**************************************************************************/ +#define CNTL_TS0 0x1 /* TS0=0 External Trigger, TS0=1 Pacer Clock */ +#define CNTL_TS1 0x2 /* TS1=0 Software triggered A/D only */ +#define CNTL_DMA 0x4 /* DMA Disabled = 0, DMA Enabled = 1 */ +#define CNTL_INTE 0x80 /* Interrupts Disabled = 0, Enabled = 1 */ + +/************************************************************************* +* MISC_REG base_reg+11 Range, and Exteded Features * +**************************************************************************/ + +#define ENHANCED_BIT (0x10) /* Enhanced Mode Enabled = 1, Disabled = 0 */ +#define MISC_OVERRUN (0x20) /* No overrun = 0, FIFP buffer full = 1 */ +#define MISC_PRETRIG (0x40) /* Pretrigger Enabled = 1, Disable = 0 */ +#define MISC_DT (0x80) /* DT-Connect Enable = 1, Disable = 0 */ + +/************************************************************************* +* Constants for dealing with the 8254 counters * +**************************************************************************/ + +#define MODE0 0x0 +#define MODE1 0x2 +#define MODE2 0x4 +#define MODE3 0x6 +#define MODE4 0x8 +#define MODE5 0xa + +#define C0 0x00 +#define C1 0x40 +#define C2 0x80 + +//#define LATCH 0x00 +#define LSBONLY 0x10 +#define MSBONLY 0x20 +#define LSBFIRST 0x30 + +#define S0 0x00 +#define S1 0x02 + +#define PACKETSIZE 512 +#define MAX_COUNT 16384 +/* Interrupt context data. Controls interrupt service handler */ + +typedef struct{ + unsigned int ir; /* shifted bits for board 0-7, 10->0, 11->1 */ + unsigned int misc_reg; +} das16_private ; +#define devpriv ((das16_private *)dev->private) + +/* Values kept for each channel. */ + +#if 0 +typedef struct CHANNEL_REC { + int open; /* Is channel device open() */ + int adc_dio_pretrig; /* TRUE = write dio before sampling */ + LONG count; /* Number of samples requested */ + BYTE dio_value; /* DIO value to be written before sampling */ + BYTE mode; /* 0 = Soft Trigger */ + /* 2 = External Trigger */ + /* 4 = Pacer Clock */ + /* 6 = Pacer Clock External Trig Enable */ + BYTE gain; /* Voltage range */ + BYTE lowChan; /* Starting Channel of MUX scan limit */ + BYTE hiChan; /* Ending Channel of MUX scan limit */ +} ChanRec; + +#endif + + + + + + +#if 0 +static WORD base_reg = PORT_MIN; /* base register address */ +static int WordsToRead; /* number of conversions until done */ +static ChanRec *CurrChan = NULL; /* pointer to Chan[minor] */ +static int MajorNumber = DEFAULT_MAJOR_DEV; /* Major number compiled in */ +static ChanRec Chan[DIO_CHANNEL + 1]; /* Channel specific information */ +static BoardRec BoardData; /* Board specific information */ +static WORD KernBuff[MAX_COUNT]; /* Kernel buffer where samples are */ + /* saved before write to user space */ + /* structure for timer data */ +static WORD *KernBuffPtr; /* pointer to kernbuf */ +struct wait_queue *adc0_wait; /* wait semaphore */ +struct timer_list TimerList = +{NULL, NULL, 0, 0, adc0_TimerHandler}; +#endif + +int irq_list[]={ -1, -1, 2, 3, 4, 5, 6, 7, -1, -1, 0, 1, -1, -1, -1, -1 }; + +static int compatibility_read(comedi_device *dev,comedi_trig *it); +static void release_resources(comedi_device *dev); + +static int das16_attach(comedi_device *dev,comedi_devconfig *it); +static int das16_detach(comedi_device *dev); +comedi_driver driver_das16={ + driver_name: "das16", + module: &__this_module, + attach: das16_attach, + detach: das16_detach, +}; + + +/* + interrupt routine + + */ + +static void das16_interrupt(int irq,void *d,struct pt_regs *regs) +{ + +} + + +/* + AD subsystem + + */ + +static void set_gain(comedi_device * dev, int gain) +{ + devpriv->misc_reg &= 0xf0; + devpriv->misc_reg |= (gain << 4); + outb_p(devpriv->misc_reg, dev->iobase + MISC_REG); +} + +static int set_channel_mux(comedi_device * dev, int first_chan, int last_chan) +{ + int channel; + + channel = (last_chan << 4) | first_chan; + outb_p(channel, dev->iobase + MUX_SCAN_LIMITS); + + while ((inb_p(dev->iobase + MISC_REG) & MISC_OVERRUN)) + /* spin */; + + return 0; +} + +static int das16_ai_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + int bReg; + + /* Put in Compatibility Mode here just to be safe */ + bReg = inb(dev->iobase + MISC_REG); + outb(bReg & ~ENHANCED_BIT, dev->iobase + MISC_REG); + + /* Set modes for this channel */ + set_gain(dev, CR_RANGE(it->chanlist[0])); + + set_channel_mux(dev, CR_CHAN(it->chanlist[0]), CR_CHAN(it->chanlist[0])); + + compatibility_read(dev,it); + + return 1; +} + + +/* + DIO subsystem + + */ + +static int das16_dio(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + int data; + + data = inb(dev->iobase + DIO_REG); + + return di_unpack(data, it); +} + +/* + AO subsystem + + */ + +/*************************************************************************** + * + * Loads driver. Called when "insmod adc.o" is invoked on the command line. + * The board is set to IRQ, + * + ***************************************************************************/ + +static int das16_attach(comedi_device *dev,comedi_devconfig *it) +{ + comedi_subdevice *s; + int iobase,irq; + int ret=0; + int chan; + + iobase=it->options[0]; + if (check_region(iobase, PORT_SIZE) != 0) { /* in use */ + printk("comedi%d: das16: Can't allocate region port address 0x%x\n", + dev->minor,iobase); + return -EIO; + } + request_region(iobase, PORT_SIZE, "das16"); + dev->iobase=iobase; + +#if 0 + /* we don't really want to search for boards... */ + /* Look to see if ADC is installed */ + for (base_reg = PORT_MIN; base_reg <= PORT_MAX; base_reg += PORT_STEP) { + if (check_region(base_reg, PORT_SIZE) == 0) { /* not in use */ + request_region(base_reg, PORT_SIZE, "adc"); + if (adc_find(base_reg)) { /* found the board */ + BoardData.base = base_reg; + break; + } else { + release_region(base_reg, PORT_SIZE); + } + } + } + + if (base_reg > PORT_MAX) { + printk("%s: No boards found from address %#x to %#x.\n", + ADAPTER_ID, PORT_MIN, PORT_MAX); + if (unregister_chrdev(MajorNumber, "adc") != 0) { + printk("%s: unregister_chrdev() failed.\n", ADAPTER_ID); + } + return -ENODEV; + } +#endif + + /* Register interrupt handler. */ + + irq=it->options[1]; + + if(irq<0 || irq>=16 || irq_list[irq]<0){ + return -EINVAL; + } + if (request_irq(irq, das16_interrupt, SA_INTERRUPT, "das16", dev) == 0) { + return -EIO; + } + dev->irq=irq; + +#if 0 + printk("%s: address=%#x IRQ=%d.", ADAPTER_ID, BoardData.base, BoardData.irq); + printk(" 4/27/95 wjasper@tx.ncsu.edu sam@tx.ncsu.edu\n"); +#endif + + if((ret=alloc_private(dev,sizeof(das16_private)))<0) + return ret; + + dev->n_subdevices=3; + + if((ret=alloc_subdevices(dev))<0) + return ret; + + s=dev->subdevices; + + if (inb(dev->iobase+STATUS_REG) & ADSR_MUX) { + chan = 16; + } else { + chan = 8; + } + /* ai subdevice */ + s->type=COMEDI_SUBD_AI; + s->n_chan=8; + s->trig[0]=das16_ai_mode0; + s->maxdata=0xfff; + + s++; + /* ao subdevice */ + s->type=COMEDI_SUBD_AO; + s->n_chan=2; +#if 0 + s->trig[0]=das16_ao; +#endif + s->maxdata=0xfff; + + s++; + /* dio subdevice */ + s->type=COMEDI_SUBD_DIO; + s->n_chan=8; + s->trig[0]=das16_dio; + s->maxdata=1; + + + /* Set interrupt level on board 2-7, 10->0 and 11->1 */ + devpriv->ir = irq_list[irq] << 4; + + /* ... clearing INTE bit in control reg */ + outb(devpriv->ir, dev->iobase+CNTRL_REG); + + outb(0x0, dev->iobase+MISC_REG); /* Put in compatibility mode */ + + printk("\n"); + + return 0; +} + + +#if 0 +/*************************************************************************** + * + * Test memory locations for exsistance of adc board. + * + ***************************************************************************/ +static int adc_find(WORD base_reg) +{ + +/* + To test if there is a DAS 16/330 board do the following: + 1. write MUX_SCAN_LIMITS register. + 2. read MUX_SCAN_LIMITS register. If fail return FALSE. + 3. read STATUS_REG (bits 0-3). If fail return FALSE. + */ + + BYTE bReg = 0x0; + + bReg = 0xa3; /* set mux from channel 3 to channel 10 */ + outb_p(bReg, MUX_SCAN_LIMITS); /* write to register */ + if (inb_p(MUX_SCAN_LIMITS) != 0xa3) + return FALSE; /* not a 16/330 board */ + if ((inb_p(STATUS_REG) & 0xf) != 0x3) + return FALSE; /* not a 16/330 board */ + bReg = 0xd2; /* set mux from channel 2 to channel 13 */ + outb_p(bReg, MUX_SCAN_LIMITS); /* write to register */ + if (inb_p(MUX_SCAN_LIMITS) != 0xd2) + return FALSE; /* not a 16/330 board */ + if ((inb_p(STATUS_REG) & 0xf) != 0x2) + return FALSE; /* not a 16/330 board */ + return TRUE; /* found it! */ +} +#endif + +/*************************************************************************** + * + * Remove driver. Called when "rmmod adc" is run on the command line. + * + ***************************************************************************/ + +static void release_resources(comedi_device *dev) +{ + if(dev->irq){ + free_irq(dev->irq,dev); + } + + if(dev->iobase) + release_region(dev->iobase, PORT_SIZE); + +} + +static int das16_detach(comedi_device *dev) +{ + release_resources(dev); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_das16); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_das16); +} +#endif + +#if 0 +static int adc_open(struct inode *iNode, struct file *filePtr) +{ + int minor = MINOR(iNode->i_rdev); + + MOD_INC_USE_COUNT; + + /* + check if device is already open: only one process may read from a + port at a time. There is still the possibility of two processes + reading from two different channels messing things up. However, + the overhead to check for this may not be worth it. + */ + + if (Chan[minor].open == TRUE) { + return -EBUSY; + } + Chan[minor].open = TRUE; /* The device is open */ + Chan[minor].gain = BP_10_00V; /* +/- 10V */ + Chan[minor].mode = filePtr->f_flags; /* set trigger mode */ + Chan[minor].lowChan = minor; /* set MUX scan limits to current channel */ + Chan[minor].hiChan = minor; + + if (Chan[minor].mode == ADC_PACER_EXTERN_TRIG) { + BoardData.pacerReg = 0x1; + } else { + BoardData.pacerReg = 0x0; + } + + outb_p(0x00, STATUS_REG); /* Clear INT bit */ + outb_p(0x0, MISC_REG); /* Put in compatibility mode */ + +#ifdef DEBUG + printk("%s: open(): minor %d mode %d.\n", ADAPTER_ID, minor, Chan[minor].mode); +#endif + return 0; +} +#endif + + + +#if 0 +static int adc_read(struct inode *iNode, struct file *filePtr, char *buf, int count) +{ + int stat; + BYTE bReg; + int minor = MINOR(iNode->i_rdev); + register int i; + + /* Read */ + + switch (Chan[minor].mode) { + + case ADC_SOFT_TRIGGER: +#ifdef DEBUG + printk("adc_read(): Entering ADC_SOFT_TRIGGER mode.\n"); +#endif + if (CompatibilityRead(KernBuff, &Chan[minor])) { + printk("adc_read: SoftTrigRead() failed.\n"); + BoardData.busy = FALSE; + return (-1); + } + break; + + case ADC_EXTERNAL_TRIGGER: +#ifdef DEBUG + printk("adc_read(): Entering ADC_EXTERNAL_TRIGGER mode.\n"); +#endif + if (Chan[minor].count == 1) { /* use compatibility mode */ + if (CompatibilityRead(KernBuff, &Chan[minor])) { + printk("adc_read: CompatibilityRead() failed with ADC_EXTERNAL_TRIGGER.\n"); + BoardData.busy = FALSE; + return (-1); + } + } else { + StopPacer(); /* disable pacer if in pacer mode */ + if (EnhancedRead(KernBuff, &Chan[minor])) { + printk("adc_read: ExternalRead() failed with ADC_EXTERNAL_TRIGGER.\n"); + BoardData.busy = FALSE; + return (-1); + } + } + break; + + case ADC_PACER_CLOCK: +#ifdef DEBUG + printk("adc_read(): Entering ADC_PACER_CLOCK mode.\n"); +#endif + if (Chan[minor].count == 1) { /* use compatibility mode */ + if (CompatibilityRead(KernBuff, &Chan[minor])) { + printk("adc_read: CompatibilityRead() failed with pacer.\n"); + BoardData.busy = FALSE; + return (-1); + } + } else { + StopPacer(); /* disable pacer if in pacer mode */ + outb_p(0x1, PACER_CLOCK_REG); /*IP0 controls counter gate */ + if (EnhancedRead(KernBuff, &Chan[minor])) { + printk("adc_read: EnchancedRead() failed.\n"); + BoardData.busy = FALSE; + return (-1); + } + } + break; + + case ADC_PACER_EXTERN_TRIG: +#ifdef DEBUG + printk("adc_read(): Entering ADC_PACER_EXTERN_TRIG mode.\n"); +#endif + StopPacer(); /* disable pacer if in pacer mode */ + outb_p(0x1, PACER_CLOCK_REG); /*IP0 controls counter gate */ + if (EnhancedRead(KernBuff, &Chan[minor])) { + printk("adc_read: EnchancedRead() with external tigger failed.\n"); + BoardData.busy = FALSE; + return (-1); + } + break; + } + + /* Check that data can be written to file. */ + + if ((stat = verify_area(VERIFY_WRITE, buf, sizeof(WORD) * Chan[minor].count)) != 0) { + printk("adc_read: Failed VERIFY_WRITE.\n"); + BoardData.busy = FALSE; + return -1; + } + /* Write data to user space */ + + for (i = 0; i < Chan[minor].count; i++) { + KernBuff[i] >>= 4; + } + + if (Chan[minor].count == 1) { + put_fs_word(*KernBuff, (WORD *) buf); + } else { + memcpy_tofs(buf, KernBuff, Chan[minor].count * sizeof(WORD)); + } + + BoardData.busy = FALSE; + return (Chan[minor].count); /* return number of samples read */ +} +#endif + + + + +#if 0 +static int dio_write(struct inode *iNode, struct file *filePtr, char *buf, int count) +{ + int minor = MINOR(iNode->i_rdev); + BYTE value; + + if (minor != DIO_CHANNEL) { + printk("dio_write: bad minor number = %d\n", minor); + return -1; + } + cli(); + value = get_fs_byte(buf) & 0xf; + outb_p(value, DIO_REG); + sti(); +#ifdef DEBUG + printk("DIO set to %#x\n", value & 0xf); +#endif + return 1; +} +#endif + + +#if 0 +static int adc_ioctl(struct inode *iNode, struct file *filePtr, unsigned int cmd, LONG arg) +{ + int minor = MINOR(iNode->i_rdev); + + switch (cmd) { + case ADC_SET_GAINS: + Chan[minor].gain = (BYTE) arg; + break; + case ADC_GET_GAINS: + put_fs_long((long) Chan[minor].gain, (long *) arg); + break; + case ADC_SET_PACER_FREQ: + if (BoardData.freq > MAX_FREQ && Chan[minor].mode == ADC_PACER_CLOCK) { + printk("ioctl: BoardData.freq = %ld out of range.\n", BoardData.freq); + return -1; + } else { + BoardData.freq = (LONG) arg; + SetPacerFreq(&BoardData); + LoadPacer(&BoardData); /* load the board frequency now */ + outb_p(BoardData.pacerReg, PACER_CLOCK_REG); + } + break; + case ADC_GET_PACER_FREQ: + put_fs_long(BoardData.freq, (long *) arg); + break; + case ADC_STOP_PACER: + StopPacer(); + break; + case CTR0: + if (arg == 1) { + BoardData.pacerReg |= 0x2; + } else { + BoardData.pacerReg &= ~(0x2); + } + outb_p(BoardData.pacerReg, PACER_CLOCK_REG); + break; + case COUNTER0: + LoadCounter0((LONG) arg); + break; + case ADC_DIO_PRESET: + if (arg == -1) { + Chan[minor].adc_dio_pretrig = FALSE; + } else { + Chan[minor].adc_dio_pretrig = TRUE; + Chan[minor].dio_value = (BYTE) (arg & 0xf); + } + +#ifdef DEBUG + printk("ioctl(ADC_DIO_PRESET): value = %#x\n", Chan[minor].dio_value); +#endif + + break; + case ADC_GET_STATUS: + /* return status register */ + put_fs_long(inb_p(STATUS_REG), (long *) arg); + break; + case SET_MUX_LOW: + Chan[minor].lowChan = (BYTE) arg; + break; + case SET_MUX_HIGH: + Chan[minor].hiChan = (BYTE) arg; + break; + case GET_MUX_SCAN_LIMITS: + put_fs_long(inb_p(MUX_SCAN_LIMITS), (long *) arg); + break; + default: + return (-EINVAL); + break; + } + return 0; +} +#endif + +/*************************************************************************** + * + * Block copy of data samples from data register to kernel memory. + * + ***************************************************************************/ + +#if 0 +inline char * + RegCopy(char *dest, const int reg, int n) +{ + __asm__ __volatile__( + "cld\n\t" + "rep\n\t" + "insw" + : + :"D"(dest), "d"(reg), "c"(n) + :"di", "dx", "cx"); + return dest; +} +#endif + +/*************************************************************************** + * + * Alarm handler to handle situations where an interrupt is missed. + * + ***************************************************************************/ + +#if 0 +static void adc0_TimerHandler(LONG unused) +{ + /* + we should only be here if an interrupt occured while + we were reading half the FIFO + */ + +#ifdef DEBUG + printk("TimerHandler: WordsToRead = %d\n", WordsToRead); +#endif + + cli(); + outb_p(0x30, COUNTERB_CONTROL); /* Set TOTALBAR to 1 */ + RegCopy((char *) KernBuffPtr, base_reg, WordsToRead); + WordsToRead -= WordsToRead; + del_timer(&TimerList); + wake_up_interruptible(&adc0_wait); +} +#endif + +/*************************************************************************** + * + * Interrupt handler used to service enhanced mode(interrupt) read(). + * + ***************************************************************************/ + +#if 0 +static void adc0_ReadInterrupt(int irq) +{ + WORD msb, lsb; + +#ifdef DEBUG + printk("Entering adc0_ReadInterrupt(). mode = %d\n", CurrChan->mode); +#endif + + cli(); + switch (CurrChan->mode) { + + case ADC_SOFT_TRIGGER: + /* Bang! Bang! Method: Force conversion, Conversion forces interrupt */ + lsb = (WORD) inb(base_reg); /* Sample: ADC -> kernel buffer */ + msb = (WORD) inb(MSB_DATA_BYTE); + *KernBuffPtr++ = (msb << 8) | lsb; + WordsToRead--; /* One sample at a time */ + if (!WordsToRead) { + wake_up_interruptible(&adc0_wait); + } else { + outb_p(0x00, STATUS_REG); /* Clear INT bit, allow Interrupt */ + outb_p(0x1, base_reg); /* Force conversion */ + sti(); + } + break; + + case ADC_PACER_CLOCK: + case ADC_EXTERNAL_TRIGGER: + if (CurrChan->count == 1) { + lsb = (WORD) inb(base_reg); /* Sample: ADC -> kernel buffer */ + msb = (WORD) inb(MSB_DATA_BYTE); + *KernBuffPtr++ = (msb << 8) | lsb; + wake_up_interruptible(&adc0_wait); + break; + } + case ADC_PACER_EXTERN_TRIG: + if (WordsToRead > PACKETSIZE) { + RegCopy((char *) KernBuffPtr, base_reg, PACKETSIZE); + WordsToRead -= PACKETSIZE; + KernBuffPtr += PACKETSIZE; + if (WordsToRead < PACKETSIZE) { + TimerList.expires = WordsToRead * 100 / BoardData.freq + 2; + add_timer(&TimerList); + } + outb_p(0x00, STATUS_REG); /* Clear INT bit, allow Interrupt */ + sti(); + } else { + outb_p(0x30, COUNTERB_CONTROL); /* Set TOTALBAR to 1 */ + RegCopy((char *) KernBuffPtr, base_reg, WordsToRead); + del_timer(&TimerList); + WordsToRead -= WordsToRead; + wake_up_interruptible(&adc0_wait); + return; + } + break; + default: + break; + } +} +#endif + +/*************************************************************************** + * + * Handles software/pacer triggered read(). + * + * Bang! Bang! Method: + * + * o Runs in Compatibility Mode + * o Force interrupt by forcing conversion. + * o Get one sample at a time and put it in the kernel buffer(kernBuff). + * o Get chan->count samples + * + ***************************************************************************/ + +static int compatibility_read(comedi_device *dev,comedi_trig *it) +{ + int bReg; + +#ifdef DEBUG + printk("Entering CompatibilityRead().\n"); +#endif + + /* Set interrupt level on board, disable interrupts, and pacer */ + + outb(devpriv->ir, dev->iobase + CNTRL_REG); /* & clear INTE bit in control reg */ + + bReg = inb(dev->iobase+MISC_REG); /* put in compatibility mode */ + outb(bReg & ~ENHANCED_BIT, MISC_REG); + + bReg = inb(dev->iobase + MUX_SCAN_LIMITS); /* Read/Write clears FIFO */ + outb(bReg, dev->iobase + MUX_SCAN_LIMITS); + +#if 0 + /* Prepare global data for parameterless interrupt handler */ + + CurrChan = chan; /* pass chanel number to global */ + KernBuffPtr = KernBuff; /* same with KernBuff */ + WordsToRead = chan->count; + + bReg = inb(CNTRL_REG); /* Turns on ADC interrupts */ + + switch (chan->mode) { + + case ADC_SOFT_TRIGGER: + outb(bReg | CNTL_INTE, CNTRL_REG); + outb(0x00, STATUS_REG); /* Clears INT bit, allow Int. */ + outb(0x1, base_reg); /* Force first conversion */ + break; + + case ADC_EXTERNAL_TRIGGER: + outb(bReg | CNTL_INTE | 0x2, CNTRL_REG); + outb(0x00, STATUS_REG); /* Clears INT bit, allow Int. */ + break; + + case ADC_PACER_CLOCK: + outb(bReg | CNTL_INTE | 0x3, CNTRL_REG); + outb(0x00, STATUS_REG); /* Clears INT bit, allow Int. */ + break; + } + +#endif + outb(devpriv->ir, dev->iobase+CNTRL_REG); /* & clear INTE bit in control reg */ + + return 0; +} + + +/**************************************************************************** + * Set Pacer Clock Frequency + * + * Description: + * Set the counters so that the pacer clock runs at the + * desired frequency. The frequency is generated by dividing + * down a 10 MHz clock, so all frequencies can not be generated. + * This routine calculated the divisor to generate a frequency + * as near as possible to the requested one. It then calculates + * the real frequency and returns it . + ****************************************************************************/ + +#if 0 +static int SetPacerFreq(BoardRec * board) +{ + short unsigned ctr1, ctr2; + LONG product, error; + + /* divide 10Mhz by frequency */ + product = 10000000.0 / board->freq + 0.5; + + /* Now the job is to find two 16 bit numbers, that when multiplied + together are approximately equal to product. Start by setting + one of them, ctr1 to 2 (minimum settable value) and increment until + the error is minimized and ctr2 is less than 32768. + + NOTE: In Mode 2, a value of 1 is illegal! Therefore, crt1 and crt2 + can never be 1. + + */ + + ctr1 = product / 32768; + if (ctr1 < 2) + ctr1 = 2; + ctr2 = product / ctr1; + error = abs(product - (long) ctr2 * (long) ctr1); + + while (error && ctr1 < 32768 && ctr2 > 1) { + ctr1++; + ctr2 = product / ctr1; + error = abs(product - (long) ctr2 * (long) ctr1); + } + + /* the frequency is prime, add 1 to it */ + if (error) { + product++; + ctr1 = product / 32768; + if (ctr1 < 2) + ctr1 = 2; + ctr2 = product / ctr1; + error = abs(product - (long) ctr2 * (long) ctr1); + + while (error && ctr1 < 32768 && ctr2 > 1) { + ctr1++; + ctr2 = product / ctr1; + error = abs(product - (long) ctr2 * (long) ctr1); + } + } + /* we can't have ctr2 equal to 1, or system hangs */ + if (ctr2 == 1) { + ctr2++; + ctr1 /= 2; + } + board->ctr1 = ctr1; + board->ctr2 = ctr2; + board->freq = 10000000.0 / ((long) ctr1 * (long) ctr2) + 0.5; + +#ifdef DEBUG + printk("SetPacerFreq: Pacer Register set to %#x\n", BoardData.pacerReg); +#endif + + return 0; +} +#endif + +/*************************************************************************** + * + * Load two part frequency to pacer counter chip. + * + ***************************************************************************/ + +static void load_pacer(comedi_device *dev,int timer) +{ + unsigned int mask; + unsigned int ctr1,ctr2; + + /* Write the values of ctr1 and ctr2 into counter A1 and A2 */ + + ctr1=timer&0xffff; + ctr2=(timer>>16)&0xffff; + + mask = C2 | MODE2 | LSBFIRST; + outb(mask, dev->iobase+COUNTERA_CONTROL); + + outb(ctr2 & 0xff , dev->iobase + COUNTERA_2_DATA); + outb(ctr2 >> 8, dev->iobase + COUNTERA_2_DATA); + + mask = C1 | MODE2 | LSBFIRST; + outb(mask, dev->iobase+COUNTERA_CONTROL); + + outb(ctr1 & 0xff, dev->iobase + COUNTERA_1_DATA); + outb(ctr1 >> 8, dev->iobase + COUNTERA_1_DATA); + +} + +/*************************************************************************** + * + * Load value into Counter 0 XXXX Mode MSB LSB + * Byte 3 Byte 2 Byte 1 Byte 0 + * + ***************************************************************************/ + +static void load_counter(comedi_device *dev,int mode,int value) +{ + unsigned int mask; + + /* Write the value into Counter 0 Mode 2 */ + + /* the mode is in the thrid byte */ + mask = (0xff & mode) | C0 | LSBFIRST; + outb(mask, dev->iobase + COUNTERA_CONTROL); + +#if 0 + if (mode & 0xff00) /* load control word only */ + return; +#endif + outb(value & 0xff, dev->iobase+COUNTERA_0_DATA); + outb((value>>8)&0xff, dev->iobase+COUNTERA_0_DATA); +} + + +/*************************************************************************** + * + * Turn off pacer timer chip. + * + ***************************************************************************/ + +static void stop_pacer(comedi_device *dev) +{ + unsigned int mask; + + mask = C2 | MODE2 | LSBFIRST; + outb(mask, dev->iobase + COUNTERA_CONTROL); + mask = C1 | MODE2 | LSBFIRST; + outb(mask, dev->iobase + COUNTERA_CONTROL); +} + +/*************************************************************************** + * + * Handles + * o pacer/counter triggered read() with software start. + * o pacer/counter triggered read() with external start. + * o external triggered read(). + * + * Pacer Method: + * + * o Runs in CIO-DAS 16/330 Enhanced Mode + * o Pacer counter controls frequency of sample conversions + * o Total counter controls number of samples taken + * o Interrupts occur every 512(PACKETSIZE) samples and at last sample + * o Get chan->count samples + * o Samples are saved in kernel buffer(kernBuff) in readInterrupt() + * + ***************************************************************************/ + +#if 0 +static int EnhancedRead(WORD * kernBuff, ChanRec * chan) +{ + BYTE bReg = 0x0; + WORD wReg = 0x0; + +#ifdef DEBUG + printk("Entering EnchacedRead().\n"); +#endif + + cli(); + + /* Set interrupt level on board, disable interrupts, and pacer */ + + outb_p(BoardData.ir, CNTRL_REG); /* & clear INTE bit in control reg */ + + /* + There is some confusion exactly what is going on, but if bit 5 in reg+11 + is equal to 1, the FIFO is over run and we can not write to reg+2, + or what I call MUX_SCAN_LIMITS. So I will try to clear bit 5 + and keep reading until the FIFO is not full. This should not + happen, but Robert Wilhelm (robert@haegar.physiol.med.tu-muenchen.de) + claims that it is. + */ + + /* clear Overrun status bit (bit 5 in reg+11) */ + bReg = (BYTE) inb_p(MISC_REG) & (~MISC_OVERRUN); + outb_p(bReg, MISC_REG); + + do { + bReg = (BYTE) inb_p(MUX_SCAN_LIMITS); /* Read/Write clears FIFO */ + outb_p(bReg, MUX_SCAN_LIMITS); + } + while (((BYTE) inb_p(MISC_REG) & MISC_OVERRUN)); + + bReg = chan->gain | ENHANCED_BIT; /* Set gain and enhanced mode */ + outb(bReg, MISC_REG); + bReg = (BYTE) inb_p(MISC_REG); /* check to make sure we are in */ + if (!(bReg & ENHANCED_BIT)) { /* enhanced mode */ + return (NO_ENHANCED_MODE); + } + do { + bReg = (BYTE) inb_p(MUX_SCAN_LIMITS); /* Read/Write clears FIFO */ + outb_p(bReg, MUX_SCAN_LIMITS); + } + while (((BYTE) inb_p(MISC_REG) & MISC_OVERRUN)); + + if (DT_CONNECT) { + bReg = (BYTE) inb_p(MISC_REG); + bReg |= MISC_DT; + outb_p(bReg, MISC_REG); + } + /* load the Total Counter for the DAS 16/330 */ + + bReg = C0 + MODE0 + LSBFIRST; /* CounterB -> mode 0, DON'T load it */ + outb_p(bReg, COUNTERB_CONTROL); /* Sets output HIGH? */ + + bReg = (BYTE) inb_p(CNTRL_REG); /* S1=0 and S0=0 from above */ + if (chan->mode == ADC_PACER_CLOCK || chan->mode == ADC_PACER_EXTERN_TRIG) { + bReg |= (0x3); /* Set S1=1 and S0=1 Internal Pacer */ + } else { + bReg |= (0x2); /* Set S1=1 and S0=0 External Pacer (A/D Triggering) */ + } + outb_p(bReg, CNTRL_REG); + + wReg = (WORD) (chan->count >> 16); /* get upper word of count */ + if (chan->count & 0xffff) + wReg++; /* increment if lower count is ! zero */ + + bReg = C0 + MODE0 + LSBFIRST; /* Counter -> mode 0, DON'T load it */ + outb_p(bReg, COUNTERB_CONTROL); /* Sets output HIGH? */ + bReg = (BYTE) (wReg & 0xff); /* load the upper word */ + outb_p(bReg, COUNTERB_0_DATA); + bReg = (BYTE) (wReg >> 8); + outb_p(bReg, COUNTERB_0_DATA); + + /* Set the trigger counters. Counter 0 is changed to counter 1 */ + + bReg = C1 + MODE2 + LSBFIRST; /* Ensure clock starts low */ + outb_p(bReg, COUNTERB_CONTROL); + + bReg = C1 + MODE0 + LSBFIRST; /* Make C0 clock high */ + outb_p(bReg, COUNTERB_CONTROL); + + bReg = C1 + MODE2 + LSBFIRST; /* Make C0 clock low */ + outb_p(bReg, COUNTERB_CONTROL); /* complete 1st clock cycle */ + + wReg = (WORD) (chan->count & 0xffff); + + if (wReg == 1) { + wReg++; /* wReg can not be 1 */ + bReg = (BYTE) (wReg & 0xff); /* load the lower word */ + outb_p(bReg, COUNTERB_1_DATA); + bReg = (BYTE) (wReg >> 8); + outb_p(bReg, COUNTERB_1_DATA); + outb_p(0x1, base_reg); /* Force conversion to get count = 1 */ + } else { + bReg = (BYTE) (wReg & 0xff); /* load the lower word */ + outb_p(bReg, COUNTERB_1_DATA); + bReg = (BYTE) (wReg >> 8); + outb_p(bReg, COUNTERB_1_DATA); + } + outb_p(0x1, base_reg); /* Force a conversion */ + outw_p(0x0, COUNTERB_1_DATA); /* rollover mode ?? */ + + if (chan->mode == ADC_PACER_CLOCK || chan->mode == ADC_PACER_EXTERN_TRIG) { + LoadPacer(&BoardData); /* Establish sample frequency */ + } + do { + bReg = (BYTE) inb_p(MUX_SCAN_LIMITS); /* Read/Write clears FIFO */ + outb_p(bReg, MUX_SCAN_LIMITS); + } + while (((BYTE) inb_p(MISC_REG) & MISC_OVERRUN)); + + outb_p(0x00, STATUS_REG); /* Clear INT bit, allow Interrupt */ + + /* Prepare global data for parameterless interrupt handler */ + + CurrChan = chan; /* pass chanel number to global */ + KernBuffPtr = KernBuff; /* same with KernBuff */ + WordsToRead = chan->count; + + bReg = (BYTE) inb_p(CNTRL_REG); /* Enable interrupts */ + bReg |= CNTL_INTE; + outb_p(bReg, CNTRL_REG); + + if (chan->mode == ADC_PACER_CLOCK || chan->mode == ADC_PACER_EXTERN_TRIG) { + outb_p(BoardData.pacerReg, PACER_CLOCK_REG); /* Start pacer begins sampling. */ + } + if (chan->adc_dio_pretrig) { + /* enable interrupts before setting dio bits */ + sti(); + outb_p(chan->dio_value, DIO_REG); + } + interruptible_sleep_on(&adc0_wait); /* Block in wait state */ + + /* turn off interrupts, and trigger control. Leave pacer clock on if enabled */ + outb_p(0x0, CNTRL_REG); + + if (WordsToRead != 0) { + printk("Timing error in EnchacedRead: WordsToRead = %d\n", WordsToRead); + return -1; + } + outb_p(0x00, STATUS_REG); /* Clear INT bit */ + return 0; +} +#endif diff --git a/comedi/drivers/das1600.c b/comedi/drivers/das1600.c new file mode 100644 index 00000000..7a92a624 --- /dev/null +++ b/comedi/drivers/das1600.c @@ -0,0 +1,452 @@ +/* + module/das1600.c + hardware driver for Keithley Metrabyte DAS1600 and compatibles + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1999 Anders Blomdell + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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;in_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;in_chan;i++){ + chan=CR_CHAN(it->chanlist[i]); + mask=1<data[i]) + data |=mask; + } + devpriv->last_do=data; + + /* why are outputs inverted? dumb... */ + outb(data ^ 0xff, dev->iobase + DAS1600_DO); + + return i; +} + +#endif + + +/* + options[0] Board base address + options[1] IRQ + options[2] DMA level select configuration + 0 == no DMA + 1 == DMA level 1 + 3 == DMA level 3 + options[3] Crystal select configuration + 0 == 10 MHz + 1 == 1 MHz + 10 == 10 MHz + options[4] Input configuration + 0 == differential + 1 == single-ended + options[5] Analog input range configuration + 0 == bipolar 10V (-10V -- +10V) + 1 == unipolar 10V (0V -- +10V) + options[6] Analog output 0 range configuration + 0 == bipolar 10V (-10V -- +10V) + 1 == bipolar 5V (-5V -- +5V) + 2 == bipolar user supplied (-xV -- +xV) + 3 == unipolar 10V (0V -- +10V) + 4 == unipolar 5V (0V -- +5V) + 5 == unipolar user supplied (0V -- +xV) + options[7] Analog output 1 range configuration + 0 == bipolar 10V (-10V -- +10V) + 1 == bipolar 5V (-5V -- +5V) + 2 == bipolar user supplied (-xV -- +xV) + 3 == unipolar 10V (0V -- +10V) + 4 == unipolar 5V (0V -- +5V) + 5 == unipolar user supplied (0V -- +xV) + */ +static int das1600_recognize(char *name) +{ + if (!strcmp("das1601/12", name)) return card_1601_12; + if (!strcmp("das1602/12", name)) return card_1602_12; + if (!strcmp("das1602/16", name)) return card_1602_16; + + return -1; +} + +static int das1600_attach(comedi_device * dev, comedi_devconfig * it) +{ + int result = 1; + int board = -1; + comedi_subdevice *s; + + dev->iobase = it->options[0]; + + printk("comedi%d: das1600: 0x%04x ", dev->minor, dev->iobase); + if (check_region(dev->iobase, DAS1600_BASE_SIZE) < 0 || + check_region(dev->iobase + 0x400, DAS1600_DIO_SIZE) < 0) { + printk("I/O port conflict\n"); + return -EIO; + } + request_region(dev->iobase, DAS1600_BASE_SIZE, "das1600"); + request_region(dev->iobase + 0x400, DAS1600_DIO_SIZE, "das1600"); + + if (board == card_1601_12) { + dev->board_name = "das1601/12"; + } else if (board == card_1602_12) { + dev->board_name = "das1602/12"; + } else if (board == card_1602_16) { + dev->board_name = "das1602/16"; + } + + dev->n_subdevices=5; + + if((result=alloc_private(dev,sizeof(das1600_private)))<0) + return result; + if((result=alloc_subdevices(dev))<0) + return result; + + devpriv->card = board; + devpriv->dma = it->options[2]; + devpriv->crystal = it->options[3]; + devpriv->adc_mux = (it->options[4] == 1) ? adc_singleended : adc_diff; + devpriv->adc_range = it->options[5]; + devpriv->dac_range[0] = it->options[6]; + devpriv->dac_range[1] = it->options[7]; + + s = dev->subdevices + 0; + /* ai subdevice */ + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE; + s->n_chan = (devpriv->adc_mux == adc_singleended) ? 16 : 8; + s->maxdata = (board == card_1602_16) ? 0xffff : 0xfff; + s->timer_type = 0; /* XXX need timer */ + s->trig[0] = das1600_ai; + switch (board) { + case card_1601_12: + switch (devpriv->adc_range) { + case adc_bipolar10: + s->range_type = RANGE_das1601_ai_10_bipolar; + break; + case adc_unipolar10: + s->range_type = RANGE_das1601_ai_10_unipolar; + break; + }; + break; + case card_1602_12: + case card_1602_16: + switch (devpriv->adc_range) { + case adc_bipolar10: + s->range_type = RANGE_das1602_ai_10_bipolar; + break; + case adc_unipolar10: + s->range_type = RANGE_das1602_ai_10_unipolar; + break; + }; + break; + } + + s++; + /* ao subdevice */ + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = 2; + s->maxdata = 0xfff; + s->trig[0] = das1600_ao; + s->range_type_list = devpriv->range_type_list; + devpriv->range_type_list[0] = range_types[devpriv->dac_range[0]]; + devpriv->range_type_list[1] = range_types[devpriv->dac_range[1]]; + + s++; + /* di subdevice */ + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE; + s->trig[0] = das1600_di; + s->n_chan = 4; + s->maxdata = 1; + s->range_type = RANGE_digital; + + s++; + /* do subdevice */ + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITEABLE; + s->trig[0] = das1600_do; + s->n_chan = 4; + s->maxdata = 1; + s->range_type = RANGE_digital; + + s++; + /* 8255 subdevice */ + subdev_8255_init(dev,s,NULL,(void *)(dev->iobase+DAS1600_Port_A)); + + printk("\n"); + return 0; +} + +static void das1600_release_resources(comedi_device * dev) +{ + if (dev->iobase) { + release_region(dev->iobase, DAS1600_BASE_SIZE); + release_region(dev->iobase + 0x400, DAS1600_DIO_SIZE); + } + if (dev->irq) { + free_irq(dev->irq, dev); + } +} + +static int das1600_detach(comedi_device * dev) +{ + printk("comedi%d: das1600: remove\n", dev->minor); + + das1600_release_resources(dev); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_das1600); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_das1600); +} +#endif diff --git a/comedi/drivers/das6402.c b/comedi/drivers/das6402.c new file mode 100644 index 00000000..89081dd9 --- /dev/null +++ b/comedi/drivers/das6402.c @@ -0,0 +1,385 @@ +/* + Some comments on the code.. + + - it shouldn't be necessary to use outb_p(). + + - ignoreirq creates a race condition. It needs to be fixed. + + + */ + +/* + An experimental driver for Computerboards' DAS6402 I/O card + + Copyright (C) 1999 Oystein Svendsen + + 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 +#include +#include +#include +#include +#include +#include + +#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;iiobase); +} +#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;iiobase+8)&0x01)) + return; + *data=inw(dev->iobase); + data++; + s->buf_int_ptr+=sizeof(sampl_t); + s->buf_int_count+=sizeof(sampl_t); + } + s->buf_int_ptr=0; + data=s->cur_trig.data; + comedi_eobuf(dev,s); + } +#if 0 + if (n>1024) { + printk("das6402: Someting is very wrong with the card fifo!\n"); + /* + * Not really... Additional samples might be placed into the + * fifo after the interrupt, but before the last one is read. + */ + } +#endif +} + +static int das6402_ai_cancel(comedi_device *dev,comedi_subdevice *s) +{ + /* + * This function should reset the board from whatever condition it + * is in (i.e., acquiring data), to a non-active state. + */ + + devpriv->das6402_ignoreirq=1; +#ifdef DEBUG + printk("das6402: Stopping acquisition\n"); +#endif + devpriv->das6402_ignoreirq=1; + outb_p(0x02,dev->iobase+10); /* disable external trigging */ + outw_p(SCANL,dev->iobase+2); /* resets the card fifo */ + outb_p(0,dev->iobase+9); /* disables interrupts */ + + outw_p(SCANL,dev->iobase+2); + + return 0; +} + +static int das6402_ai_mode2(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + devpriv->das6402_ignoreirq=1; + +#ifdef DEBUG + printk("das6402: Starting acquisition\n"); +#endif + outb_p(0x03,dev->iobase+10); /* enable external trigging */ + outw_p(SCANL,dev->iobase+2); /* resets the card fifo */ + outb_p(IRQ|CONVSRC|BURSTEN|INTE,dev->iobase+9); + + devpriv->ai_bytes_to_read=it->n*sizeof(sampl_t); + + /* um... ignoreirq is a nasty race condition */ + devpriv->das6402_ignoreirq=0; + + outw_p(SCANL,dev->iobase+2); + + return 0; +} + + +static int board_init(comedi_device *dev) +{ + BYTE b; + + devpriv->das6402_ignoreirq=1; + + outb(0x07,dev->iobase+8); + + /* register 11 */ + outb_p(MODE,dev->iobase+11); + b=BIP|SEM|MODE|GAIN|FIFOHFULL; + outb_p(b, dev->iobase+11); + + /* register 8 */ + outb_p(EXTEND,dev->iobase+8); + b=EXTEND|MHZ; + outb_p(b,dev->iobase+8); + b= MHZ|CLRINT|CLRXTR|CLRXIN; + outb_p(b,dev->iobase+8); + + /* register 9 */ + b=IRQ|CONVSRC|BURSTEN|INTE; + outb_p(b,dev->iobase+9); + + /* register 10 */ + b=TGSEL|TGEN; + outb_p(b,dev->iobase+10); + + b=0x07; + outb_p(b,dev->iobase+8); + + das6402_setcounter(dev); + + outw_p(SCANL,dev->iobase+2); /* reset card fifo */ + + devpriv->das6402_ignoreirq=0; + + return 0; +} + +static int das6402_detach(comedi_device *dev) +{ + if(dev->irq)free_irq(dev->irq,dev); + if(dev->iobase)release_region(dev->iobase,DAS6402_SIZE); + + return 0; +} + +static int das6402_attach(comedi_device *dev,comedi_devconfig *it) +{ + int irq; + int iobase; + int ret; + comedi_subdevice *s; + + dev->board_name="das6402"; + + iobase=it->options[0]; + if(iobase==0)iobase=0x300; + + printk("comedi%d: das6402: 0x%04x",dev->minor,iobase); + + if(check_region(iobase,DAS6402_SIZE)<0){ + printk(" I/O port conflict\n"); + return -EIO; + } + dev->iobase=iobase; + request_region(dev->iobase,DAS6402_SIZE,"das6402"); + + /* should do a probe here */ + + irq=it->options[0]; + printk(" ( irq = %d )", irq); + ret=request_irq(irq, intr_handler, 0, "das6402", dev); + if(ret<0){ + printk("irq conflict\n"); + return ret; + } + dev->irq=irq; + + if((ret=alloc_private(dev,sizeof(das6402_private)))<0) + return ret; + + dev->n_subdevices=1; + if((ret=alloc_subdevices(dev))<0) + return ret; + + /* ai subdevice */ + s=dev->subdevices+0; + s->type=COMEDI_SUBD_AI; + s->subdev_flags=SDF_READABLE; + s->n_chan=8; + s->trig[2]=das6402_ai_mode2; + s->cancel=das6402_ai_cancel; + s->maxdata=(1<<12)-1; + s->len_chanlist=16; /* ? */ + s->range_type = RANGE_unknown; + s->timer_type = TIMER_nanosec; + + board_init(dev); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_das6402); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_das6402); +} +#endif diff --git a/comedi/drivers/dt2801.c b/comedi/drivers/dt2801.c new file mode 100644 index 00000000..5837ba18 --- /dev/null +++ b/comedi/drivers/dt2801.c @@ -0,0 +1,465 @@ +/* + * Device Driver for DataTranslation DT2801 + * + */ + +#include +#include +#include +#include +#include + +#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;typen_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<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<range_type_list=devpriv->dac_range_types; + devpriv->dac_range_types[0]=dac_range_lkup(it->options[3],it->options[4]); + devpriv->dac_range_types[1]=dac_range_lkup(it->options[5],it->options[6]); + s->trig[0]=dt2801_ao_mode0; + + s++; + /* 1st digital subdevice */ + s->type=COMEDI_SUBD_DIO; + s->subdev_flags=SDF_READABLE|SDF_WRITEABLE; + s->n_chan=8; + s->maxdata=1; + s->range_type=RANGE_digital; + s->trig[0]=dt2801_dio; + + s++; + /* 2nd digital subdevice */ + s->type=COMEDI_SUBD_DIO; + s->subdev_flags=SDF_READABLE|SDF_WRITEABLE; + s->n_chan=8; + s->maxdata=1; + s->range_type=RANGE_digital; + s->trig[0]=dt2801_dio; + + return 0; +} + +static int dt2801_detach(comedi_device *dev) +{ + if(dev->iobase) + release_region(dev->iobase,DT2801_IOSIZE); + + return 0; +} + +static int dt2801_ai_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + int data; + int stat; + + + stat = dt2801_writecmd(dev, DT_C_READ_ADIM); + dt2801_writedata(dev, CR_RANGE(it->chanlist[0])); + dt2801_writedata(dev, CR_CHAN(it->chanlist[0])); + stat = dt2801_readdata2(dev, &data); + + if (stat != 0) { + printk("dt2801: stat = %x\n", stat); + return -EIO; + } + + it->data[0]=data; + + return 1; +} + +static int dt2801_ao_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + int chan=CR_CHAN(it->chanlist[0]); + + dt2801_writecmd(dev, DT_C_WRITE_DAIM); + dt2801_writedata(dev, chan); + dt2801_writedata2(dev, it->data[0]); + + return 1; +} + +static int dt2801_dio(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + unsigned int bits; + int which=0; + + if(s==dev->subdevices+4)which=1; + + if(it->flags & TRIG_CONFIG){ + /* configure */ + if(it->chanlist[it->n_chan-1]){ + s->io_bits=0xff; + dt2801_writecmd(dev, DT_C_SET_DIGOUT); + }else{ + s->io_bits=0; + dt2801_writecmd(dev, DT_C_SET_DIGIN); + } + dt2801_writedata(dev, which); + }else{ + if(s->io_bits){ + do_pack(&s->state,it); + dt2801_writecmd(dev, DT_C_WRITE_DIG); + dt2801_writedata(dev, which); + dt2801_writedata(dev, s->state); + }else{ + dt2801_writecmd(dev, DT_C_READ_DIG); + dt2801_writedata(dev, which); + dt2801_readdata(dev, &bits); + di_unpack(bits,it); + } + } + return it->n_chan; +} + + + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_dt2801); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_dt2801); +} +#endif diff --git a/comedi/drivers/dt2811.c b/comedi/drivers/dt2811.c new file mode 100644 index 00000000..be12eb84 --- /dev/null +++ b/comedi/drivers/dt2811.c @@ -0,0 +1,567 @@ +/* + module/dt2811.c + hardware driver for Data Translation DT2811 + + COMEDI - Linux Control and Measurement Device Interface + History: + Base Version - David A. Schleef + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *driver_name = "dt2811"; + +/* +--BEGIN-RANGE-DEFS-- +RANGE_dt2811_pgh_ai_5_unipolar + 0 5 + 0 2.5 + 0 1.25 + 0 0.625 +RANGE_dt2811_pgh_ai_2_5_bipolar + -2.5 2.5 + -1.25 1.25 + -0.625 0.625 + -0.3125 0.3125 +RANGE_dt2811_pgh_ai_5_bipolar + -5 5 + -2.5 2.5 + -1.25 1.25 + -0.625 0.625 +RANGE_dt2811_pgl_ai_5_unipolar + 0 5 + 0 0.5 + 0 0.05 + 0 0.01 +RANGE_dt2811_pgl_ai_2_5_bipolar + -2.5 2.5 + -0.25 0.25 + -0.025 0.025 + -0.005 0.005 +RANGE_dt2811_pgl_ai_5_bipolar + -5 5 + -0.5 0.5 + -0.05 0.05 + -0.01 0.01 +RANGE_dt2811_ao_5_bipolar + -5 5 +RANGE_dt2811_ao_2_5_bipolar + -2.5 2.5 +RANGE_dt2811_ao_5_uniipolar + 0 5 +---END-RANGE-DEFS--- +*/ + +/* + + 0x00 ADCSR R/W A/D Control/Status Register + bit 7 - (R) 1 indicates A/D conversion done + reading ADDAT clears bit + (W) ignored + bit 6 - (R) 1 indicates A/D error + (W) ignored + bit 5 - (R) 1 indicates A/D busy, cleared at end + of conversion + (W) ignored + bit 4 - (R) 0 + (W) + bit 3 - (R) 0 + bit 2 - (R/W) 1 indicates interrupts enabled + bits 1,0 - (R/W) mode bits + 00 single conversion on ADGCR load + 01 continuous conversion, internal clock, + (clock enabled on ADGCR load) + 10 continuous conversion, internal clock, + external trigger + 11 continuous conversion, external clock, + external trigger + + 0x01 ADGCR R/W A/D Gain/Channel Register + bit 6,7 - (R/W) gain select + 00 gain=1, both PGH, PGL models + 01 gain=2 PGH, 10 PGL + 10 gain=4 PGH, 100 PGL + 11 gain=8 PGH, 500 PGL + bit 4,5 - reserved + bit 3-0 - (R/W) channel select + channel number from 0-15 + + 0x02,0x03 (R) ADDAT A/D Data Register + (W) DADAT0 D/A Data Register 0 + 0x02 low byte + 0x03 high byte + + 0x04,0x05 (W) DADAT0 D/A Data Register 1 + + 0x06 (R) DIO0 Digital Input Port 0 + (W) DIO1 Digital Output Port 1 + + 0x07 TMRCTR (R/W) Timer/Counter Register + bits 6,7 - reserved + bits 5-3 - Timer frequency control (mantissa) + 543 divisor freqency (kHz) + 000 1 600 + 001 10 60 + 010 2 300 + 011 3 200 + 100 4 150 + 101 5 120 + 110 6 100 + 111 12 50 + bits 2-0 - Timer frequency control (exponent) + 210 multiply divisor/divide frequency by + 000 1 + 001 10 + 010 100 + 011 1000 + 100 10000 + 101 100000 + 110 1000000 + 111 10000000 + + */ + +#define DT2811_SIZE 8 + +#define DT2811_ADCSR 0 +#define DT2811_ADGCR 1 +#define DT2811_ADDATLO 2 +#define DT2811_ADDATHI 3 +#define DT2811_DADAT0LO 2 +#define DT2811_DADAT0HI 3 +#define DT2811_DADAT1LO 4 +#define DT2811_DADAT1HI 5 +#define DT2811_DIO 6 +#define DT2811_TMRCTR 7 + +/* + * flags + */ + +/* ADCSR */ + +#define DT2811_ADDONE 0x80 +#define DT2811_ADERROR 0x40 +#define DT2811_ADBUSY 0x20 +#define DT2811_CLRERROR 0x10 +#define DT2811_INTENB 0x04 +#define DT2811_ADMODE 0x03 + +static int dt2811_attach(comedi_device *dev,comedi_devconfig *it); +static int dt2811_detach(comedi_device *dev); +static int dt2811_recognize(char *name); +comedi_driver driver_dt2811={ + driver_name: "dt2811", + module: &__this_module, + attach: dt2811_attach, + detach: dt2811_detach, + recognize: dt2811_recognize, +}; + +static int dt2811_ai(comedi_device * dev, comedi_subdevice * s, comedi_trig * it); +#if 0 +static int dt2811_ai_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it); +#endif +static int dt2811_ao(comedi_device * dev, comedi_subdevice * s, comedi_trig * it); +static int dt2811_di(comedi_device * dev, comedi_subdevice * s, comedi_trig * it); +static int dt2811_do(comedi_device * dev, comedi_subdevice * s, comedi_trig * it); + +enum { card_2811_pgh, card_2811_pgl }; +typedef struct { + int ntrig; + int curadchan; + enum { + adc_singleended, adc_diff, adc_pseudo_diff + } adc_mux; + enum { + adc_bipolar_5, adc_bipolar_2_5, adc_unipolar_5 + } adc_range; + enum { + dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5 + } dac_range[2]; + int range_type_list[2]; +} dt2811_private; + +#define devpriv ((dt2811_private *)dev->private) + +static int adc_range_types[][2] = +{ + /* dt2811-pgh dt2811-pgl */ + { RANGE_dt2811_pgh_ai_5_bipolar, RANGE_dt2811_pgl_ai_5_bipolar }, + { RANGE_dt2811_pgh_ai_2_5_bipolar, RANGE_dt2811_pgl_ai_2_5_bipolar }, + { RANGE_dt2811_pgh_ai_5_unipolar, RANGE_dt2811_pgl_ai_5_unipolar } +}; +static int dac_range_types[] = +{ + RANGE_dt2811_ao_5_bipolar, + RANGE_dt2811_ao_2_5_bipolar, + RANGE_dt2811_ao_5_uniipolar +}; + +#define DT2811_TIMEOUT 5 + +static void dt2811_interrupt(int irq, void *d, struct pt_regs *regs) +{ + int lo, hi; + int data; + comedi_device *dev = d; + + lo = inb(dev->iobase + DT2811_ADDATLO); + hi = inb(dev->iobase + DT2811_ADDATHI); + + data = lo + (hi << 8); + + if (!(--devpriv->ntrig)) { + /* XXX */ + /* how to turn off acquisition */ + comedi_done(dev, dev->subdevices + 0); + } +} + +/* + options[0] Board base address + options[1] IRQ + options[2] Input configuration + 0 == single-ended + 1 == differential + 2 == pseudo-differential + options[3] Analog input range configuration + 0 == bipolar 5 (-5V -- +5V) + 1 == bipolar 2.5V (-2.5V -- +2.5V) + 2 == unipolar 5V (0V -- +5V) + options[4] Analog output 0 range configuration + 0 == bipolar 5 (-5V -- +5V) + 1 == bipolar 2.5V (-2.5V -- +2.5V) + 2 == unipolar 5V (0V -- +5V) + options[5] Analog output 1 range configuration + 0 == bipolar 5 (-5V -- +5V) + 1 == bipolar 2.5V (-2.5V -- +2.5V) + 2 == unipolar 5V (0V -- +5V) +*/ +static int dt2811_recognize(char *name) +{ + if(!strcmp("dt2811-pgh", name))return card_2811_pgh; + if(!strcmp("dt2811-pgl", name))return card_2811_pgl; + + return -1; +} + +static int dt2811_attach(comedi_device * dev, comedi_devconfig * it) +{ + int i, irq, irqs; + long flags; + int ret; + comedi_subdevice *s; + int board = -1; + + dev->iobase = it->options[0]; + + printk("comedi%d: dt2811: base=0x%04x\n", dev->minor, dev->iobase); + + if (check_region(dev->iobase, DT2811_SIZE) < 0) { + printk("I/O port conflict\n"); + return -EIO; + } + request_region(dev->iobase, DT2811_SIZE, driver_name); + if (board == card_2811_pgh) { + dev->board_name = "dt2811-pgh"; + } else if (board == card_2811_pgl) { + dev->board_name = "dt2811-pgl"; + } + dev->iosize = DT2811_SIZE; + +#if 0 + outb(0, dev->iobase + DT2811_ADCSR); + udelay(100); + i = inb(dev->iobase + DT2811_ADDATLO); + i = inb(dev->iobase + DT2811_ADDATHI); +#endif + +#if 1 + irq = it->options[1]; + if (irq < 0) { + save_flags(flags); + sti(); + irqs = probe_irq_on(); + + outb(DT2811_CLRERROR | DT2811_INTENB, dev->iobase + DT2811_ADCSR); + outb(0, dev->iobase + DT2811_ADGCR); + + udelay(100); + + irq = probe_irq_off(irqs); + restore_flags(flags); + + /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */ + + if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) { + printk("error probing irq (bad) \n"); + } + dev->irq = 0; + if (irq > 0) { + i = inb(dev->iobase + DT2811_ADDATLO); + i = inb(dev->iobase + DT2811_ADDATHI); + printk("(irq = %d)\n", irq); + request_irq(irq, dt2811_interrupt, 0 * SA_INTERRUPT, driver_name, dev); + dev->irq = irq; + } else if (irq == 0) { + printk("(no irq)\n"); + } else { + printk("( multiple irq's -- this is bad! )\n"); + } + } +#endif + + dev->n_subdevices = 4; + if ((ret = alloc_subdevices(dev)) < 0) + return ret; + if ((ret = alloc_private(dev, sizeof(dt2811_private))) < 0) + return ret; + switch (it->options[2]) { + case 0: devpriv->adc_mux = adc_singleended; break; + case 1: devpriv->adc_mux = adc_diff; break; + case 2: devpriv->adc_mux = adc_pseudo_diff; break; + default:devpriv->adc_mux = adc_singleended; break; + } + switch (it->options[3]) { + case 0: devpriv->adc_range = adc_bipolar_5; break; + case 1: devpriv->adc_range = adc_bipolar_2_5; break; + case 2: devpriv->adc_range = adc_unipolar_5; break; + default:devpriv->adc_range = adc_bipolar_5; break; + } + switch (it->options[4]) { + case 0: devpriv->dac_range[0] = dac_bipolar_5; break; + case 1: devpriv->dac_range[0] = dac_bipolar_2_5; break; + case 2: devpriv->dac_range[0] = dac_unipolar_5; break; + default:devpriv->dac_range[0] = dac_bipolar_5; break; + } + switch (it->options[5]) { + case 0: devpriv->dac_range[1] = dac_bipolar_5; break; + case 1: devpriv->dac_range[1] = dac_bipolar_2_5; break; + case 2: devpriv->dac_range[1] = dac_unipolar_5; break; + default:devpriv->dac_range[1] = dac_bipolar_5; break; + } + + s = dev->subdevices + 0; + /* initialize the ADC subdevice */ + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE; + s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16; + s->trig[0] = dt2811_ai; + s->maxdata = 0xfff; + s->range_type = adc_range_types[devpriv->adc_range][board]; + + s = dev->subdevices + 1; + /* ao subdevice */ + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = 2; + s->trig[0] = dt2811_ao; + s->maxdata = 0xfff; + s->range_type_list = devpriv->range_type_list; + devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]]; + devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]]; + + s = dev->subdevices + 2; + /* di subdevice */ + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 8; + s->trig[0] = dt2811_di; + s->maxdata = 1; + s->range_type = RANGE_digital; + + s = dev->subdevices + 3; + /* do subdevice */ + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = 8; + s->trig[0] = dt2811_do; + s->maxdata = 1; + s->state = 0; + s->range_type = RANGE_digital; + + return 0; +} + + +static int dt2811_detach(comedi_device * dev) +{ + printk("comedi%d: dt2811: remove\n", dev->minor); + + if (dev->irq) { + free_irq(dev->irq, dev); + } + release_region(dev->iobase, dev->iosize); + + return 0; +} + + +static int dt2811_ai(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + int i; + for(i=0 ; i < it->n_chan ; i++) { + int lo, hi; + int chan; + + chan = CR_CHAN(it->chanlist[i]); + + outb(chan, dev->iobase + DT2811_ADGCR); + while (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY) + udelay(25); + lo = inb(dev->iobase + DT2811_ADDATLO); + hi = inb(dev->iobase + DT2811_ADDATHI); + + it->data[i] = lo + 0x100 * hi; + } + return i; +} + +#if 0 +static int dt2811_ai(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + + switch (it->mode) { + case 0: + return dt2811_ai_mode0(dev, s, it); + case 1: +#if defined(FROM_DT2814) + outb(it->chan | DT2814_ENB | (it->trigvar << 5), dev->iobase + DT2814_CSR); +#endif + default: + return -EINVAL; + } +} + +static int dt2811_ai_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + int lo, hi; + int chan; + + chan = CR_CHAN(it->chanlist[0]); + + outb(chan, dev->iobase + DT2811_ADGCR); + while (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY) + udelay(25); + lo = inb(dev->iobase + DT2811_ADDATLO); + hi = inb(dev->iobase + DT2811_ADDATHI); + + it->data[0] = lo + 0x100 * hi; + + return 0; +} +#endif + + +#if 0 +int dt2811_adtrig(kdev_t minor, comedi_adtrig * adtrig) +{ + comedi_device *dev = comedi_devices + minor; + + if (adtrig->n < 1) + return 0; + dev->curadchan = adtrig->chan; + switch (dev->i_admode) { + case COMEDI_MDEMAND: + dev->ntrig = adtrig->n - 1; + /*printk("dt2811: AD soft trigger\n"); */ + /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); *//* not neccessary */ + outb(dev->curadchan, dev->iobase + DT2811_ADGCR); + do_gettimeofday(&trigtime); + break; + case COMEDI_MCONTS: + dev->ntrig = adtrig->n; + break; + } + + return 0; +} +#endif + +static int dt2811_ao(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + int i; + for(i=0 ; i < it->n_chan ; i++) { + int lo, hi; + int chan; + + chan = CR_CHAN(it->chanlist[i]); + + lo = (it->data[i] & 0xff); + hi = (it->data[i] >> 8) & 0x0f; + + switch (chan) { + case 0: + outb(lo, dev->iobase + DT2811_DADAT0LO); + outb(hi, dev->iobase + DT2811_DADAT0HI); + break; + case 1: + outb(lo, dev->iobase + DT2811_DADAT1LO); + outb(hi, dev->iobase + DT2811_DADAT1HI); + break; + } + } + return i; +} + +static int dt2811_di(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + unsigned int bits; + + bits=inb(dev->iobase + DT2811_DIO); + + return di_unpack(bits,it); +} + +static int dt2811_do(comedi_device * dev, comedi_subdevice * s, comedi_trig * it) +{ + do_pack(&s->state,it); + + outb(s->state, dev->iobase + DT2811_DIO); + + return it->n_chan; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_dt2811); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_dt2811); +} +#endif diff --git a/comedi/drivers/dt2814.c b/comedi/drivers/dt2814.c new file mode 100644 index 00000000..134de7ec --- /dev/null +++ b/comedi/drivers/dt2814.c @@ -0,0 +1,245 @@ +/* + module/dt2814.c + hardware driver for Data Translation DT2814 + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#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;iiobase+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;iiobase+DT2814_CSR)&DT2814_FINISH) + break; + } + inb(dev->iobase+DT2814_DATA); + inb(dev->iobase+DT2814_DATA); + restore_flags(flags); + + comedi_done(dev,dev->subdevices); + } +} + + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_dt2814); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_dt2814); +} +#endif diff --git a/comedi/drivers/dt2815.c b/comedi/drivers/dt2815.c new file mode 100644 index 00000000..39f72442 --- /dev/null +++ b/comedi/drivers/dt2815.c @@ -0,0 +1,235 @@ +/* + module/dt2815.c + hardware driver for Data Translation DT2815 + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1999 Anders Blomdell + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* +--BEGIN-RANGE-DEFS-- +RANGE_dt2815_ao_32_current + 0 32 mA +RANGE_dt2815_ao_20_current + 4 20 mA +---END-RANGE-DEFS--- +*/ + +#define RANGE_dt2815_ao_5_unipolar RANGE_unipolar5 +#define RANGE_dt2815_ao_5_bipolar RANGE_bipolar5 + +#define DT2815_SIZE 2 + +#define DT2815_DATA 0 +#define DT2815_STATUS 1 + +static int dt2815_attach(comedi_device *dev,comedi_devconfig *it); +static int dt2815_detach(comedi_device *dev); +comedi_driver driver_dt2815={ + driver_name: "dt2815", + module: &__this_module, + attach: dt2815_attach, + detach: dt2815_detach, +}; + +static void dt2815_free_resources(comedi_device * dev); + +typedef struct { + unsigned int range_type_list[8]; +} dt2815_private; + +#define devpriv ((dt2815_private *)dev->private) + +static int dt2815_ao(comedi_device * dev, comedi_subdevice *s, comedi_trig * it) +{ + int i; + for(i=0 ; i < it->n_chan ; i++) { + int t; + int chan = CR_CHAN(it->chanlist[i]); + int data = it->data[i]; + unsigned int status; + unsigned int lo, hi; + + lo = ((data & 0x0f) << 4) | (chan << 1) | 0x01; + hi = (data & 0xff0) >> 4; + for (t = 0 ; t < 30 ; t++) { + status = inb(dev->iobase + DT2815_STATUS); + if (status == 0x00) { + outb(lo, dev->iobase + DT2815_DATA); + break; + } + udelay(10); + } + if (status != 0x00) { + rt_printk("dt2815: failed to write low byte on %d reason %x, %d\n", + chan, status, t); + } + for (t = 0 ; t < 30 ; t++) { + status = inb(dev->iobase + DT2815_STATUS); + if (status == 0x10) { + outb(hi, dev->iobase + DT2815_DATA); + break; + } + udelay(10); + } + if (status != 0x10) { + rt_printk("dt2815: failed to write high byte on %d reason %x, %d\n", + chan, status, t); + } + } + return i; +} + +/* + options[0] Board base address + options[1] IRQ (not applicable) + options[2] Voltage unipolar/bipolar configuration + 0 == unipolar 5V (0V -- +5V) + 1 == bipolar 5V (-5V -- +5V) + options[3] Current offset configuration + 0 == disabled (0mA -- +32mAV) + 1 == enabled (+4mA -- +20mAV) + options[4] Firmware program configuration + 0 == program 1 (see manual table 5-4) + 1 == program 2 (see manual table 5-4) + 2 == program 3 (see manual table 5-4) + 3 == program 4 (see manual table 5-4) + options[5] Analog output 0 range configuration + 0 == voltage + 1 == current + options[6] Analog output 1 range configuration + ... + options[12] Analog output 7 range configuration + 0 == voltage + 1 == current + */ + +static int dt2815_attach(comedi_device * dev, comedi_devconfig * it) +{ + comedi_subdevice *s; + int i; + unsigned int current_range_type,voltage_range_type; + + dev->iobase = it->options[0]; + printk("comedi%d: dt2815: 0x%04x ", dev->minor, dev->iobase); + if (check_region(dev->iobase, DT2815_SIZE) < 0) { + printk("I/O port conflict\n"); + return -EIO; + } + request_region(dev->iobase, DT2815_SIZE, "dt2815"); + + dev->board_name = "dt2815"; + + dev->n_subdevices = 1; + if(alloc_subdevices(dev)<0) + return -ENOMEM; + if(alloc_private(dev,sizeof(dt2815_private))<0) + return -ENOMEM; + + s=dev->subdevices; + /* ao subdevice */ + s->type=COMEDI_SUBD_AO; + s->subdev_flags=SDF_WRITEABLE; + s->maxdata=0xfff; + s->n_chan=8; + s->trig[0] = dt2815_ao; + s->range_type_list=devpriv->range_type_list; + + current_range_type = (it->options[3]) + ? RANGE_dt2815_ao_20_current + : RANGE_dt2815_ao_32_current; + voltage_range_type = (it->options[2]) + ? RANGE_dt2815_ao_5_bipolar + : RANGE_dt2815_ao_5_unipolar; + for (i = 0; i < 8; i++) { + devpriv->range_type_list[i] = (it->options[5+i]) + ? current_range_type + : voltage_range_type; + } + + /* Init the 2815 */ + outb(0x00, dev->iobase + DT2815_STATUS); + for (i = 0 ; i < 100 ; i++) { + /* This is incredibly slow (approx 20 ms) */ + unsigned int status; + + udelay(1000); + status = inb(dev->iobase + DT2815_STATUS); + if (status == 4) { + unsigned int program; + program = (it->options[4] & 0x3) << 3 | 0x7; + outb(program, dev->iobase + DT2815_DATA); + printk(", program: 0x%x (@t=%d)\n", program, i); + break; + } else if (status != 0x00) { + printk("dt2815: unexpected status 0x%x (@t=%d)\n", status, i); + if (status & 0x60) { + outb(0x00, dev->iobase + DT2815_STATUS); + } + } + } + + + printk("\n"); + + return 0; +} + +static void dt2815_free_resources(comedi_device * dev) +{ + if(dev->iobase) + release_region(dev->iobase, DT2815_SIZE); +} + +static int dt2815_detach(comedi_device * dev) +{ + printk("comedi%d: dt2815: remove\n", dev->minor); + + dt2815_free_resources(dev); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_dt2815); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_dt2815); +} +#endif diff --git a/comedi/drivers/dt2817.c b/comedi/drivers/dt2817.c new file mode 100644 index 00000000..3e3a6ec2 --- /dev/null +++ b/comedi/drivers/dt2817.c @@ -0,0 +1,164 @@ +/* + module/dt2817.c + hardware driver for Data Translation DT2817 + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#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;in_chan;i++){ + chan=CR_CHAN(it->chanlist[i]); + if(chan<8){ + mask=0xff; + }else if(chan<16){ + mask=0xff00; + }else if(chan<24){ + mask=0xff0000; + }else mask=0xff000000; + if(it->data[i])s->io_bits|=mask; + else s->io_bits&=~mask; + } + if(s->io_bits&0x000000ff)oe|=0x1; + if(s->io_bits&0x0000ff00)oe|=0x2; + if(s->io_bits&0x00ff0000)oe|=0x4; + if(s->io_bits&0xff000000)oe|=0x8; + + outb(oe,dev->iobase + DT2817_CR); + }else{ + int data; + + if(it->flags & TRIG_WRITE){ + do_pack(&s->state,it); + if(s->io_bits&0x000000ff) + outb(s->state&0xff, dev->iobase + DT2817_DATA+0); + if(s->io_bits&0x0000ff00) + outb((s->state>>8)&0xff, dev->iobase + DT2817_DATA+1); + if(s->io_bits&0x00ff0000) + outb((s->state>>16)&0xff, dev->iobase + DT2817_DATA+2); + if(s->io_bits&0xff000000) + outb((s->state>>24)&0xff, dev->iobase + DT2817_DATA+3); + }else{ + data = inb(dev->iobase + DT2817_DATA + 0); + data |= (inb(dev->iobase + DT2817_DATA + 1)<<8); + data |= (inb(dev->iobase + DT2817_DATA + 2)<<16); + data |= (inb(dev->iobase + DT2817_DATA + 3)<<24); + di_unpack(data,it); + } + } + + return it->n_chan; +} + +static int dt2817_attach(comedi_device *dev,comedi_devconfig *it) +{ + int ret; + comedi_subdevice *s; + + dev->iobase=it->options[0]; + printk("comedi%d: dt2817: 0x%04x ",dev->minor,dev->iobase); + if(check_region(dev->iobase,DT2817_SIZE)<0){ + printk("I/O port conflict\n"); + return -EIO; + } + request_region(dev->iobase,DT2817_SIZE,"dt2817"); + dev->board_name="dt2817"; + dev->iosize=DT2817_SIZE; + + dev->n_subdevices=1; + if((ret=alloc_subdevices(dev))<0) + return ret; + + s=dev->subdevices+0; + + s->n_chan=32; + s->type=COMEDI_SUBD_DIO; + s->subdev_flags=SDF_READABLE|SDF_WRITEABLE; + s->range_type=RANGE_digital; + s->maxdata=1; + s->trig[0]=dt2817_dio; + + s->state=0; + outb(0,dev->iobase+DT2817_CR); + + printk("\n"); + + return 0; +} + + +static int dt2817_detach(comedi_device *dev) +{ + printk("comedi%d: dt2817: remove\n",dev->minor); + + release_region(dev->iobase,dev->iosize); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_dt2817); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_dt2817); +} +#endif diff --git a/comedi/drivers/dt282x.c b/comedi/drivers/dt282x.c new file mode 100644 index 00000000..7da8671b --- /dev/null +++ b/comedi/drivers/dt282x.c @@ -0,0 +1,1256 @@ +/* + modules/dt282x.c + hardware driver for Data Translation DT2821 series + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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;_iad_2scomp){ + for(i=0;ibuf_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<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<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<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;in_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;iboard_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<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<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(dma2dma[0].chan=dma1; + + ret = request_dma(dma2, "dt282x B"); + if (ret) + return -EBUSY; + devpriv->dma[1].chan=dma2; + + devpriv->dma_maxsize = PAGE_SIZE >> 1; + devpriv->dma[0].buf = (void *) get_free_page(GFP_KERNEL | GFP_DMA); + devpriv->dma[1].buf = (void *) get_free_page(GFP_KERNEL | GFP_DMA); + if (!devpriv->dma[0].buf || !devpriv->dma[1].buf) { + printk(" can't get DMA memory"); + return -ENOMEM; + } + + printk(" (dma=%d,%d)",dma1,dma2); + + devpriv->usedma=1; + + return 0; +} + diff --git a/comedi/drivers/dt3000.c b/comedi/drivers/dt3000.c new file mode 100644 index 00000000..d234c096 --- /dev/null +++ b/comedi/drivers/dt3000.c @@ -0,0 +1,665 @@ +/* + module/dt3000.c + Data Translation DT3000 series driver + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1999 David A. Schleef + + 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 +#include +#include +#include +#include + +#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;iiobase+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;in_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;in_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<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<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;ipci_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;idevice == 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;idevice == dt3k_boardtypes[i].device_id){ + *board=i; + return from; + } + } + printk("unknown Data Translation PCI device found with device_id=0x%04x\n",from->device); + } + from=(struct pci_dev *)(from->global_list.next); + } + *board=-1; + return from; +} + +#endif +#endif /* PCI_SUPPORT_VER1 */ + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_dt3000); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_dt3000); +} +#endif diff --git a/comedi/drivers/mite.c b/comedi/drivers/mite.c new file mode 100644 index 00000000..5ff8a444 --- /dev/null +++ b/comedi/drivers/mite.c @@ -0,0 +1,350 @@ +/* + module/mite.c + Hardware driver for NI Mite PCI interface chip + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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;iring[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;iprealloc_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 + 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 +}; + +#endif + +#ifdef LINUX_V22 + +EXPORT_SYMBOL(mite_dma_arm); +EXPORT_SYMBOL(mite_dma_disarm); +EXPORT_SYMBOL(mite_dma_prep); +EXPORT_SYMBOL(mite_setup); +EXPORT_SYMBOL(mite_unsetup); +EXPORT_SYMBOL(mite_kvmem_segment_load); +EXPORT_SYMBOL(mite_devices); + +#endif + +#endif + diff --git a/comedi/drivers/mite.h b/comedi/drivers/mite.h new file mode 100644 index 00000000..e038a2d9 --- /dev/null +++ b/comedi/drivers/mite.h @@ -0,0 +1,241 @@ +/* + module/mite.h + Hardware driver for NI Mite PCI interface chip + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1999 David A. Schleef + + 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 +#include +#ifdef PCI_SUPPORT_VER1 +#include +#endif + + +#define PCI_VENDOR_ID_NATINST 0x1093 + + +#define MITE_RING_SIZE 32 + +struct mite_dma_chain{ + u32 count; + u32 addr; + u32 unused; + u32 next; +}; + +struct mite_struct{ + struct mite_struct *next; + int used; + +#ifdef PCI_SUPPORT_VER1 + unsigned char pci_bus; + unsigned char pci_device_fn; + int irq; + int device_id; +#else + struct pci_dev *pcidev; +#endif + unsigned long mite_phys_addr; + void *mite_io_addr; + unsigned long daq_phys_addr; + void *daq_io_addr; + + struct mite_dma_chain ring[MITE_RING_SIZE]; +}; + +extern struct mite_struct *mite_devices; + +#ifdef PCI_SUPPORT_VER1 +#define mite_irq(a) ((a)->irq) +#define mite_device_id(a) ((a)->device_id) +#else +#define mite_irq(a) ((a)->pcidev->irq) +#define mite_device_id(a) ((a)->pcidev->device) +#endif + +#define mite_iobase(a) ((a)->daq_io_addr) + + +void mite_init(void); +void mite_cleanup(void); +int mite_setup(struct mite_struct *mite); +void mite_unsetup(struct mite_struct *mite); + +void mite_dma_prep(struct mite_struct *mite,comedi_subdevice *s); + + +#define CHAN_OFFSET(x) (0x100*(x)) + +/* DMA base for chan 0 is 0x500, chan 1 is 0x600 */ + +#define MITE_CHOR 0x500 +#define CHOR_DMARESET (1<<31) +#define CHOR_SET_SEND_TC (1<<11) +#define CHOR_CLR_SEND_TC (1<<10) +#define CHOR_SET_LPAUSE (1<<9) +#define CHOR_CLR_LPAUSE (1<<8) +#define CHOR_CLRDONE (1<<7) +#define CHOR_CLRRB (1<<6) +#define CHOR_CLRLC (1<<5) +#define CHOR_FRESET (1<<4) +#define CHOR_ABORT (1<<3) +#define CHOR_STOP (1<<2) +#define CHOR_CONT (1<<1) +#define CHOR_START (1<<0) +#define CHOR_PON (CHOR_CLR_SEND_TC|CHOR_CLR_LPAUSE) + +#define MITE_CHCR 0x504 +#define CHCR_SET_DMA_IE (1<<31) +#define CHCR_CLR_DMA_IE (1<<30) +#define CHCR_SET_LINKP_IE (1<<29) +#define CHCR_CLR_LINKP_IE (1<<28) +#define CHCR_SET_SAR_IE (1<<27) +#define CHCR_CLR_SAR_IE (1<<26) +#define CHCR_SET_DONE_IE (1<<25) +#define CHCR_CLR_DONE_IE (1<<24) +#define CHCR_SET_MRDY_IE (1<<23) +#define CHCR_CLR_MRDY_IE (1<<22) +#define CHCR_SET_DRDY_IE (1<<21) +#define CHCR_CLR_DRDY_IE (1<<20) +#define CHCR_SET_LC_IE (1<<19) +#define CHCR_CLR_LC_IE (1<<18) +#define CHCR_SET_CONT_RB_IE (1<<17) +#define CHCR_CLR_CONT_RB_IE (1<<16) +#define CHCR_FIFODIS (1<<15) +#define CHCR_FIFO_ON 0 +#define CHCR_BURSTEN (1<<14) +#define CHCR_NO_BURSTEN 0 +#define CHCR_NFTP(x) ((x)<<11) +#define CHCR_NFTP0 CHCR_NFTP(0) +#define CHCR_NFTP1 CHCR_NFTP(1) +#define CHCR_NFTP2 CHCR_NFTP(2) +#define CHCR_NFTP4 CHCR_NFTP(3) +#define CHCR_NFTP8 CHCR_NFTP(4) +#define CHCR_NFTP16 CHCR_NFTP(5) +#define CHCR_NETP(x) ((x)<<11) +#define CHCR_NETP0 CHCR_NETP(0) +#define CHCR_NETP1 CHCR_NETP(1) +#define CHCR_NETP2 CHCR_NETP(2) +#define CHCR_NETP4 CHCR_NETP(3) +#define CHCR_NETP8 CHCR_NETP(4) +#define CHCR_CHEND1 (1<<5) +#define CHCR_CHEND0 (1<<4) +#define CHCR_DIR (1<<3) +#define CHCR_DEV_TO_MEM CHCR_DIR +#define CHCR_MEM_TO_DEV 0 +#define CHCR_NORMAL ((0)<<0) +#define CHCR_CONTINUE ((1)<<0) +#define CHCR_RINGBUFF ((2)<<0) +#define CHCR_LINKSHORT ((4)<<0) +#define CHCR_LINKLONG ((5)<<0) +#define CHCRPON (CHCR_CLR_DMA_IE | CHCR_CLR_LINKP_IE | CHCR_CLR_SAR_IE | CHCR_CLR_DONE_IE | CHCR_CLR_MRDY_IE | CHCR_CLR_DRDY_IE | CHCR_CLR_LC_IE | CHCR_CLR_CONT_IE) + +#define MITE_TCR 0x508 + +/* CR bits */ +#define CR_RL(x) ((x)<<21) +#define CR_RL0 CR_RL(0) +#define CR_RL1 CR_RL(1) +#define CR_RL2 CR_RL(2) +#define CR_RL4 CR_RL(3) +#define CR_RL8 CR_RL(4) +#define CR_RL16 CR_RL(5) +#define CR_RL32 CR_RL(6) +#define CR_RL64 CR_RL(7) +#define CR_RD(x) ((x)<<19) +#define CR_RD0 CR_RD(0) +#define CR_RD32 CR_RD(1) +#define CR_RD512 CR_RD(2) +#define CR_RD8192 CR_RD(3) +#define CR_REQS(x) ((x)<<16) +#define CR_REQSDRQ0 CR_REQS(4) +#define CR_REQSDRQ1 CR_REQS(5) +#define CR_REQSDRQ2 CR_REQS(6) +#define CR_REQSDRQ3 CR_REQS(7) +#define CR_ASEQx(x) ((x)<<10) +#define CR_ASEQx0 CR_ASEQx(0) +#define CR_ASEQDONT CR_ASEQx0 +#define CR_ASEQxP1 CR_ASEQx(1) +#define CR_ASEQUP CR_ASEQxP1 +#define CR_ASEQxP2 CR_ASEQx(2) +#define CR_ASEQDOWN CR_ASEQxP2 +#define CR_ASEQxP4 CR_ASEQx(3) +#define CR_ASEQxP8 CR_ASEQx(4) +#define CR_ASEQxP16 CR_ASEQx(5) +#define CR_ASEQxP32 CR_ASEQx(6) +#define CR_ASEQxP64 CR_ASEQx(7) +#define CR_ASEQxM1 CR_ASEQx(9) +#define CR_ASEQxM2 CR_ASEQx(10) +#define CR_ASEQxM4 CR_ASEQx(11) +#define CR_ASEQxM8 CR_ASEQx(12) +#define CR_ASEQxM16 CR_ASEQx(13) +#define CR_ASEQxM32 CR_ASEQx(14) +#define CR_ASEQxM64 CR_ASEQx(15) +#define CR_PSIZEBYTE (1<<8) +#define CR_PSIZEHALF (2<<8) +#define CR_PSIZEWORD (3<<8) +#define CR_PORTCPU (0<<6) +#define CR_PORTIO (1<<6) +#define CR_PORTVXI (2<<6) +#define CR_PORTMXI (3<<6) +#define CR_AMDEVICE (1<<0) + +#define MITE_MCR 0x50c +#define MCRPON 0 + +#define MITE_MAR 0x510 + +#define MITE_DCR 0x514 +#define DCR_NORMAL (1<<29) +#define DCRPON 0 + +#define MITE_DAR 0x518 + +#define MITE_LKCR 0x51c + +#define MITE_LKAR 0x520 +#define MITE_LLKAR 0x524 +#define MITE_BAR 0x528 +#define MITE_BCR 0x52c +#define MITE_SAR 0x530 +#define MITE_WSCR 0x534 +#define MITE_WSER 0x538 +#define MITE_CHSR 0x53c +#define MITE_FCR 0x540 + +#define MITE_FIFO 0x80 +#define MITE_FIFOEND 0xff + +#define MITE_AMRAM 0x00 +#define MITE_AMDEVICE 0x01 +#define MITE_AMHOST_A32_SINGLE 0x09 +#define MITE_AMHOST_A24_SINGLE 0x39 +#define MITE_AMHOST_A16_SINGLE 0x29 +#define MITE_AMHOST_A32_BLOCK 0x0b +#define MITE_AMHOST_A32D64_BLOCK 0x08 +#define MITE_AMHOST_A24_BLOCK 0x3b + + + +#endif + diff --git a/comedi/drivers/multiq3.c b/comedi/drivers/multiq3.c new file mode 100644 index 00000000..c8d7dfca --- /dev/null +++ b/comedi/drivers/multiq3.c @@ -0,0 +1,314 @@ +/* + module/multiq3.c + hardware driver for Quanser Consulting MultiQ-3 board + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1999 Anders Blomdell + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define MULTIQ3_SIZE 16 + +/* + * MULTIQ-3 port offsets + */ +#define MULTIQ3_DIGIN_PORT 0 +#define MULTIQ3_DIGOUT_PORT 0 +#define MULTIQ3_DAC_DATA 2 +#define MULTIQ3_AD_DATA 4 +#define MULTIQ3_AD_CS 4 +#define MULTIQ3_STATUS 6 +#define MULTIQ3_CONTROL 6 +#define MULTIQ3_CLK_DATA 8 +#define MULTIQ3_ENC_DATA 12 +#define MULTIQ3_ENC_CONTROL 14 + +/* + * flags for CONTROL register + */ +#define MULTIQ3_AD_MUX_EN 0x0040 +#define MULTIQ3_AD_AUTOZ 0x0080 +#define MULTIQ3_AD_AUTOCAL 0x0100 +#define MULTIQ3_AD_SH 0x0200 +#define MULTIQ3_AD_CLOCK_4M 0x0400 +#define MULTIQ3_DA_LOAD 0x1800 + +#define MULTIQ3_CONTROL_MUST 0x0600 + +/* + * flags for STATUS register + */ +#define MULTIQ3_STATUS_EOC 0x008 +#define MULTIQ3_STATUS_EOC_I 0x010 + +/* + * flags for encoder control + */ +#define MULTIQ3_CLOCK_DATA 0x00 +#define MULTIQ3_CLOCK_SETUP 0x18 +#define MULTIQ3_INPUT_SETUP 0x41 +#define MULTIQ3_QUAD_X4 0x38 +#define MULTIQ3_BP_RESET 0x01 +#define MULTIQ3_CNTR_RESET 0x02 +#define MULTIQ3_TRSFRPR_CTR 0x08 +#define MULTIQ3_TRSFRCNTR_OL 0x10 +#define MULTIQ3_EFLAG_RESET 0x06 + +#define MULTIQ3_TIMEOUT 30 + +static int multiq3_attach(comedi_device *dev,comedi_devconfig *it); +static int multiq3_detach(comedi_device *dev); +comedi_driver driver_multiq3={ + driver_name: "multiq3", + module: &__this_module, + attach: multiq3_attach, + detach: multiq3_detach, +}; + + +static int multiq3_ai(comedi_device *dev, comedi_subdevice *s, comedi_trig *it) +{ + int i, hi, lo; + int chan; + int data; + int status, control; + + chan = CR_CHAN(it->chanlist[0]); + control = MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan<<3); + outw(control, dev->iobase+MULTIQ3_CONTROL); + for (i = 0; i < MULTIQ3_TIMEOUT; i++) { + status = inw(dev->iobase+MULTIQ3_STATUS); + if(status & MULTIQ3_STATUS_EOC) { + break; + } + udelay(10); + } + if(i == MULTIQ3_TIMEOUT){ + rt_printk("multiq3: timeout\n"); + return -ETIME; + } + outw(0, dev->iobase+MULTIQ3_AD_CS); + for (i = 0; i < MULTIQ3_TIMEOUT; i++) { + status = inw(dev->iobase+MULTIQ3_STATUS); + if(status & MULTIQ3_STATUS_EOC_I) { + break; + } + udelay(10); + } + hi = inb(dev->iobase + MULTIQ3_AD_CS) &0xff; + lo = inb(dev->iobase + MULTIQ3_AD_CS) &0xff; + + data = (((hi << 8) | lo) + 0x1000) & 0x1fff; + it->data[0]=data; + + return 1; +} + +static int multiq3_ao(comedi_device *dev, comedi_subdevice *s, comedi_trig *it) +{ + int chan, control; + + chan=CR_CHAN(it->chanlist[0]); + control = MULTIQ3_CONTROL_MUST | MULTIQ3_DA_LOAD | chan; + outw(control, dev->iobase+MULTIQ3_CONTROL); + outw(it->data[0], dev->iobase+MULTIQ3_DAC_DATA); + control = MULTIQ3_CONTROL_MUST; + outw(control, dev->iobase+MULTIQ3_CONTROL); + return 1; +} + +static int multiq3_di(comedi_device *dev, comedi_subdevice *s, comedi_trig *it) +{ + unsigned int bits; + + bits = inw(dev->iobase + MULTIQ3_DIGIN_PORT); + + return di_unpack(bits,it); +} + +static int multiq3_do(comedi_device *dev, comedi_subdevice *s, comedi_trig *it) +{ + do_pack(&s->state,it); + + outw(s->state, dev->iobase + MULTIQ3_DIGOUT_PORT); + + return it->n_chan; +} + +static int multiq3_ei(comedi_device *dev, comedi_subdevice *s, comedi_trig *it) +{ + int b1, b2, b3; + int chan; + int data; + int control; + + chan = CR_CHAN(it->chanlist[0]); + control = MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan<<3); + outw(control, dev->iobase+MULTIQ3_CONTROL); + outb(MULTIQ3_BP_RESET, dev->iobase+MULTIQ3_ENC_CONTROL); + outb(MULTIQ3_TRSFRCNTR_OL, dev->iobase+MULTIQ3_ENC_CONTROL); + b1 = inb(dev->iobase+MULTIQ3_ENC_DATA); + b2 = inb(dev->iobase+MULTIQ3_ENC_DATA); + b3 = inb(dev->iobase+MULTIQ3_ENC_DATA); + + data = (((b3<<16) | (b2 << 8) | (b1)) + 0x800000) & 0xffffff; + ((lsampl_t*)(it->data))[0] = data; + + return 1; +} + +static void encoder_reset(comedi_device *dev) { + int chan; + for (chan = 0 ; chan < dev->subdevices[4].n_chan ; chan++) { + int control = MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan<<3); + outw(control, dev->iobase+MULTIQ3_CONTROL); + outb(MULTIQ3_EFLAG_RESET, dev->iobase+MULTIQ3_ENC_CONTROL); + outb(MULTIQ3_BP_RESET, dev->iobase+MULTIQ3_ENC_CONTROL); + outb(MULTIQ3_CLOCK_DATA, dev->iobase+MULTIQ3_ENC_DATA); + outb(MULTIQ3_CLOCK_SETUP, dev->iobase+MULTIQ3_ENC_CONTROL); + outb(MULTIQ3_INPUT_SETUP, dev->iobase+MULTIQ3_ENC_CONTROL); + outb(MULTIQ3_QUAD_X4, dev->iobase+MULTIQ3_ENC_CONTROL); + outb(MULTIQ3_CNTR_RESET, dev->iobase+MULTIQ3_ENC_CONTROL); + } +} + +/* + options[0] - I/O port + options[1] - irq + options[2] - number of encoder chips installed + */ + +static int multiq3_attach(comedi_device * dev, comedi_devconfig * it) +{ + int result = 0; + int iobase; + int irq; + comedi_subdevice *s; + + iobase = it->options[0]; + printk("comedi%d: multiq3: 0x%04x ", dev->minor, iobase); + if (check_region(iobase, MULTIQ3_SIZE) < 0) { + printk("comedi%d: I/O port conflict\n", dev->minor); + return -EIO; + } + + request_region(dev->iobase, MULTIQ3_SIZE, "multiq3"); + dev->iobase = iobase; + dev->iosize = MULTIQ3_SIZE; + + irq = it->options[1]; + if (irq > 0) { + printk("comedi%d: irq = %d ignored\n", dev->minor, irq); + } else if(irq == 0) { + printk("comedi%d: no irq\n", dev->minor); + } + dev->board_name = "multiq3"; + dev->n_subdevices = 5; + result = alloc_subdevices(dev); + if(result<0)return result; + + s = dev->subdevices + 0; + /* ai subdevice */ + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 8; + s->trig[0] = multiq3_ai; + s->maxdata = 0x1fff; + s->range_type = RANGE_bipolar5; + + s = dev->subdevices + 1; + /* ao subdevice */ + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = 8; + s->trig[0] = multiq3_ao; + s->maxdata = 0xfff; + s->range_type = RANGE_bipolar5; + + s = dev->subdevices + 2; + /* di subdevice */ + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 16; + s->trig[0] = multiq3_di; + s->maxdata = 1; + s->range_type = RANGE_digital; + + s = dev->subdevices + 3; + /* do subdevice */ + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = 16; + s->trig[0] = multiq3_do; + s->maxdata = 1; + s->range_type = RANGE_digital; + s->state = 0; + + s = dev->subdevices + 4; + /* encoder (counter) subdevice */ + s->type = COMEDI_SUBD_COUNTER; + s->subdev_flags = SDF_READABLE | SDF_LSAMPL; + s->n_chan = it->options[2] * 2; + s->trig[0] = multiq3_ei; + s->maxdata = 0xffffff; + s->range_type = RANGE_unknown; + + encoder_reset(dev); + + return 0; +} + + +static int multiq3_detach(comedi_device * dev) +{ + printk("comedi%d: multiq3: remove\n", dev->minor); + + if (dev->iobase) { release_region(dev->iobase, dev->iosize); } + if (dev->irq) { free_irq(dev->irq,dev); } + + return 0; +} + + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_multiq3); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_multiq3); +} +#endif diff --git a/comedi/drivers/ni_atmio.c b/comedi/drivers/ni_atmio.c new file mode 100644 index 00000000..46a339e7 --- /dev/null +++ b/comedi/drivers/ni_atmio.c @@ -0,0 +1,409 @@ +/* + module/atmio-E.c + Hardware driver for NI AT-MIO E series cards + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + 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 + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_COMEDI_RT +#include +#endif +#include +#include +#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 + + 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 + + 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;isubdevices+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;ibuf_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;chann_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;idata[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;in_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;ibuf_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;in_chan;i++){ + mask=1<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<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<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;in_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;in_chans;j++){ + s->maxdata_list[chan]= + (1<n_bits)-1; + chan++; + } + } + }else{ + s->maxdata=(1<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(addrn_chans){ + bits=boardtype.caldac[i]->packbits(addr,val,&bitstring); + loadbit=SerDacLd(i); + break; + } + addr-=boardtype.caldac[i]->n_chans; + } + + for(bit=1<<(bits-1);bit;bit>>=1){ + ni_writeb(((bit&bitstring)?0x02:0),Serial_Command); + ni_writeb(1|((bit&bitstring)?0x02:0),Serial_Command); + } + ni_writeb(loadbit,Serial_Command); + ni_writeb(0,Serial_Command); +} + + + +static int pack_mb88341(int addr,int val,int *bitstring) +{ + /* + Fujitsu MB 88341 + Note that address bits are reversed. Thanks to + Ingo Keen for noticing this. + + Note also that the 88341 expects address values from + 1-12, whereas we use channel numbers 0-11. The NI + docs use 1-12, also, so be careful here. + */ + addr++; + *bitstring=((addr&0x1)<<11) | + ((addr&0x2)<<9) | + ((addr&0x4)<<7) | + ((addr&0x8)<<5) | + (val&0xff); + return 12; +} + +static int pack_dac8800(int addr,int val,int *bitstring) +{ + *bitstring=((addr&0x7)<<8)|(val&0xff); + return 11; +} + +static int pack_dac8043(int addr,int val,int *bitstring) +{ + *bitstring=val&0xfff; + return 12; +} + +static int pack_ad8522(int addr,int val,int *bitstring) +{ + *bitstring=(val&0xfff)|(addr ? 0xc000:0xa000); + return 16; +} + + + +/* + * + * Programmable Function Inputs + * + */ + +#if 0 + +static void pfi_setup(comedi_device *dev) +{ + /* currently, we don't output any signals, thus, all + the PFI's are input */ + + win_out(IO_Bidirection_Pin_Register,0); +} + + + +/* + * + * General Purpose Counter/Timer section + * + */ + + +/* event counting */ + +int gpct_start(comedi_device *dev,int chan) +{ + /* select load source */ + Gi_Load_Source_Select = 0; + + /* load initial counter value */ + Gi_Load_A = 0; + + /* strobe */ + Gi_Load=1; + +/* command reg */ + Gi_Up_Down = 1; /* up counting */ + Gi_Bank_Switch_Enable = 0; + Gi_Bank_Switch_Mode = 0; + +/* input select reg */ + Gi_Source_Select = PFIn; + Gi_Source_Polarity = 0; /* rising edges */ + Gi_Gate_Select = 31; /* logic low */ + Gi_OR_Gate = 0; + Gi_Output_Polarity = 1; /* active low */ + Gi_Gate_Select_Load_Source = 0; + +/* mode reg */ + Gi_Gate_Polarity = 0; /* disable inversion */ + Gi_Loading_On_Gate = 0; + Gi_Output_Mode = 1; /* one clock cycle output */ + Gi_Loading_On_TC = 0; + Gi_Gating_Mode = 1; + Gi_Gate_On_Both_Edges = 0; + Gi_Trigger_Mode_For_Edge_Gate = 2; + Gi_Stop_Mode = 0; + Gi_Counting_Once = 0; + + +/* int enable reg -- ignore */ + +/* + Gi_TC_Interrupt_Enable = 0; + Gi_Gate_Interrupt_Enable = 0; +*/ +} + +static int gpct_sp(comedi_device *dev,comedi_param *it) +{ + switch(it->pnum){ + case COMEDI_CTR_INPUT: + { + /* which input source? */ + int pval=it->pval; + + if(pval<0 || pval>=10)return -EINVAL; + changeparam(&dev->chinfo[it->chan].paramlist,COMEDI_CTR_INPUT, + 0,pval); + /* XXX set it */ + return 0; + } + case COMEDI_CTR_POLARITY: + /* count rising or falling edges? */ + if(it->pval<0 || it->pval>=4)return -EINVAL; + changeparam(&dev->chinfo[it->chan].paramlist,COMEDI_AREF, + 0,it->pval); + /* XXX set it */ + return 0; + case COMEDI_COUNT: + changeparam(&dev->chinfo[it->chan].paramlist,COMEDI_COUNT, + 0,it->pval); /* irrelevant... */ + devpriv->gpct[it->chan-boardtype->counteroffset]=it->pval; + return 0; + } + return -EINVAL; +} + +#endif + +static int ni_8255_callback(int dir,int port,int data,void *arg) +{ + comedi_device *dev=arg; + + if(dir){ + ni_writeb(25+2*port,data); + return 0; + }else{ + return ni_readb(25+2*port); + } +} + diff --git a/comedi/drivers/ni_pcidio.c b/comedi/drivers/ni_pcidio.c new file mode 100644 index 00000000..3edb64a4 --- /dev/null +++ b/comedi/drivers/ni_pcidio.c @@ -0,0 +1,431 @@ +/* + module/ni-dio.c + driver for National Instruments PCI-DIO-96 + National Instruments PCI-DIO-32HS + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1999 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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;iboard=i; + devpriv->mite=mite; + + return 0; + } + } + } + printk("no device found\n"); + return -EIO; +} + + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_pcidio); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_pcidio); +} +#endif diff --git a/comedi/drivers/ni_pcimio.c b/comedi/drivers/ni_pcimio.c new file mode 100644 index 00000000..12f1c56c --- /dev/null +++ b/comedi/drivers/ni_pcimio.c @@ -0,0 +1,430 @@ +/* + module/pcimio-E.c + Hardware driver for NI PCI-MIO E series cards + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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;iboard=i; + devpriv->mite=mite; + + return 0; + } + } + } + printk("no device found\n"); + return -EIO; +} + + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_pcimio); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_pcimio); +} +#endif diff --git a/comedi/drivers/ni_stc.h b/comedi/drivers/ni_stc.h new file mode 100644 index 00000000..ea2ae484 --- /dev/null +++ b/comedi/drivers/ni_stc.h @@ -0,0 +1,472 @@ +/* + module/ni_stc.h + Register descriptions for NI DAQ-STC chip + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998-9 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/* + References: + DAQ-STC Technical Reference Manual +*/ + +#ifndef _COMEDI_NI_STC_H +#define _COMEDI_NI_STC_H + +#define _bit15 0x8000 +#define _bit14 0x4000 +#define _bit13 0x2000 +#define _bit12 0x1000 +#define _bit11 0x0800 +#define _bit10 0x0400 +#define _bit9 0x0200 +#define _bit8 0x0100 +#define _bit7 0x0080 +#define _bit6 0x0040 +#define _bit5 0x0020 +#define _bit4 0x0010 +#define _bit3 0x0008 +#define _bit2 0x0004 +#define _bit1 0x0002 +#define _bit0 0x0001 + +/* Registers in the National Instruments DAQ-STC chip */ + +#define Interrupt_A_Ack_Register 2 +#define G0_Gate_Interrupt_Ack _bit15 +#define G0_TC_Interrupt_Ack _bit14 +#define AI_Error_Interrupt_Ack _bit13 +#define AI_STOP_Interrupt_Ack _bit12 +#define AI_START_Interrupt_Ack _bit11 +#define AI_START2_Interrupt_Ack _bit10 +#define AI_START1_Interrupt_Ack _bit9 +#define AI_SC_TC_Interrupt_Ack _bit8 +#define AI_SC_TC_Error_Confirm _bit7 +#define G0_TC_Error_Confirm _bit6 +#define G0_Gate_Error_Confirm _bit5 + +#define AI_Status_1_Register 2 +#define Interrupt_A_St 0x8000 +#define AI_FIFO_Full_St 0x4000 +#define AI_FIFO_Half_Full_St 0x2000 +#define AI_FIFO_Empty_St 0x1000 +#define AI_Overrun_St 0x0800 +#define AI_Overflow_St 0x0400 +#define AI_SC_TC_Error_St 0x0200 +#define AI_START2_St 0x0100 +#define AI_START1_St 0x0080 +#define AI_SC_TC_St 0x0040 +#define AI_START_St 0x0020 +#define AI_STOP_St 0x0010 +#define G0_TC_St 0x0008 +#define G0_Gate_Interrupt_St 0x0004 +#define AI_FIFO_Request_St 0x0002 +#define Pass_Thru_0_Interrupt_St 0x0001 + +#define Interrupt_B_Ack_Register 3 +#define AO_Status_1_Register 3 +#define Interrupt_B_St _bit15 +#define AO_FIFO_Full_St _bit14 +#define AO_FIFO_Half_Full_St _bit13 +#define AO_FIFO_Empty_St _bit12 +#define AO_BC_TC_Error_St _bit11 +#define AO_START_St _bit10 +#define AO_Overrun_St _bit9 +#define AO_START1_St _bit8 +#define AO_BC_TC_St _bit7 +#define AO_UC_TC_St _bit6 +#define AO_UPDATE_St _bit5 +#define AO_UI2_TC_St _bit4 +#define G1_TC_St _bit3 +#define G1_Gate_Interrupt_St _bit2 +#define AO_FIFO_Request_St _bit1 +#define Pass_Thru_1_Interrupt_St _bit0 + + +#define AI_Command_2_Register 4 +#define AI_End_On_SC_TC _bit15 +#define AI_End_On_End_Of_Scan _bit14 +#define AI_START1_Disable _bit11 +#define AI_SC_Save_Trace _bit10 +#define AI_SI_Switch_Load_On_SC_TC _bit9 +#define AI_SI_Switch_Load_On_STOP _bit8 +#define AI_SI_Switch_Load_On_TC _bit7 +#define AI_SC_Switch_Load_On_TC _bit4 +#define AI_STOP_Pulse _bit3 +#define AI_START_Pulse _bit2 +#define AI_START2_Pulse _bit1 +#define AI_START1_Pulse _bit0 + +#define AO_Command_2_Register 5 +#define AO_End_On_BC_TC(x) ((x)<<14) +#define AO_Start_Stop_Gate_Enable _bit13 +#define AO_UC_Save_Trace _bit12 +#define AO_BC_Gate_Enable _bit11 +#define AO_BC_Save_Trace _bit10 +#define AO_UI_Switch_Load_On_BC_TC _bit9 +#define AO_UI_Switch_Load_On_Stop _bit8 +#define AO_UI_Switch_Load_On_TC _bit7 +#define AO_UC_Switch_Load_On_BC_TC _bit6 +#define AO_UC_Switch_Load_On_TC _bit5 +#define AO_BC_Switch_Load_On_TC _bit4 +#define AO_Mute_B _bit3 +#define AO_Mute_A _bit2 +#define AO_UPDATE2_Pulse _bit1 +#define AO_START1_Pulse _bit0 + +#define DIO_Input_Register 7 +#define AI_Command_1_Register 8 + +#define AO_Command_1_Register 9 +#define AO_Analog_Trigger_Reset _bit15 +#define AO_START_Pulse _bit14 +#define AO_Disarm _bit13 +#define AO_UI2_Arm_Disarm _bit12 +#define AO_UI2_Load _bit11 +#define AO_UI_Arm _bit10 +#define AO_UI_Load _bit9 +#define AO_UC_Arm _bit8 +#define AO_UC_Load _bit7 +#define AO_BC_Arm _bit6 +#define AO_BC_Load _bit5 +#define AO_DAC1_Update_Mode _bit4 +#define AO_LDAC1_Source_Select _bit3 +#define AO_DAC0_Update_Mode _bit2 +#define AO_LDAC0_Source_Select _bit1 +#define AO_UPDATE_Pulse _bit0 + + +#define DIO_Output_Register 10 +#define DIO_Control_Register 11 +#define AI_Mode_1_Register 12 + +#define AI_Mode_2_Register 13 +#define AI_SC_Gate_Enable _bit15 +#define AI_Start_Stop_Gate_Enable _bit14 +#define AI_Pre_Trigger _bit13 +#define AI_External_MUX_Present _bit12 +#define AI_SI2_Ininial_Load_Source _bit9 +#define AI_SI2_Reload_Mode _bit8 +#define AI_SI_Initial_Load_Source _bit7 +#define AI_SI_Reload_Mode(a) ((a)<<4) +#define AI_SI_Write_Switch _bit3 +#define AI_SC_Initial_Load_Source _bit2 +#define AI_SC_Reload_Mode _bit1 +#define AI_SC_Write_Switch _bit0 + +#define AI_SI_Load_A_Registers 14 +#define AI_SI_Load_B_Registers 16 +#define AI_SC_Load_A_Registers 18 +#define AI_SC_Load_B_Registers 20 +#define AI_SI2_Load_A_Register 23 +#define AI_SI2_Load_B_Register 25 + +#define AO_Mode_1_Register 39 +#define AO_UPDATE_Source_Select(x) (((x)&0x1f)<<11) +#define AO_UI_Source_Select(x) (((x)&0x1f)<<6) +#define AO_Multiple_Channels _bit5 +#define AO_UPDATE_Source_Polarity _bit4 +#define AO_UI_Source_Polarity _bit3 +#define AO_UC_Switch_Load_Every_TC _bit2 +#define AO_Continuous _bit1 +#define AO_Trigger_Once _bit0 + +#define AO_Mode_2_Register 39 +#define AO_FIFO_Mode(x) ((x)<<14) +#define AO_FIFO_Retransmit_Enable _bit13 +#define AO_START1_Disable _bit12 +#define AO_UC_Initial_Load_Source _bit11 +#define AO_UC_Write_Switch _bit10 +#define AO_UI2_Initial_Load_Source _bit9 +#define AO_UI2_Reload_Mode _bit8 +#define AO_UI_Initial_Load_Source _bit7 +#define AO_UI_Reload_Mode(x) ((x)<<4) +#define AO_UI_Write_Switch _bit3 +#define AO_BC_Initial_Load_Source _bit2 +#define AO_BC_Reload_Mode _bit1 +#define AO_BC_Write_Switch _bit0 + +#define AO_UI_Load_A_Register_High 40 +#define AO_UI_Load_A_Register_Low 41 +#define AO_BC_Load_A_Register_High 44 +#define AO_BC_Load_A_Register_Low 45 +#define AO_BC_Load_B_Register_High 46 +#define AO_BC_Load_B_Register_Low 47 +#define AO_UC_Load_A_Register_High 48 +#define AO_UC_Load_A_Register_Low 49 + +#define Clock_and_FOUT_Register 56 +#define Interrupt_Control_Register 59 +#define AI_Output_Control_Register 60 + +#define AI_START_STOP_Select_Register 62 +#define AI_START_Polarity _bit15 +#define AI_STOP_Polarity _bit14 +#define AI_STOP_Sync _bit13 +#define AI_STOP_Edge _bit12 +#define AI_STOP_Select(a) ((a)<<7) +#define AI_START_Sync _bit6 +#define AI_START_Edge _bit5 +#define AI_START_Select(a) (a) + +#define AI_Trigger_Select_Register 63 +#define AO_Start_Select_Register 66 + +#define AO_Trigger_Select_Register 67 +#define AO_UI2_External_Gate_Enable _bit15 +#define AO_Delayed_START1 _bit14 +#define AO_START1_Polarity _bit13 +#define AO_UI2_Source_Polarity _bit12 +#define AO_UI2_Source_Select(x) (((x)&0x1f)<<7) +#define AO_START1_Sync _bit6 +#define AO_START1_Edge _bit5 +#define AO_START1_Select(x) (((x)&0x1f)<<0) + +#define AO_Mode_3_Register 70 +#define AO_UI2_Switch_Load_Next_TC _bit13 +#define AO_UC_Switch_Load_Every_BC_TC _bit12 +#define AO_Trigger_Length _bit11 +#define AO_Stop_On_Overrun_Error _bit5 +#define AO_Stop_On_BC_TC_Trigger_Error _bit4 +#define AO_Stop_On_BC_TC_Error _bit3 +#define AO_Not_An_UPDATE _bit2 +#define AO_Software_Gate _bit1 + +#define Joint_Reset_Register 72 +#define AO_Configuration_End _bit9 +#define AI_Configuration_End _bit8 +#define AO_Configuration_Start _bit5 +#define AI_Configuration_Start _bit4 +#define AO_Reset _bit1 +#define AI_Reset _bit0 + +#define Interrupt_A_Enable_Register 73 +#define Pass_Thru_0_Interrupt_Enable _bit9 +#define G0_Gate_Interrupt_Enable _bit8 +#define AI_FIFO_Interrupt_Enable _bit7 +#define G0_TC_Interrupt_Enable _bit6 +#define AI_Error_Interrupt_Enable _bit5 +#define AI_STOP_Interrupt_Enable _bit4 +#define AI_START_Interrupt_Enable _bit3 +#define AI_START2_Interrupt_Enable _bit2 +#define AI_START1_Interrupt_Enable _bit1 +#define AI_SC_TC_Interrupt_Enable _bit0 + +#define Interrupt_B_Enable_Register 75 +#define Pass_Thru_1_Interrupt_Enable _bit11 +#define G1_Gate_Interrupt_Enable _bit10 +#define G1_TC_Interrupt_Enable _bit9 +#define AO_FIFO_Interrupt_Enable _bit8 +#define AO_UI2_TC_Interrupt_Enable _bit7 +#define AO_UC_TC_Interrupt_Enable _bit6 +#define AO_Error_Interrupt_Enable _bit5 +#define AO_STOP_Interrupt_Enable _bit4 +#define AO_START_Interrupt_Enable _bit3 +#define AO_UPDATE_Interrupt_Enable _bit2 +#define AO_START1_Interrupt_Enable _bit1 +#define AO_BC_TC_Interrupt_Enable _bit0 + +#define Second_IRQ_B_Enable_Register 76 +#define AI_Personal_Register 77 +#define AO_Personal_Register 78 +#define Write_Strobe_0_Register 82 +#define Write_Strobe_1_Register 83 +#define Write_Strobe_2_Register 84 +#define Write_Strobe_3_Register 85 + +#define AO_Output_Control_Register 86 +#define AO_External_Gate_Enable _bit15 +#define AO_External_Gate_Select(x) (((x)&0x1f)<<10) +#define AO_Number_Of_Channels(x) (((x)&0xf)<<6) +#define AO_UPDATE2_Output_Select(x) (((x)&0x3)<<4) +#define AO_External_Gate_Polarity _bit3 +#define AO_UPDATE2_Output_Toggle _bit2 +#define AO_UPDATE_Output_Select(x) (((x)&0x3)<<0) + +#define AI_Mode_3_Register 87 +#define AI_Trigger_Length _bit15 +#define AI_Delay_START _bit14 +#define AI_Software_Gate _bit13 +#define AI_SI_Special_Trigger_Delay _bit12 +#define AI_SI2_Source_Select _bit11 +#define AI_Delayed_START2 _bit10 +#define AI_Delayed_START1 _bit9 +#define AI_External_Gate_Mode _bit8 +#define AI_FIFO_Mode_HF_to_E (3<<6) +#define AI_FIFO_Mode_F (2<<6) +#define AI_FIFO_Mode_HF (1<<6) +#define AI_FIFO_Mode_NE (0<<6) +#define AI_External_Gate_Polarity _bit5 +#define AI_External_Gate_Select(a) (a) + + +/* 16 bit registers shadowed from DAQ-STC */ +#define Window_Address 0x00 +#define Window_Data 0x02 +#define Interrupt_A_Ack 0x04 +#define AI_Status_1 0x04 +#define Interrupt_B_Ack 0x06 +#define AO_Status_1 0x06 +#define AI_Command_2 0x08 +#define G_Status 0x08 +#define AO_Command_2 0x0a +#define AI_Status_2 0x0a +#define G0_Command 0x0c +#define AO_Status_2 0x0c +#define G1_Command 0x0e +#define DIO_Parallel_Input 0x0e + +#define G_Autoincrement_Register(a) (68+(a)) +#define G_Command_Register(a) (6+(a)) +#define G_HW_Save_Register1(a) (8+(a)*2) +#define G_HW_Save_Register2(a) (9+(a)*2) +#define G_Input_Select_Register(a) (36+(a)) +#define G_Load_A_Register1(a) (28+(a)*4) +#define G_Load_A_Register2(a) (29+(a)*4) +#define G_Load_B_Register1(a) (30+(a)*4) +#define G_Load_B_Register2(a) (31+(a)*4) +#define G_Mode_Register(a) (26+(a)) +#define G_Save_Register1(a) (12+(a)*2) +#define G_Save_Register2(a) (13+(a)*2) +#define G_Status_Register 4 + +/* command register */ +#define G_Disarm_Copy _bit15 /* strobe */ +#define G_Save_Trace_Copy _bit14 +#define G_Arm_Copy _bit13 /* strobe */ +#define G_Bank_Switch_Enable _bit12 +#define G_Bank_Switch_Mode _bit11 +#define G_Bank_Switch_Start _bit10 /* strobe */ +#define G_Little_Big_Endian _bit9 +#define G_Synchronized_Gate _bit8 +#define G_Write_Switch _bit7 +#define G_Up_Down(a) (((a)&0x03)<<5) +#define G_Disarm _bit4 /* strobe */ +#define G_Analog_Trigger_Reset _bit3 /* strobe */ +#define G_Load _bit2 /* strobe */ +#define G_Save_Trace _bit1 +#define G_Arm _bit0 /* strobe */ + +/* input select register */ +#define G_Source_Polarity _bit15 +#define G_Output_Polarity _bit14 +#define G_OR_Gate _bit13 +#define G_Gate_Select_Load_Source _bit12 +#define G_Gate_Select(a) (((a)&0x1f)<<7) +#define G_Source_Select(a) (((a)&0x1f)<<2) +#define G_Write_Acknowledges_Irq _bit1 +#define G_Read_Acknowledges_Irq _bit0 + +/* mode register */ +#define G_Reload_Source_Switching _bit15 +#define G_Loading_On_Gate _bit14 +#define G_Gate_Polarity _bit13 +#define G_Loading_On_TC _bit12 +#define G_Counting_Once(a) (((a)&0x03)<<10) +#define G_Output_Mode(a) (((a)&0x03)<<8) +#define G_Load_Source_Select _bit7 +#define G_Stop_Mode(a) (((a)&0x03)<<5) +#define G_Trigger_Mode_For_Edge_Gate(a) (((a)&0x03)<<3) +#define G_Gate_On_Both_Edges _bit1 +#define G_Gating_Mode(a) (((a)&0x03)<<0) + + + +/* Additional windowed registers unique to E series */ + +#define Configuration_Memory_Clear 82 +#define ADC_FIFO_Clear 83 +#define DAC_FIFO_Clear 84 + + +/* i/o port offsets */ + +/* 8 bit registers */ +#define Status 0x01 +#define Serial_Command 0x0d +#define Misc_Command 0x0f +#define Port_A 0x19 +#define Port_B 0x1b +#define Port_C 0x1d +#define Configuration 0x1f +#define Strobes 0x01 +#define Channel_A_Mode 0x03 +#define Channel_B_Mode 0x05 +#define Channel_C_Mode 0x07 +#define AI_AO_Select 0x09 +#define G0_G1_Select 0x0b + +/* 16 bit registers */ +#define Configuration_Memory_Low 0x10 +#define Configuration_Memory_High 0x12 +#define ADC_FIFO_Data_Register 0x1c +#define AO_Configuration 0x16 +#define DAC_FIFO_Data 0x1e +#define DAC0_Direct_Data 0x18 +#define DAC1_Direct_Data 0x1a + + +#define SerDacLd(x) (0x08<<(x)) + +/* + This is stuff unique to the NI E series drivers, + but I thought I'd put it here anyway. +*/ + +enum{ ai_gain_16=0, ai_gain_8, ai_gain_14, ai_gain_8_602x }; +extern struct caldac_struct caldac_mb88341, + caldac_dac8800, + caldac_dac8043, + caldac_ad8522; + +typedef struct ni_board_struct{ + int device_id; + char *name; + + int n_adchan; + int adbits; + + int ai_fifo_depth; + int alwaysdither; + int gainlkup; + + int n_aochan; + int aobits; + + int ao_fifo_depth; + int aorangelkup; + + int ao_unipolar; + + int has_8255; + + struct caldac_struct **caldac; +}ni_board; + +static ni_board ni_boards[]; +#define n_ni_boards (sizeof(ni_boards)/sizeof(ni_board)) + +#define boardtype ni_boards[dev->board] + +#define NI_E_IRQ_FLAGS 0 + + + +#endif /* _COMEDI_NI_STC_H */ + diff --git a/comedi/drivers/pcl711.c b/comedi/drivers/pcl711.c new file mode 100644 index 00000000..b76ac640 --- /dev/null +++ b/comedi/drivers/pcl711.c @@ -0,0 +1,543 @@ +/* + module/pcl711.c + hardware driver for PC-LabCard PCL-711 and AdSys ACL-8112 + and compatibles + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + Janne Jalkanen + Eric Bunn + + 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#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;niobase + 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;in_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;in_chan;i++){ + chan=CR_CHAN(it->chanlist[i]); + mask=(1<data[i]) + data |= mask; + } + outb(data & 0xff, dev->iobase + PCL711_DO_LO); + outb((data >> 8), dev->iobase + PCL711_DO_HI); + s->state = data; + + return it->n_chan; +} + +/* Free any resources that we have claimed */ +static void free_resources(comedi_device * dev) +{ + if (dev->irq) + free_irq(dev->irq, dev); + + if (dev->iobase) + release_region(dev->iobase, dev->iosize); +} + +static int pcl711_recognize(char *name) +{ + int i; + + for (i = 0; i < n_boardtypes; i++) { + if (!strcmp(boardtypes[i].name, name)) { + return i; + } + } + return -1; +} + +/* Initialization */ +static int pcl711_attach(comedi_device * dev, comedi_devconfig * it) +{ + int ret; + int iobase; + int irq; + comedi_subdevice *s; + + /* claim our I/O space */ + + iobase = it->options[0]; + printk("comedi%d: pcl711: 0x%04x ", dev->minor, iobase); + if (check_region(iobase, PCL711_SIZE) < 0) { + printk("I/O port conflict\n"); + return -EIO; + } + request_region(dev->iobase, PCL711_SIZE, "pcl711"); + dev->iobase = iobase; + dev->iosize = PCL711_SIZE; + + /* there should be a sanity check here */ + + /* set up some name stuff */ + dev->board_name = boardtypes[dev->board].name; + + /* grab our IRQ */ + irq = it->options[1]; + if (irq < 0 || irq > boardtypes[dev->board].maxirq) { + printk("irq out of range\n"); + free_resources(dev); + return -EINVAL; + } + if (irq) { + if (request_irq(irq, pcl711_interrupt, SA_INTERRUPT, "pcl711", dev)) { + printk("unable to allocate irq %d\n", irq); + free_resources(dev); + return -EINVAL; + } else { + printk("( irq = %d )\n", irq); + } + } + dev->irq = irq; + + dev->n_subdevices = 4; + if((ret=alloc_subdevices(dev))<0) + return ret; + if((ret=alloc_private(dev,sizeof(pcl711_private)))<0) + return ret; + + s = dev->subdevices + 0; + /* AI subdevice */ + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE; + s->n_chan = this_board->n_aichan; + s->maxdata = 0xfff; + s->len_chanlist = 1; + s->timer_type = this_board->ai_timer_type; + s->range_type = this_board->ai_range_type; + s->trig[0] = pcl711_ai_mode0; + s->trig[1] = pcl711_ai_mode1; + s->trig[4] = pcl711_ai_mode4; + + s++; + /* AO subdevice */ + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = this_board->n_aochan; + s->maxdata = 0xfff; + s->len_chanlist = 1; + s->range_type = RANGE_bipolar5; + s->trig[0] = pcl711_ao; + + s++; + /* 16-bit digital input */ + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 16; + s->maxdata = 1; + s->len_chanlist = 16; + s->range_type = RANGE_digital; + s->trig[0] = pcl711_di; + + s++; + /* 16-bit digital out */ + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = 16; + s->maxdata = 1; + s->len_chanlist = 16; + s->range_type = RANGE_digital; + s->state=0; + s->trig[0] = pcl711_do; + + /* + this is the "base value" for the mode register, which is + used for the irq on the PCL711 + */ + if(this_board->is_pcl711b){ + devpriv->mode=(dev->irq<<4); + } + + /* clear DAC */ + outb(0, dev->iobase + PCL711_DA0_LO); + outb(0, dev->iobase + PCL711_DA0_HI); + outb(0, dev->iobase + PCL711_DA1_LO); + outb(0, dev->iobase + PCL711_DA1_HI); + + printk("\n"); + + return 0; +} + + +/* + * Removes device + */ + +static int pcl711_detach(comedi_device * dev) +{ + printk("comedi%d: pcl711: remove\n", dev->minor); + + free_resources(dev); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_pcl711); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_pcl711); +} +#endif diff --git a/comedi/drivers/pcl725.c b/comedi/drivers/pcl725.c new file mode 100644 index 00000000..daf9249f --- /dev/null +++ b/comedi/drivers/pcl725.c @@ -0,0 +1,121 @@ +/* + * Driver for PCL725 and clones + * David A. Schleef + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#define PCL725_SIZE 2 + +#define PCL725_DO 0 +#define PCL725_DI 1 + +static int pcl725_attach(comedi_device *dev,comedi_devconfig *it); +static int pcl725_detach(comedi_device *dev); +comedi_driver driver_pcl725={ + driver_name: "pcl725", + module: &__this_module, + attach: pcl725_attach, + detach: pcl725_detach, +}; + +static int pcl725_do(comedi_device *dev,comedi_subdevice *s,comedi_trig *it); +static int pcl725_di(comedi_device *dev,comedi_subdevice *s,comedi_trig *it); + +static int pcl725_do(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + do_pack(&s->state,it); + + outb(s->state,dev->iobase+PCL725_DO); + + return it->n_chan; +} + +static int pcl725_di(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + unsigned int bits; + + bits=inb(dev->iobase+PCL725_DI); + + return di_unpack(bits,it); +} + +static int pcl725_attach(comedi_device *dev,comedi_devconfig *it) +{ + comedi_subdevice *s; + + dev->iobase=it->options[0]; + printk("comedi%d: pcl725: 0x%04x ",dev->minor,dev->iobase); + if(check_region(dev->iobase,PCL725_SIZE)<0){ + printk("I/O port conflict\n"); + return -EIO; + } + request_region(dev->iobase,PCL725_SIZE,"pcl725"); + dev->board_name="pcl725"; + dev->iobase=dev->iobase; + dev->iosize=PCL725_SIZE; + dev->irq=0; + + dev->n_subdevices=2; + + if(alloc_subdevices(dev)<0) + return -ENOMEM; + + s=dev->subdevices+0; + /* do */ + s->type=COMEDI_SUBD_DO; + s->subdev_flags=SDF_WRITEABLE; + s->maxdata=1; + s->n_chan=8; + s->trig[0]=pcl725_do; + s->range_type=RANGE_digital; + + s=dev->subdevices+1; + /* do */ + s->type=COMEDI_SUBD_DI; + s->subdev_flags=SDF_READABLE; + s->maxdata=1; + s->n_chan=8; + s->trig[0]=pcl725_di; + s->range_type=RANGE_digital; + + printk("\n"); + + return 0; +} + + +static int pcl725_detach(comedi_device *dev) +{ + printk("comedi%d: pcl725: remove\n",dev->minor); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_pcl725); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_pcl725); +} +#endif diff --git a/comedi/drivers/pcl726.c b/comedi/drivers/pcl726.c new file mode 100644 index 00000000..c3dd430e --- /dev/null +++ b/comedi/drivers/pcl726.c @@ -0,0 +1,218 @@ +/* + module/pcl726.c + hardware driver for PC-LabCard PCL-726 and compatibles + ACL-6126 + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#undef PCL726_IRQ /* no interrupt support (yet) */ + + +#define PCL726_SIZE 16 + +#define PCL726_DAC0_HI 0 +#define PCL726_DAC0_LO 1 + +#define PCL726_DO_HI 12 +#define PCL726_DO_LO 13 +#define PCL726_DI_HI 14 +#define PCL726_DI_LO 15 + + +static int pcl726_attach(comedi_device *dev,comedi_devconfig *it); +static int pcl726_detach(comedi_device *dev); +comedi_driver driver_pcl726={ + driver_name: "pcl726", + module: &__this_module, + attach: pcl726_attach, + detach: pcl726_detach, +}; + + +typedef struct{ + int bipolar[6]; +}pcl726_private; +#define devpriv ((pcl726_private *)dev->private) + + +static int pcl726_ao(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + int hi,lo; + int chan=CR_CHAN(it->chanlist[0]); + + lo=it->data[0]&0xff; + hi=(it->data[0]>>8)&0xf; + if(devpriv->bipolar[chan])hi^=0x8; +/* + the programming info did not say which order to write bytes. + switch the order of the next two lines if you get glitches. +*/ + outb(hi,dev->iobase+PCL726_DAC0_HI + 2*chan); + outb(lo,dev->iobase+PCL726_DAC0_LO + 2*chan); + + return 1; +} + +static int pcl726_di(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + unsigned int bits; + + bits=inb(dev->iobase+PCL726_DI_LO)| + (inb(dev->iobase+PCL726_DI_HI)<<8); + + return di_unpack(bits,it); +} + +static int pcl726_do(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + do_pack(&s->state,it); + + outb(s->state&0xff,dev->iobase+PCL726_DO_LO); + outb((s->state>>8),dev->iobase+PCL726_DO_HI); + + return it->n_chan; +} + +static int pcl726_attach(comedi_device *dev,comedi_devconfig *it) +{ + comedi_subdevice *s; + int ret; + + dev->iobase=it->options[0]; + printk("comedi%d: pcl726: 0x%04x ",dev->minor,dev->iobase); + if(check_region(dev->iobase,PCL726_SIZE)<0){ + printk("I/O port conflict\n"); + return -EIO; + } + + dev->board_name="pcl726"; + +#ifdef PCL726_IRQ + irq=dev->options[1]; + if(!is_b)irq=0; + if(irq<0 || irq==1 || irq>7){ + printk("irq out of range\n"); + return -EIO; + } + if(irq){ + if(request_irq(irq,pcl726_interrupt,SA_INTERRUPT,"pcl726",dev)){ + printk("unable to allocate irq %d\n",irq); + irq=0; + }else{ + printk("( irq = %d )\n",irq); + } + } + dev->irq=irq; +#endif + + request_region(dev->iobase,PCL726_SIZE,"pcl726"); + dev->iosize=PCL726_SIZE; + + if((ret=alloc_private(dev,sizeof(pcl726_private)))<0) + return -ENOMEM; + + dev->n_subdevices=3; + if((ret=alloc_subdevices(dev))<0) + return ret; + + s=dev->subdevices+0; + /* ao */ + s->type=COMEDI_SUBD_AO; + s->subdev_flags=SDF_WRITEABLE; + s->n_chan=6; + s->maxdata=0xfff; + s->trig[0]=pcl726_ao; + s->range_type=RANGE_unknown; /* XXX */ + + s=dev->subdevices+1; + /* di */ + s->type=COMEDI_SUBD_DI; + s->subdev_flags=SDF_READABLE; + s->n_chan=16; + s->maxdata=1; + s->trig[0]=pcl726_di; + s->range_type=RANGE_digital; + + s=dev->subdevices+2; + /* do */ + s->type=COMEDI_SUBD_DO; + s->subdev_flags=SDF_WRITEABLE; + s->n_chan=16; + s->maxdata=1; + s->trig[0]=pcl726_do; + s->range_type=RANGE_digital; + + printk("\n"); + + return 0; +} + + +static int pcl726_detach(comedi_device *dev) +{ + printk("comedi%d: pcl726: remove\n",dev->minor); + +#ifdef PCL726_IRQ + if(dev->irq){ + free_irq(dev->irq,dev); + } +#endif + + release_region(dev->iobase,dev->iosize); + + return 0; +} + + + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_pcl726); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_pcl726); +} +#endif diff --git a/comedi/drivers/rti800.c b/comedi/drivers/rti800.c new file mode 100644 index 00000000..31657608 --- /dev/null +++ b/comedi/drivers/rti800.c @@ -0,0 +1,454 @@ +/* + module/rti800.c + hardware driver for Analog Devices RTI-800/815 board + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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 + +/* +--BEGIN-RANGE-DEFS-- +RANGE_rti800_ai_10_bipolar + -10 10 + -1 1 + -0.1 0.1 + -0.02 0.02 +RANGE_rti800_ai_5_bipolar + -5 5 + -0.5 0.5 + -0.05 0.05 + -0.01 0.01 +RANGE_rti800_ai_10_unipolar + 0 10 + 0 1 + 0 0.1 + 0 0.02 +---END-RANGE-DEFS--- +*/ + +static int rti800_attach(comedi_device *dev,comedi_devconfig *it); +static int rti800_detach(comedi_device *dev); +static int rti800_recognize(char *name); +comedi_driver driver_rti800={ + driver_name: "rti800", + module: &__this_module, + attach: rti800_attach, + detach: rti800_detach, + recognize: rti800_recognize, +}; + +static void rti800_interrupt(int irq, void *dev, struct pt_regs *regs); + +typedef struct { + enum { + adc_diff, adc_pseudodiff, adc_singleended + } adc_mux; + enum { + adc_bipolar10, adc_bipolar5, adc_unipolar10 + } adc_range; + enum { + adc_2comp, adc_straight + } adc_coding; + enum { + dac_bipolar10, dac_unipolar10 + } dac0_range, dac1_range; + enum { + dac_2comp, dac_straight + } dac0_coding, dac1_coding; + int ao_range_type_list[2]; +} rti800_private; + +#define devpriv ((rti800_private *)dev->private) + +#define RTI800_TIMEOUT 10 + +static void rti800_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + + +} + +static int gaindelay[]={10,20,40,80}; + +static int rti800_ai_mode0(comedi_device * dev, comedi_subdevice *s, comedi_trig * it) +{ + int i; + for(i=0 ; i < it->n_chan ; i++) { + int t, hi, lo, gain; + int chan; + int data; + int status; + + chan = CR_CHAN(it->chanlist[i]); + gain = CR_RANGE(it->chanlist[i]); + + inb(dev->iobase + RTI800_ADCHI); + outb(0,dev->iobase+RTI800_CLRFLAGS); + + outb(chan | (gain << 5), dev->iobase + RTI800_MUXGAIN); + + /* contrary to the docs, there needs to be a delay here */ + udelay(gaindelay[gain]); + + outb(0, dev->iobase + RTI800_CONVERT); + for (t = 0; t < RTI800_TIMEOUT; t++) { + status=inb(dev->iobase+RTI800_CSR); +#if DEBUG + rt_printk("status=%x\n",status); +#endif + if(status & RTI800_OVERRUN){ + rt_printk("rti800: a/d overflow\n"); + outb(0,dev->iobase+RTI800_CLRFLAGS); + + return -ETIME; + } + if (status & RTI800_DONE) + break; + udelay(8); + } + if(t==RTI800_TIMEOUT){ + rt_printk("rti800: timeout\n"); + + return -ETIME; + } + lo = inb(dev->iobase + RTI800_ADCLO); + hi = inb(dev->iobase + RTI800_ADCHI); + + data = (hi << 8) | lo; + data &= 0xfff; + if (devpriv->adc_coding == adc_2comp) { + data ^= 0x800; + } + it->data[i]=data; + } + return i; + +} + +static int rti800_ai_mode1(comedi_device * dev, comedi_subdevice *s, comedi_trig * it) +{ + return -EINVAL; +} + + +static int rti800_ao(comedi_device * dev, comedi_subdevice *s, comedi_trig * it) +{ + int i; + for(i=0 ; i < it->n_chan ; i++) { + int chan; + int data; + + chan=CR_CHAN(it->chanlist[i]); + data=it->data[i]; + + switch(chan){ + case 0: + if (devpriv->dac0_coding == dac_2comp) { + data ^= 0x800; + } + outb(data & 0xff, dev->iobase + RTI800_DAC0LO); + outb(data >> 8, dev->iobase + RTI800_DAC0HI); + break; + case 1: + if (devpriv->dac1_coding == dac_2comp) { + data ^= 0x800; + } + outb(data & 0xff, dev->iobase + RTI800_DAC1LO); + outb(data >> 8, dev->iobase + RTI800_DAC1HI); + break; + default: + return -EINVAL; + } + } + return i; +} + +static int rti800_di(comedi_device * dev, comedi_subdevice *s, comedi_trig * it) +{ + unsigned int bits; + + bits = inb(dev->iobase + RTI800_DI); + + return di_unpack(bits,it); +} + +static int rti800_do(comedi_device * dev, comedi_subdevice *s, comedi_trig * it) +{ + do_pack(&s->state,it); + + /* Outputs are inverted... */ + outb(s->state ^ 0xff, dev->iobase + RTI800_DO); + + return it->n_chan; +} + + +/* + options[0] - I/O port + options[1] - irq + options[2] - a/d mux + 0=differential, 1=pseudodiff, 2=single + options[3] - a/d range + 0=bipolar10, 1=bipolar5, 2=unipolar10 + options[4] - a/d coding + 0=2's comp, 1=straight binary + options[5] - dac0 range + 0=bipolar10, 1=unipolar10 + options[6] - dac0 coding + 0=2's comp, 1=straight binary + options[7] - dac1 range + options[8] - dac1 coding + */ + +static int rti800_recognize(char *name) +{ + if (!strcmp("rti800", name))return 0; + if (!strcmp("rti815", name))return 0; + + return -1; +} + +static int rti800_attach(comedi_device * dev, comedi_devconfig * it) +{ + int irq; + int iobase; + int ret; + comedi_subdevice *s; + + iobase = it->options[0]; + printk("comedi%d: rti800: 0x%04x ", dev->minor, iobase); + if (check_region(iobase, RTI800_SIZE) < 0) { + printk("I/O port conflict\n"); + return -EIO; + } + request_region(dev->iobase, RTI800_SIZE, "rti800"); + dev->iobase = iobase; + dev->iosize = RTI800_SIZE; + +#ifdef DEBUG + printk("fingerprint=%x,%x,%x,%x,%x ", + inb(dev->iobase + 0), + inb(dev->iobase + 1), + inb(dev->iobase + 2), + inb(dev->iobase + 3), + inb(dev->iobase + 4)); +#endif + + outb(0,dev->iobase+RTI800_CSR); + inb(dev->iobase+RTI800_ADCHI); + outb(0,dev->iobase+RTI800_CLRFLAGS); + + irq=it->options[1]; + if(irq>0){ + printk("( irq = %d )\n",irq); + if((ret=request_irq(irq,rti800_interrupt, SA_INTERRUPT, "rti800", dev))<0) + return ret; + dev->irq=irq; + }else if(irq == 0){ + printk("( no irq )"); + } + + if (dev->board == 0) + dev->board_name = "rti800"; + else + dev->board_name = "rti815"; + + + if (dev->board != 0) { + dev->n_subdevices=4; + }else{ + dev->n_subdevices=3; + } + if((ret=alloc_subdevices(dev))<0) + return ret; + if((ret=alloc_private(dev,sizeof(rti800_private)))<0) + return ret; + + devpriv->adc_mux = it->options[2]; + devpriv->adc_range = it->options[3]; + devpriv->adc_coding = it->options[4]; + devpriv->dac0_range = it->options[5]; + devpriv->dac0_coding = it->options[6]; + devpriv->dac1_range = it->options[7]; + devpriv->dac1_coding = it->options[8]; + + s=dev->subdevices+0; + /* ai subdevice */ + s->type=COMEDI_SUBD_AI; + s->subdev_flags=SDF_READABLE; + s->n_chan=(devpriv->adc_mux? 16 : 8); + s->trig[0]=rti800_ai_mode0; + s->trig[1]=rti800_ai_mode1; + s->maxdata=0xfff; + switch (devpriv->adc_range) { + case adc_bipolar10: + s->range_type = RANGE_rti800_ai_10_bipolar; + break; + case adc_bipolar5: + s->range_type = RANGE_rti800_ai_5_bipolar; + break; + case adc_unipolar10: + s->range_type = RANGE_rti800_ai_10_unipolar; + break; + } + + if (dev->board == 1) { + s++; + /* ao subdevice (only on rti815) */ + s->type=COMEDI_SUBD_AO; + s->subdev_flags=SDF_WRITEABLE; + s->n_chan=2; + s->trig[0]=rti800_ao; + s->maxdata=0xfff; + s->range_type=0; + s->range_type_list=devpriv->ao_range_type_list; + switch (devpriv->dac0_range) { + case dac_bipolar10: + devpriv->ao_range_type_list[0] = RANGE_bipolar10; + break; + case dac_unipolar10: + devpriv->ao_range_type_list[0] = RANGE_unipolar10; + break; + } + switch (devpriv->dac1_range) { + case dac_bipolar10: + devpriv->ao_range_type_list[1] = RANGE_bipolar10; + break; + case dac_unipolar10: + devpriv->ao_range_type_list[1] = RANGE_unipolar10; + break; + } + } + + s++; + /* di */ + s->type=COMEDI_SUBD_DI; + s->subdev_flags=SDF_READABLE; + s->n_chan=8; + s->trig[0]=rti800_di; + s->maxdata=1; + s->range_type=RANGE_digital; + + s++; + /* do */ + s->type=COMEDI_SUBD_DO; + s->subdev_flags=SDF_WRITEABLE; + s->n_chan=8; + s->trig[0]=rti800_do; + s->maxdata=1; + s->range_type=RANGE_digital; + + +/* don't yet know how to deal with counter/timers */ +#if 0 + s++; + /* do */ + s->type=COMEDI_SUBD_TIMER; + s->n_chan=0; + s->trig[0]=NULL; + s->maxdata=0 +#endif + + printk("\n"); + + return 0; +} + + +static int rti800_detach(comedi_device * dev) +{ + printk("comedi%d: rti800: remove\n", dev->minor); + + if(dev->iobase) + release_region(dev->iobase, dev->iosize); + + if(dev->irq) + free_irq(dev->irq,dev); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_rti800); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_rti800); +} +#endif diff --git a/comedi/drivers/rti802.c b/comedi/drivers/rti802.c new file mode 100644 index 00000000..8b52dad7 --- /dev/null +++ b/comedi/drivers/rti802.c @@ -0,0 +1,163 @@ +/* + module/rti802.c + hardware driver for Analog Devices RTI-802 board + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1999 Anders Blomdell + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define RTI802_SIZE 4 + +#define RTI802_SELECT 0 +#define RTI802_DATALOW 1 +#define RTI802_DATAHIGH 2 + +static int rti802_attach(comedi_device *dev,comedi_devconfig *it); +static int rti802_detach(comedi_device *dev); +comedi_driver driver_rti802={ + driver_name: "rti802", + module: &__this_module, + attach: rti802_attach, + detach: rti802_detach, +}; + +static void rti802_free_resources(comedi_device * dev); + +typedef struct { + enum { + dac_2comp, dac_straight + } dac_coding[8]; + unsigned int range_type_list[8]; +} rti802_private; + +#define devpriv ((rti802_private *)dev->private) + +static int rti802_ao(comedi_device * dev, comedi_subdevice *s, comedi_trig * it) +{ + int i; + for(i=0 ; i < it->n_chan ; i++) { + int chan = CR_CHAN(it->chanlist[i]); + int data = it->data[i]; + + if (devpriv->dac_coding[chan] == dac_2comp) { + data ^= 0x800; + } + outb(chan, dev->iobase + RTI802_SELECT); + outb(data & 0xff, dev->iobase + RTI802_DATALOW); + outb(data >> 8, dev->iobase + RTI802_DATAHIGH); + } + return i; +} + +/* + options: + [0] - i/o base + [1] - unused + [2] - dac#0 0=two's comp, 1=straight + [3] - dac#0 0=bipolar, 1=unipolar + [4] - dac#1 ... + ... + [17] - dac#7 ... + */ + +static int rti802_attach(comedi_device * dev, comedi_devconfig * it) +{ + comedi_subdevice *s; + int i; + + dev->iobase = it->options[0]; + printk("comedi%d: rti802: 0x%04x ", dev->minor, dev->iobase); + if (check_region(dev->iobase, RTI802_SIZE) < 0) { + printk("I/O port conflict\n"); + return -EIO; + } + request_region(dev->iobase, RTI802_SIZE, "rti802"); + + dev->board_name = "rti802"; + + dev->n_subdevices = 1; + if(alloc_subdevices(dev)<0 || alloc_private(dev,sizeof(rti802_private))){ + return -ENOMEM; + } + + s=dev->subdevices; + /* ao subdevice */ + s->type=COMEDI_SUBD_AO; + s->subdev_flags=SDF_WRITEABLE; + s->maxdata=0xfff; + s->n_chan=8; + s->trig[0] = rti802_ao; + s->range_type_list=devpriv->range_type_list; + + for (i = 0; i < 8; i++) { + devpriv->dac_coding[i] = (it->options[3 + 2 * i]) + ? (dac_straight) + : (dac_2comp); + devpriv->range_type_list[i] = (it->options[2 + 2 * i]) + ? RANGE_unipolar10 + : RANGE_bipolar10; + } + + printk("\n"); + + return 0; +} + +static void rti802_free_resources(comedi_device * dev) +{ + if(dev->iobase) + release_region(dev->iobase, RTI802_SIZE); +} + +static int rti802_detach(comedi_device * dev) +{ + printk("comedi%d: rti802: remove\n", dev->minor); + + rti802_free_resources(dev); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_rti802); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_rti802); +} +#endif diff --git a/comedi/dummy.c b/comedi/dummy.c new file mode 100644 index 00000000..44a71a8b --- /dev/null +++ b/comedi/dummy.c @@ -0,0 +1,125 @@ +/* + * dummy driver + * David A. Schleef + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* this structure is for data unique to this hardware driver. If + several hardware drivers keep similar information in this structure, + feel free to suggest moving the variable to the comedi_device struct. */ +typedef struct{ + int data; +}dummy_private; +#define devpriv ((dummy_private *)dev->private) + +static int dummy_attach(comedi_device *dev,comedi_devconfig *it); +static int dummy_detach(comedi_device *dev); +static int dummy_recognize(char *name); +comedi_driver driver_dummy={ + driver_name: "dummy", + attach: dummy_attach, + detach: dummy_detach, + recognize: dummy_recognize, +}; + +static int dummy_ai(comedi_device *dev,comedi_subdevice *s,comedi_trig *it); +static int dummy_ao(comedi_device *dev,comedi_subdevice *s,comedi_trig *it); + +static int dummy_recognize(char *name) +{ + if(!strcmp("dummy",name))return 0; + if(!strcmp("example",name))return 1; + + return -1; +} + +static int dummy_attach(comedi_device *dev,comedi_devconfig *it) +{ + comedi_subdevice *s; + +/* there is a *_init() function for each hardware driver installed. + Each one is called in sequence. + if it->name is a device that this hardware driver supports, we try + to initialize it. If everything works, return a 1. If there is + an error, return the error. If we do not match it->name, return a + 0 so that the next *_init() function can be tried. */ + +/* *dev is zeroed except for dev->minor, which contains the minor + number being used. *it has configuration information (not really + used here--see other drivers.) this function is responsible for + filling the rest of *dev. + + no longer true + */ + + printk("comedi%d: dummy: ",dev->minor); + + /* this is an appropriate place to put probe and initialization + code. remember to fill in dev->iobase and friends. */ + dev->board_name="dummy"; + + if(alloc_private(dev,sizeof(dummy_private))<0) + return -ENOMEM; +#if DEBUG + printk("malloc ok\n"); +#endif + + dev->n_subdevices=2; + if(alloc_subdevices(dev)<0) + return -ENOMEM; + + /* analog input subdevice */ + s=dev->subdevices+0; + s->type=COMEDI_SUBD_AI; + s->subdev_flags=SDF_READABLE; + s->n_chan=1; + s->maxdata=0xffff; + s->range_type=RANGE_unknown; + s->trig[0]=dummy_ai; + + /* analog output subdevice */ + s=dev->subdevices+1; + s->type=COMEDI_SUBD_AO; + s->subdev_flags=SDF_WRITEABLE; + s->n_chan=1; + s->maxdata=0xffff; + s->range_type=RANGE_unknown; + s->trig[0]=dummy_ao; + + return 1; +} + + +static int dummy_detach(comedi_device *dev) +{ + printk("comedi%d: dummy: remove\n",dev->minor); + + return 0; +} + +static int dummy_ai(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + it->data[0]=devpriv->data; + + return 0; +} + +static int dummy_ao(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + devpriv->data=it->data[0]; + + return 0; +} + diff --git a/comedi/kcomedilib/Makefile b/comedi/kcomedilib/Makefile new file mode 100644 index 00000000..fc530185 --- /dev/null +++ b/comedi/kcomedilib/Makefile @@ -0,0 +1,13 @@ + +ALL_SUB_DIRS := +MOD_SUB_DIRS := +SUB_DIRS := +MOD_LIST_NAME := MISC_MODULES + +EXTRA_CFLAGS := -I ../ + +M_OBJS := kcomedilib.o + + +include $(TOPDIR)/Rules.make + diff --git a/comedi/kcomedilib/kcomedilib.c b/comedi/kcomedilib/kcomedilib.c new file mode 100644 index 00000000..6c9a546c --- /dev/null +++ b/comedi/kcomedilib/kcomedilib.c @@ -0,0 +1,552 @@ +/* + kcomedilib/kcomedilib.c + a comedlib interface for kernel modules + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-2000 David A. Schleef + + 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef LINUX_V22 +#include +#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;in_subdevices;i++){ + s=dev->subdevices+i; + us=tmp+i; + + us->type = s->type; + us->n_chan = s->n_chan; + us->subd_flags = s->subdev_flags; + us->timer_type = s->timer_type; + us->len_chanlist = s->len_chanlist; + us->maxdata = s->maxdata; + us->range_type = s->range_type; + + if(s->busy) + us->subd_flags |= SDF_BUSY; + if(s->busy == file) + us->subd_flags |= SDF_BUSY_OWNER; + if(s->lock) + us->subd_flags |= SDF_LOCKED; + if(s->lock == file) + us->subd_flags |= SDF_LOCK_OWNER; + if(s->maxdata_list) + us->subd_flags |= SDF_MAXDATA; + if(s->flaglist) + us->subd_flags |= SDF_FLAGS; + if(s->range_type_list) + us->subd_flags |= SDF_RANGETYPE; + + } + + ret=copy_to_user(arg,tmp,dev->n_subdevices*sizeof(comedi_subdinfo)); + + kfree(tmp); + + return ret?-EFAULT:0; +} + + +/* + COMEDI_CHANINFO + subdevice info ioctl + + arg: + pointer to chaninfo structure + + reads: + chaninfo structure at arg + + writes: + arrays at elements of chaninfo structure + +*/ +static int do_chaninfo_ioctl(comedi_device *dev,comedi_chaninfo *arg) +{ + comedi_subdevice *s; + comedi_chaninfo it; + int ret; + + if(copy_from_user(&it,arg,sizeof(comedi_chaninfo))) + return -EFAULT; + + if(it.subdev>=dev->n_subdevices) + return -EINVAL; + s=dev->subdevices+it.subdev; + + if(it.flaglist){ + if(s->subdev_flags & SDF_FLAGS) + ret=copy_to_user(it.flaglist,s->flaglist,s->n_chan*sizeof(unsigned int)); + else + ret=clear_user(it.flaglist,s->n_chan*sizeof(unsigned int)); + if(ret)return -EFAULT; + } + + if(it.rangelist){ + if(s->subdev_flags & SDF_FLAGS) + ret=copy_to_user(it.rangelist,s->range_type_list,s->n_chan*sizeof(unsigned int)); + else + ret=clear_user(it.rangelist,s->n_chan*sizeof(unsigned int)); + if(ret)return -EFAULT; + } + + return 0; +} +#endif + + +/* + COMEDI_TRIG + trigger ioctl + + arg: + pointer to trig structure + + reads: + trig structure at arg + channel/range list + + writes: + modified trig structure at arg + data list + + this function is too complicated +*/ +int comedi_trig_ioctl(unsigned int minor,unsigned int subdev,comedi_trig *it) +{ + comedi_device *dev; + comedi_subdevice *s; + int ret=0; + + if((ret=minor_to_dev(minor,&dev))<0) + return ret; + + if(it->subdev>=dev->n_subdevices) + return -ENODEV; + + s=dev->subdevices+it->subdev; + if(s->type==COMEDI_SUBD_UNUSED) + return -EIO; + + /* is subdevice RT capable? (important!) */ + if(!(s->subdev_flags&SDF_RT)){ + ret=-EINVAL; + goto cleanup; + } + + /* are we locked? (ioctl lock) */ + if(s->lock && s->lock!=&rtcomedi_lock_semaphore) + return -EACCES; + + /* are we busy? */ + if(s->busy) + return -EBUSY; + s->busy=(void *)&rtcomedi_lock_semaphore; + + /* make sure channel/gain list isn't too long */ + if(it->n_chan > s->len_chanlist){ + ret = -EINVAL; + goto cleanup; + } + + /* make sure each element in channel/gain list is valid */ + if((ret=check_chanlist(s,it->n_chan,it->chanlist))<0) + goto cleanup; + + s->buf_user_ptr=0; + s->buf_user_count=0; + s->buf_int_ptr=0; + s->buf_int_count=0; + + if(it->data==NULL){ + ret=-EINVAL; + goto cleanup; + } + + if(!it->data_len){ +#if 0 + ret=-EINVAL; + goto cleanup; +#else + it->data_len=it->n_chan*it->n; + rt_printk("comedi: warning: trig->data_len not set\n"); +#endif + } + + if(it->mode>=5 || s->trig[it->mode]==NULL){ + ret=-EINVAL; + goto cleanup; + } + + s->cur_trig=*it; + + ret=s->trig[it->mode](dev,s,it); + + if(ret==0)return 0; + if(ret<0)goto cleanup; + + if(ret>it->n*it->n_chan){ + rt_printk("comedi: (bug) trig returned too many samples\n"); + } + +cleanup: + s->busy=NULL; + + return ret; +} + +/* + This function does the same as above, but without any sanity + checks. Any insanity in code calling this function must be + assumed by the writer. + */ +int __comedi_trig_ioctl(unsigned int minor,unsigned int subdev,comedi_trig *it) +{ + comedi_device *dev; + comedi_subdevice *s; + int ret=0; + + dev=comedi_devices+minor; + s=dev->subdevices+subdev; + + s->cur_trig=*it; + + ret=s->trig[it->mode](dev,s,it); + + return ret; +} + +/* + COMEDI_LOCK + lock subdevice + + arg: + subdevice number + + reads: + none + + writes: + none + + non-RT linux always controls rtcomedi_lock_semaphore. If an + RT-linux process wants the lock, it first checks rtcomedi_lock_semaphore. + If it is 1, it knows it is pre-empting this function, and fails. + Obviously, if RT-linux fails to get a lock, it *must* allow + linux to run, since that is the only way to free the lock. + + This function is not SMP compatible. + + necessary locking: + - ioctl/rt lock (this type) + - lock while subdevice busy + - lock while subdevice being programmed + +*/ +int comedi_lock_ioctl(unsigned int minor,unsigned int subdev) +{ + int ret=0; + comedi_subdevice *s; + comedi_device *dev; + + if(rtcomedi_lock_semaphore) + return -EBUSY; + + if((ret=minor_to_dev(minor,&dev))<0) + return ret; + + if(subdev>=dev->n_subdevices) + return -EINVAL; + s=dev->subdevices+subdev; + + if(s->busy) + return -EBUSY; + + /* &rtcomedi_lock_semaphore is just a convenient address */ + + if(s->lock && s->lock!=&rtcomedi_lock_semaphore){ + ret=-EACCES; + }else{ + s->lock=(void *)&rtcomedi_lock_semaphore; + + if(s->do_lock) + s->do_lock(dev,s); + } + + return ret; +} + + +/* + COMEDI_UNLOCK + unlock subdevice + + arg: + subdevice number + + reads: + none + + writes: + none + +*/ +int comedi_unlock_ioctl(unsigned int minor,unsigned int subdev) +{ + int ret=0; + comedi_subdevice *s; + comedi_device *dev; + + if(rtcomedi_lock_semaphore) + return -EBUSY; + + if((ret=minor_to_dev(minor,&dev))<0) + return ret; + + if(subdev>=dev->n_subdevices) + return -EINVAL; + s=dev->subdevices+subdev; + + if(s->busy) + return -EBUSY; + + if(s->lock && s->lock!=&rtcomedi_lock_semaphore) + return -EACCES; + + if(s->do_unlock) + s->do_unlock(dev,s); + + if(s->lock==&rtcomedi_lock_semaphore){ + s->lock=NULL; + + s->cb_mask=0; + s->cb_func=NULL; + s->cb_arg=NULL; + } + + return 0; +} + +/* + COMEDI_CANCEL + cancel acquisition ioctl + + arg: + subdevice number + + reads: + nothing + + writes: + nothing + +*/ +int comedi_cancel_ioctl(unsigned int minor,unsigned int subdev) +{ + int ret=0; + comedi_subdevice *s; + comedi_device *dev; + + if(rtcomedi_lock_semaphore) + return -EBUSY; + + if((ret=minor_to_dev(minor,&dev))<0) + return ret; + + if(subdev>=dev->n_subdevices) + return -EINVAL; + s=dev->subdevices+subdev; + + if(s->lock && s->lock!=&rtcomedi_lock_semaphore) + return -EACCES; + + if(!s->busy) + return 0; + + if(s->busy!=&rtcomedi_lock_semaphore) + return -EBUSY; + + if(!s->cancel) + return -EINVAL; + + if((ret=s->cancel(dev,s))) + return ret; + + s->busy=NULL; + + return 0; +} + +/* + registration of callback functions + + XXX - this needs additional work. Specifically, being SDF_RT is _not_ a + sufficient condition for being able to do callbacks. + */ +int comedi_register_callback(unsigned int minor,unsigned int subdev, + unsigned int mask,int (*cb)(unsigned int,void *),void *arg) +{ + comedi_device *dev; + comedi_subdevice *s; + int ret; + + if((ret=minor_to_dev(minor,&dev))<0) + return ret; + + if(subdev>=dev->n_subdevices) + return -ENODEV; + + s=dev->subdevices+subdev; + if(s->type==COMEDI_SUBD_UNUSED) + return -EIO; + + /* is subdevice RT capable? (important!) */ + if(!(s->subdev_flags&SDF_RT)) + return -EINVAL; + + /* are we locked? (ioctl lock) */ + if(s->lock && s->lock!=&rtcomedi_lock_semaphore) + return -EACCES; + + /* are we busy? */ + if(s->busy) + return -EBUSY; + + if(!mask){ + s->cb_mask=0; + s->cb_func=NULL; + s->cb_arg=NULL; + }else{ + s->cb_mask=mask; + s->cb_func=cb; + s->cb_arg=arg; + } + + return 0; +} + + diff --git a/comedi/kern_compat.h b/comedi/kern_compat.h new file mode 100644 index 00000000..77a69367 --- /dev/null +++ b/comedi/kern_compat.h @@ -0,0 +1,216 @@ +/* + kern_compat.h + Kernel compatibility header file + + Copyright (C) 1997-8 David A. Schleef + + 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 +#include +#include +#include +#include + +#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 + +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 +#define PCI_SUPPORT_VER1 +#else +#define PCI_SUPPORT_VER2 +#endif + +#if LINUX_VERSION_CODE < 0x20159 +#define DEV_FREE_SKB(skb) dev_kfree_skb (skb, FREE_WRITE); +#else /* Grrr, unneeded incompatible change. */ +#define DEV_FREE_SKB(skb) dev_kfree_skb(skb); +#endif + +#ifndef COMPAT_V22 + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,5) +typedef struct wait_queue *wait_queue_head_t; +#define DECLARE_WAITQUEUE(x,y) struct wait_queue x={y,NULL} +#define init_waitqueue_head(x) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,10) /* ? */ +#define file_atomic_inc(x) ((*(x))++) +#else +#define file_atomic_inc(x) atomic_inc(x) +#endif + + +#endif + + +#endif /* _KERN_COMPAT_H */ + + + + diff --git a/comedi/kvmem.c b/comedi/kvmem.c new file mode 100644 index 00000000..6ed3874a --- /dev/null +++ b/comedi/kvmem.c @@ -0,0 +1,90 @@ +#include "kvmem.h" + +/* allocate user space mmapable block of memory in the kernel space */ +void * rvmalloc(unsigned long size) +{ + void * mem; + unsigned long adr, page; + + mem=vmalloc(size); + if (mem) + { + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) + { +#if LINUX_VERSION_CODE < 0x020300 + page = kvirt_to_phys(adr); + mem_map_reserve(MAP_NR(phys_to_virt(page))); +#else + page = kvirt_to_pa(adr); + mem_map_reserve(MAP_NR(__va(page))); +#endif + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +void rvfree(void * mem, unsigned long size) +{ + unsigned long adr, page; + + if (mem) + { + adr=(unsigned long) mem; + while (size > 0) + { +#if LINUX_VERSION_CODE < 0x020300 + page = kvirt_to_phys(adr); + mem_map_unreserve(MAP_NR(phys_to_virt(page))); +#else + page = kvirt_to_pa(adr); + mem_map_unreserve(MAP_NR(__va(page))); +#endif + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } +} + +/* this function will map (fragment of) rvmalloc'ed memory area to user space */ +int rvmmap(void *mem, unsigned memsize, struct vm_area_struct *vma) { + unsigned long pos, size, start=vma->vm_start; +#if LINUX_VERSION_CODE < 0x20300 + unsigned long offset = vma->vm_offset; +#else + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; +#endif + /* this is not time critical code, so we check the arguments */ + /* vma->vm_offset HAS to be checked (and is checked)*/ + if (offset<0) + return -EFAULT; + size = vma->vm_end - vma->vm_start; + if (size + offset > memsize) + return -EFAULT; + pos = (unsigned long) mem + offset; + if (pos%PAGE_SIZE || start%PAGE_SIZE || size%PAGE_SIZE) + return -EFAULT; + + while (size>0) { +#if LINUX_VERSION_CODE < 0x020300 + if (remap_page_range(start,kvirt_to_phys(pos), PAGE_SIZE, + vma->vm_page_prot )) { + /* FIXME: what should we do here to unmap previous pages ?*/ + printk(KERN_ERR "rvmmap failed: vm_start=0x%lx, vm_end=0x%lx, size=0x%lx, pos=0x%lx; please report to motyl@stan.chemie.unibas.ch\n",vma->vm_start,vma->vm_end,size,pos); + return -EFAULT; + } +#else + if (remap_page_range(start, kvirt_to_pa(pos), + PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; +#endif + pos+=PAGE_SIZE; + start+=PAGE_SIZE; + size-=PAGE_SIZE; + } + return 0; +} diff --git a/comedi/kvmem.h b/comedi/kvmem.h new file mode 100644 index 00000000..a09427e8 --- /dev/null +++ b/comedi/kvmem.h @@ -0,0 +1,114 @@ +/*******************************/ +/* Memory management functions */ +/*******************************/ + +#ifndef _KVMEM_H +#define _KVMEM_H +#include +#include +#include +#include +#include + +/* convert virtual user memory address to physical address */ +/* (virt_to_phys only works for kmalloced kernel memory) */ + +#if LINUX_VERSION_CODE < 0x020300 +static inline unsigned long uvirt_to_phys(unsigned long adr) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *ptep, pte; + + pgd = pgd_offset(current->mm, adr); + if (pgd_none(*pgd)) + return 0; + pmd = pmd_offset(pgd, adr); + if (pmd_none(*pmd)) + return 0; + ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/); + pte = *ptep; + if(pte_present(pte)) + return virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1)))); + return 0; +} + +static inline unsigned long uvirt_to_bus(unsigned long adr) +{ + return virt_to_bus(phys_to_virt(uvirt_to_phys(adr))); +} + +/* convert virtual kernel memory address to physical address */ +/* (virt_to_phys only works for kmalloced kernel memory) */ + +static inline unsigned long kvirt_to_phys(unsigned long adr) +{ + return uvirt_to_phys(VMALLOC_VMADDR(adr)); +} + +static inline unsigned long kvirt_to_bus(unsigned long adr) +{ + return uvirt_to_bus(VMALLOC_VMADDR(adr)); +} + +#else + +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + + if(!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if(pte_present(pte)) + ret = (page_address(pte_page(pte))|(adr&(PAGE_SIZE-1))); + } + } + return ret; +} + +static inline unsigned long uvirt_to_bus(unsigned long adr) +{ + unsigned long kva, ret; + + kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); + ret = virt_to_bus((void *)kva); + + return ret; +} + +static inline unsigned long kvirt_to_bus(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = virt_to_bus((void *)kva); + + return ret; +} + +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + + return ret; +} + + +#endif + + +extern void * rvmalloc(unsigned long size); +extern void rvfree(void * mem, unsigned long size); +extern int rvmmap(void *mem, unsigned memsize, struct vm_area_struct *vma); +#endif diff --git a/comedi/mk_range.c b/comedi/mk_range.c new file mode 100644 index 00000000..aa855875 --- /dev/null +++ b/comedi/mk_range.c @@ -0,0 +1,130 @@ + + +#include +#include +#include + +#include "../include/comedi.h" + +char s[100]; +char name[100]; + +int get_flags(char *p); + +#define skip_white(a) {while(*(a) && isspace(*(a)))(a)++;} + +void help(void) +{ + fprintf(stderr, "Use the source, dumbass\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + char *p; + double a, b; + int i = 0, j = 0, ln = 0; + int mode = 0; + int flags = 0; + int n; + + if (argc != 2) + help(); + if (!strcmp(argv[1], "include")) { + mode = 1; + } else if (!strcmp(argv[1], "source")) { + mode = 2; + } else + help(); + + if(mode==1){ + }else{ + printf("#ifdef RANGE_C\n"); + printf("comedi_krange comedi_kranges[]={\n"); + } + while (1) { + fgets(s, 100, stdin); + ln++; + if (feof(stdin)) + break; + + s[strlen(s) - 1] = 0; + p = s; + skip_white(p); + if(p==s){ + if (mode == 1) { + if (i) + printf("%d)\n", j); + printf("#define %s __RANGE(%d,", p, i); + } else { + printf("/* %s */\n", p); + } + j = 0; + }else{ + if (isdigit(*p) || (*p) == '-') { + sscanf(p, "%lf %lf%n", &a, &b , &n); + p+=n; + + flags=get_flags(p); + if(flags<0){ + printf("\n\n#error while running scripts/mk_range, line %d, unknown flag\n", ln); + return 1; + } + + if (mode == 2) { + printf("\t{%d,%d,%d},\n", (int)(1e6*a), (int)(1e6*b),flags); + } + i++; + j++; + } else { + printf("\n\n#error while running scripts/mk_range, line %d\n", ln); + return 1; + } + } + } + if(mode==1){ + printf("%d)\n", j); + }else{ + printf("};\n"); + printf("unsigned int comedi_max_range=%d;\n",i); + printf("#endif\n"); + } + return 0; +} + +int get_flags(char *p) +{ + int flags=0; + + while(*p){ + skip_white(p); + if(!strncmp(p,"ext",3)){ + flags|=RF_EXTERNAL; + p+=3; + continue; + } + if(!strncmp(p,"mA",2)){ + flags|=UNIT_mA; + p+=2; + continue; + } + if(!strncmp(p,"volt",4)){ + flags|=UNIT_volt; + p+=4; + continue; + } + if(!strncmp(p,"none",4)){ + flags|=UNIT_none; + p+=4; + continue; + } + if(!strncmp(p,"unitless",8)){ + flags|=UNIT_none; + p+=8; + continue; + } + return -1; + } + return flags; +} + diff --git a/comedi/module.c b/comedi/module.c new file mode 100644 index 00000000..c514a6ce --- /dev/null +++ b/comedi/module.c @@ -0,0 +1,1598 @@ +/* + module/module.c + comedi kernel module + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef LINUX_V22 +#include +#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;in_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(mf_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(msubdev_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;in_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 \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;iminor,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;in_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;in_chan;i++){ + chan=CR_CHAN(it->chanlist[i]); + mask=1<data[i]) + (*bits) |=mask; + } + + return i; +} + +int mode_to_command(comedi_cmd *cmd,comedi_trig *it) +{ + memset(cmd,0,sizeof(comedi_cmd)); + cmd->subdev=it->subdev; + cmd->chanlist_len=it->n_chan; + cmd->chanlist=it->chanlist; + cmd->data=it->data; + cmd->data_len=it->data_len; + + cmd->start_src=TRIG_NOW; + + switch(it->mode){ + case 1: + cmd->scan_begin_src=TRIG_FOLLOW; + cmd->convert_src=TRIG_TIMER; + cmd->convert_arg=it->trigvar; + cmd->scan_end_src=TRIG_COUNT; + cmd->scan_end_arg=it->n_chan; + cmd->stop_src=TRIG_COUNT; + cmd->stop_arg=it->n; + + break; + case 2: + cmd->scan_begin_src=TRIG_TIMER; + cmd->scan_begin_arg=it->trigvar; + cmd->convert_src=TRIG_TIMER; + cmd->convert_arg=it->trigvar1; + cmd->scan_end_src=TRIG_COUNT; + cmd->scan_end_arg=it->n_chan; + cmd->stop_src=TRIG_COUNT; + cmd->stop_arg=it->n; + + break; + case 3: + cmd->scan_begin_src=TRIG_FOLLOW; + cmd->convert_src=TRIG_EXT; + cmd->convert_arg=it->trigvar; + cmd->scan_end_src=TRIG_COUNT; + cmd->scan_end_arg=it->n_chan; + cmd->stop_src=TRIG_COUNT; + cmd->stop_arg=it->n; + + break; + case 4: + cmd->scan_begin_src=TRIG_EXT; + cmd->scan_begin_arg=it->trigvar; + cmd->convert_src=TRIG_TIMER; + cmd->convert_arg=it->trigvar1; + cmd->scan_end_src=TRIG_COUNT; + cmd->scan_end_arg=it->n_chan; + cmd->stop_src=TRIG_COUNT; + cmd->stop_arg=it->n; + + break; + default: + return -EINVAL; + } + + return 0; +} + +int command_to_mode(comedi_trig *it,comedi_cmd *cmd) +{ + it->subdev=cmd->subdev; + it->flags=0; + it->n_chan=cmd->chanlist_len; + it->chanlist=cmd->chanlist; + it->data=cmd->data; + it->data_len=cmd->data_len; + + if(cmd->start_src==TRIG_NOW && + cmd->scan_begin_src==TRIG_FOLLOW && + cmd->convert_src==TRIG_TIMER && + cmd->scan_end_src==TRIG_COUNT && + cmd->stop_src==TRIG_COUNT){ + /* mode 1 */ + + it->mode=1; + it->trigsrc=0; + it->trigvar=0; + it->n=cmd->stop_arg; + + return 0; + } + if(cmd->start_src==TRIG_NOW && + cmd->scan_begin_src==TRIG_TIMER && + cmd->convert_src==TRIG_TIMER && + cmd->scan_end_src==TRIG_COUNT && + cmd->stop_src==TRIG_COUNT){ + /* mode 2 */ + + it->mode=2; + it->trigsrc=0; + it->trigvar=cmd->scan_begin_arg; + it->trigvar1=cmd->convert_arg; + it->n=cmd->stop_arg; + + return 0; + } + if(cmd->start_src==TRIG_NOW && + cmd->scan_begin_src==TRIG_FOLLOW && + cmd->convert_src==TRIG_EXT && + cmd->scan_end_src==TRIG_COUNT && + cmd->stop_src==TRIG_COUNT){ + /* mode 3 */ + /* nobody actually uses mode 3, so... */ + + return -EINVAL; + } + if(cmd->start_src==TRIG_NOW && + cmd->scan_begin_src==TRIG_EXT && + cmd->convert_src==TRIG_TIMER && + cmd->scan_end_src==TRIG_COUNT && + cmd->stop_src==TRIG_COUNT){ + /* mode 4 */ + + it->mode=4; + it->trigsrc=0; + it->trigvar=cmd->scan_begin_arg; + it->trigvar1=cmd->convert_arg; + it->n=cmd->stop_arg; + + return 0; + } + return -EINVAL; +} + +#ifdef CONFIG_COMEDI_RT + +static struct comedi_irq_struct *comedi_irqs; + +int comedi_request_irq(unsigned irq,void (*handler)(int, void *,struct pt_regs *), + unsigned long flags,const char *device,void *dev_id) +{ + struct comedi_irq_struct *it; + int ret; + + it=kmalloc(sizeof(*it),GFP_KERNEL); + if(!it) + return -ENOMEM; + + it->handler=handler; + it->irq=irq; + it->dev_id=dev_id; + it->flags=flags; + + ret=request_irq(irq,handler,flags&~SA_PRIORITY,device,dev_id); + if(ret<0){ + kfree(it); + return ret; + } + + if(flags&SA_PRIORITY){ + get_priority_irq(it); + } + + it->next=comedi_irqs; + comedi_irqs=it; + + return 0; +} + +int comedi_change_irq_flags(unsigned int irq,void *dev_id,unsigned long flags) +{ + struct comedi_irq_struct *it; + + it=get_irq_struct(irq); + if(it){ + if((it->flags&~SA_PRIORITY)!=(flags&~SA_PRIORITY)) + return -EINVAL; + + if((it->flags&SA_PRIORITY)==(flags&SA_PRIORITY)) + return 0; + + it->flags=flags; + if(flags&SA_PRIORITY){ + return get_priority_irq(it); + }else{ + return free_priority_irq(it); + } + } + + return -EINVAL; +} + +void comedi_free_irq(unsigned int irq,void *dev_id) +{ + struct comedi_irq_struct *it,*prev; + + prev=NULL; + for(it=comedi_irqs;it;it=it->next){ + if(it->irq==irq){ + break; + } + prev=it; + } + if(it->flags&SA_PRIORITY) + free_priority_irq(it); + + free_irq(it->irq,it->dev_id); + + if(prev) prev->next=it->next; + else comedi_irqs=it->next; + + kfree(it); +} + +struct comedi_irq_struct *get_irq_struct(unsigned int irq) +{ + struct comedi_irq_struct *it; + + for(it=comedi_irqs;it;it=it->next){ + if(it->irq==irq){ + return it; + } + } + return NULL; +} + +#endif + diff --git a/comedi/old/atmio-16d.c b/comedi/old/atmio-16d.c new file mode 100644 index 00000000..a427de38 --- /dev/null +++ b/comedi/old/atmio-16d.c @@ -0,0 +1,116 @@ +/* + module/atmio-16d.c + hardware driver for National Instruments AT-MIO-16D + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1995 Claus Schroeter + Copyright (C) 1998 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/* + This driver is an adaptation of one written by clausi + + specifications can be found in NI document 320489.pdf +*/ + +#define Command_Register_1 0x00 /* wo */ +#define Status_Register 0x00 /* ro */ +#define Command_Register_2 0x02 /* wo */ + +#define Start_Convert_Register 0x08 /* wo */ +#define Start_DAQ_Register 0x0a /* wo */ +#define AD_Clear_Register 0x0c /* wo */ +#define External_Strobe_Register 0x0e /* wo */ + +#define DAC0_Register 0x10 /* wo */ +#define DAC1_Register 0x12 /* wo */ +#define INT2CLR_Register 0x14 /* wo */ + +#define Mux_Counter_Register 0x04 /* wo */ +#define Mux_Gain_Register 0x06 /* wo */ +#define AD_FIFO_Register 0x16 /* ro */ +#define DMA_TC_INT_Clear_Register 0x16 /* wo */ + +#define Am9513A_Data_Register 0x18 /* rw */ +#define Am9513A_Command_Register 0x1a /* wo */ +#define Am9513A_Status_Register 0x1a /* ro */ + +#define MIO_16_Digital_Input_Register 0x1c /* ro */ +#define MIO_16_Digital_Output_Register 0x1c /* wo */ + +#define RTSI_Switch_Shift_Register 0x1e /* wo 8 */ +#define RTSI_Switch_Strobe_Register 0x1f /* wo 8 */ + +#define DIO_24_PORTA_Register 0x00 /* rw 8 */ +#define DIO_24_PORTB_Register 0x01 /* rw 8 */ +#define DIO_24_PORTC_Register 0x02 /* rw 8 */ +#define DIO_24_CNFG_Register 0x03 /* wo 8 */ + + +#define _B(b) ((struct bitchan){_B_CHAN,(b)}) + +#define _B_CHAN Command_Register_1 +#define DAQSTOPINTEN _bit9 +#define TCINTEN _bit8 +#define CONVINTEN _bit7 +#define DBDMA _bit6 +#define DMAEN _bit5 +#define DAQEN _bit4 +#define SCANEN _bit3 +#define SCANDIV _bit2 +#define CNT32 _bit1 +#define TWOSCADC _bit0 +#undef _B_CHAN + +#define _B_CHAN Status_Register +#define GINT _bit15 +#define DAQSTOPINT _bit14 +#define CONVAVAIL _bit13 +#define OUT2INT _bit12 +#define DAQPROG _bit11 +#define DMATCINT _bit10 +#define OVERFLOW _bit9 +#define OVERRUN _bit8 +#define GAIN1 _bit7 +#define GAIN0 _bit6 +#define DMACH _bit5 +#define MUX1EN _bit4 +#define MUX0EN _bit3 +#define MA2 _bit2 +#define MA1 _bit1 +#define MA0 _bit0 +#undef _B_CHAN + +#define _B_CHAN Command_Register_2 +#define DOUTEN1 _bit9 +#define DOUTEN0 _bit8 +#define INTEN _bit7 +#define INT2EN _bit6 +#define LDAC _bit5 +#define SCN2 _bit4 +#define A4RCV _bit3 +#define A4DRV _bit2 +#define A2RCV _bit1 +#define A2DRV _bit0 +#undef _B_CHAN + + + + + + diff --git a/comedi/old/atmio-16f.c b/comedi/old/atmio-16f.c new file mode 100644 index 00000000..4829992c --- /dev/null +++ b/comedi/old/atmio-16f.c @@ -0,0 +1,528 @@ +/* + module/ni_F.c + hardware driver for National Instruments AT-MIO-16F + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1995 Claus Schroeter + Copyright (C) 1999 David A. Schleef + + 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 +#include +#include +#include + +#define ATMIO16F_SIZE 16 + +#define Command_Register_1 0x00 /* wo */ +#define Command_Register_2 0x02 /* wo */ +#define Command_Register_3 0x04 /* wo */ +#define Command_Register_4 0x06 /* wo */ +#define Status_Register_1 0x18 /* ro */ +#define Status_Register_2 0x1a /* ro */ + +#define ADC_FIFO_Register 0x00 /* ro */ +#define CONFIGMEM_Register 0x08 /* wo */ + +#define DAC0_Register 0x10 /* wo */ +#define DAC1_Register 0x12 /* wo */ + +#define CONFIGMEMCLR_Register 0x1b /* ro 8 */ +#define CONFIGMEMLD_Register 0x1b /* wo 8 */ +#define DAQ_Clear_Register 0x19 /* ro 8 */ +#define DAQ_Start_Register 0x1d /* ro 8 */ +#define Single_Conversion_Register 0x1d /* wo 8 */ +#define ADC_Calibration_Register 0x1f /* wo 8 */ + +#define TMRREQ_Clear_Register 0x1f /* ro 8 */ +#define DAC_Update_Register 0x18 /* wo */ +#define DAC_Clear_Register 0x1e /* ro 8 */ + +#define DMA_Channel_Clear_Register 0x0b /* ro 8 */ +#define DMATCA_Clear_Register 0x19 /* wo 8 */ +#define DMATCB_Clear_Register 0x09 /* ro 8 */ +#define External_Strobe_Register 0x1e /* wo 8 */ +#define Calibration_DAC_0_Load_Register 0x0a /* wo 8 */ +#define Calibration_DAC_1_Load_Register 0x1a /* wo 8 */ + +#define Am9513A_Data_Register 0x14 /* rw */ +#define Am9513A_Command_Register 0x16 /* wo */ +#define Am9513A_Status_Register 0x16 /* ro */ + +#define Digital_Input_Register 0x1c /* ro */ +#define Digital_Output_Register 0x1c /* wo */ + +#define RTSI_Switch_Shift_Register 0x0c /* wo 8 */ +#define RTSI_Switch_Strobe_Register 0x0e /* wo 8 */ + + +#define EEPROMCS _bit15 +#define SDATA _bit14 +#define SCLK _bit13 +#define SCANDIV _bit12 +#define INTGATE _bit10 +#define RETRIG_DIS _bit9 +#define DAQEN _bit8 +#define SCANEN _bit7 +#define SCN2 _bit6 +#define CNT32 _bit5 +#define RTSITRIG _bit4 + +#define A4RCV _bit15 +#define A4DRV _bit14 +#define A2RCV _bit13 +#define A2DRV _bit12 +#define BIPDAC1 _bit11 +#define BIPDAC0 _bit10 +#define EXTREFDAC1 _bit9 +#define EXTREFDAC0 _bit8 +#define EISA_DMA _bit7 +#define DMACHBB _bits543 +#define DMACHAB _bits210 + + + +/* stuff i don't know */ +#define MIO_ADCLR 0 +#define BIPOLAR 0 +#define DIFF 0 +#define MIO_SCONV 0 +#define MIO_SR 0 +#define FIFOEF 0 +#define MIO_ADFIFO 0 +#define mio_reset_bitcmd(a,b) /* */ +#define mio_set_bitcmd(a,b) /* */ + + + +#define DB_ERROR (1<<0) +#define DB_OVERFLOW (1<<1) +#define DB_HALF (1<<2) +#define DB_EMPTY (1<<3) + +#define DAQ_RUNNING (1<<0) +#define DAQ_DACUP (1<<1) +#define DAQ_SCONV (1<<2) +#define DAQ_BIPOLAR (1<<3) +#define DAQ_DIFF (1<<4) +#define DAQ_SYNC (1<<5) + + +typedef struct { + int adc; + int dac0; +} AdcVal; + +typedef struct{ + int DaqMode; + int CurrentDac; + int WgenStat; + int admode[16]; +}ni_F_private; +#define devpriv ((ni_F_private *)dev->private) + +int atmio16f_rem(comedi_device *dev); + +#if 0 +static void mio_SetDaqMode(int mode) +{ + devpriv->DaqMode = mode; +} +#endif + +/**********************************************************************/ +static int atmio16f_ai(comedi_device * dev, comedi_subdevice *s, comedi_trig * it) +{ + int chan,aref,range; + int i; + + chan=CR_CHAN(it->chanlist[0]); + range=CR_RANGE(it->chanlist[0]); + aref=CR_AREF(it->chanlist[0]); + + outw(0x00, dev->iobase + MIO_ADCLR); + + /* set ADC UNIPOLAR/BIPOLAR mode */ + if (range) + mio_reset_bitcmd(BIPOLAR, 0); + else + mio_set_bitcmd(BIPOLAR, 0); + + /* set ADC DIFF/RSE mode */ + if (aref) /* XXX check */ + mio_set_bitcmd(DIFF, 0); + else + mio_reset_bitcmd(DIFF, 0); + + outw(0x00, dev->iobase + MIO_SCONV); + + /* poll status register */ + for(i=0;i<25;i++){ + if( inw(dev->iobase + MIO_SR) & FIFOEF ) + break; + udelay(5); + } + if(i==25){ + comedi_error(dev,"timeout"); + return -ETIME; + } + + /* read value */ + it->data[0] = inw(dev->iobase + MIO_ADFIFO); + + return 1; +} + + +#if 0 +/*********************************************************************** + * + * Doublebuffer FIFO mechanism + * + */ + +#define MAX_DBUFSIZE 1024 + +struct wait_queue *DaqWait = NULL; +struct semaphore DaqSem = MUTEX; + + +unsigned int DblBuf[2][MAX_DBUFSIZE + 1]; + +unsigned int DblBufState = 0; + +unsigned int ActualReadCount = 0; +unsigned int ActualWriteCount = 0; +unsigned int ActualResidue = 0; + +unsigned int ActualWrite = 0; +unsigned int ActualRead = 0; + +/***********************************************************************/ +void mio_DaqInit(void) +{ + + DblBufState = 0; + ActualResidue = 0; + ActualWriteCount = 0; + ActualReadCount = 0; + + ActualRead = 0; + ActualWrite = 0; + DaqMode = 0; + + /*clear A/D circuit */ + outw(0x0000, MIO_ADCLR); + +} + + +/**********************************************************************/ +void mio_SwapWrite(void) +{ + ActualWrite = (ActualWrite ? 0 : 1); + DBGprint(DBG_DATA, ("Swapped Write to %d", ActualWrite)); + ActualWriteCount = 0; + if (ActualResidue >= (2 * MAX_DBUFSIZE)) { /* overflow buffer bit set */ + DBGprint(DBG_DATA, ("Buffer Overflow")); + DblBufState |= (DB_ERROR | DB_OVERFLOW); + } +} + +/**********************************************************************/ +void mio_SwapRead(void) +{ + ActualRead = (ActualRead ? 0 : 1); + DBGprint(DBG_DATA, ("Swapped Read to %d", ActualRead)); + ActualReadCount = 0; +} + +/**********************************************************************/ +void mio_AddDBuf(unsigned int val) +{ + if (ActualWriteCount < MAX_DBUFSIZE) { + DblBuf[ActualWrite][ActualWriteCount] = val; + ActualWriteCount++; + ActualResidue++; + } else { + mio_SwapWrite(); + } + if (ActualResidue >= MAX_DBUFSIZE) { + DblBufState |= DB_HALF; + if (DaqMode & DAQ_SYNC) + wake_up_interruptible(&DaqWait); + } +} + +/**********************************************************************/ +unsigned int mio_GetDBuf(void) +{ + unsigned int ret = 0; + + if (ActualResidue > 0) { + if (ActualReadCount < MAX_DBUFSIZE) { + ret = DblBuf[ActualRead][ActualReadCount]; + ActualReadCount++; + ActualResidue--; + } else { + mio_SwapRead(); + } + if (ActualResidue < MAX_DBUFSIZE) + DblBufState &= ~DB_HALF; + } else { + ret = 0; + DblBufState |= DB_EMPTY; + } + + DBGprint(DBG_DATA, ("residue=%d", ActualResidue)); + return ret; +} + +/**********************************************************************/ +int mio_SyncRead(int *buf, int count) +{ + int retval, i; + int dummy; + char *tbuf; + + + if (retval = verify_area(VERIFY_WRITE, buf, count)) + return retval; + + + if (DblBufState & DB_EMPTY) { + DBGprint(DBG_DATA, ("Buffer Empty")); + return -EINTR; + } + if (!(DblBufState & DB_HALF) && (DaqMode & DAQ_RUNNING)) { + DBGprint(DBG_DATA, ("\nWaiting for DB_HALF")); + DaqMode |= DAQ_SYNC; + interruptible_sleep_on(&DaqWait); + DaqMode &= ~DAQ_SYNC; + DBGprint(DBG_DATA, ("\nGot DB_HALF")); + } + if (DblBufState & DB_OVERFLOW) { + DBGprint(DBG_DATA, ("Double Buffer Overflow")); + return -EOVERFLOW; + } + /* copy block to FS */ + for (i = 0; i < count && i < ActualResidue; i++) { + dummy = mio_GetDBuf(); + DBGprint(DBG_DATA, ("buf=%d", dummy)); + if (DblBufState & DB_EMPTY) { + DBGprint(DBG_DATA, ("count=%d", i)); + return i; + } + put_fs_long(dummy, (int *) &(buf[i])); + /*memcpy_tofs((int *) buf, &dummy ,sizeof(int)); */ + } + + return count; + +} + +/**********************************************************************/ +void mio_DaqFinish(void) +{ + + DaqMode &= ~DAQ_RUNNING; + if (DaqMode & DAQ_SYNC) + wake_up_interruptible(&DaqWait); +} + +/**********************************************************************/ +void mio_DaqStart(int mode) +{ + + DBGprint(DBG_DATA, ("mode (0x%x)", mode)); + + mio_DaqInit(); + + DaqMode |= (DAQ_RUNNING | mode); + + /*clear A/D circuit */ + outw(0x0000, MIO_ADCLR); + + /* set ADC UNIPOLAR/BIPOLAR mode */ + if (DaqMode & DAQ_BIPOLAR) + mio_reset_bitcmd(BIPOLAR, 0); + else + mio_set_bitcmd(BIPOLAR, 0); + + /* set ADC DIFF/RSE mode */ + if (DaqMode & DAQ_DIFF) + mio_set_bitcmd(DIFF, 0); + else + mio_reset_bitcmd(DIFF, 0); + + if (DaqMode & DAQ_SCONV) { + DBGprint(DBG_DATA, ("trigger conversion")); + outw(0x00, MIO_SCONV); /* trigger first value */ + while (!(inw(MIO_SR) & FIFOEF)); /* sync FIFOEF */ + } + if (DaqMode & DAQ_DACUP) + printk("WGEN MODE\n"); + +} + +/***********************************************************************/ +void mio_DaqIRQ(int status) +{ + comedi_sample s; + + if (DaqMode & DAQ_RUNNING) { + + /* read FIFO content into Buffer */ + s.chan = dev->adchan; + s.job = dev->job; + while (inw(MIO_SR) & FIFOEF) { + + it->data = inw(dev->iobase + MIO_ADFIFO); + + } + + /* trigger next conversion */ + if (DaqMode & DAQ_SCONV) { + outw(0x0000, dev->iobase + MIO_SCONV); + } + if ((DaqMode & DAQ_DACUP) && !(WgenStat & WGEN_RUNNING)) { +#if DEBUG + if (dbgMask) + printk("Finish DACUP DAQ Cycle\n"); +#endif + DaqMode &= ~DAQ_RUNNING; + while (!(inw(MIO_SR) & FIFOEF)); /* poll status */ + val = inw(MIO_ADFIFO) | (CurrentDac << 16); + mio_AddDBuf(val); + + if (DaqMode & DAQ_SYNC) + wake_up_interruptible(&DaqWait); + } + } else + printk("DAQ-INTERRUPT unknown status (0x%x)", status); + +} + +#endif + +int atmio16f_ao(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + return -EINVAL; +} + +int atmio16f_di(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + return -EINVAL; +} + +int atmio16f_do(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + return -EINVAL; +} + + +int atmio16f_init(comedi_device *dev,comedi_devconfig *it) +{ + int ret=0; + comedi_subdevice *s; + + if(strcmp(it->board_name,"at-mio-16f-5")) + return 0; + + dev->driver_name="atmio16f"; + dev->board_name="at-mio-16f"; + if(it->options[0]) + dev->iobase=it->options[0]; + else return -EINVAL; + + printk("comedi: atmio16f: 0x%04x\n",dev->iobase); + if(check_region(dev->iobase,ATMIO16F_SIZE)<0){ + comedi_error(dev,"I/O port conflict"); + return -EIO; + } + request_region(dev->iobase,ATMIO16F_SIZE,"atmio16f"); + dev->iosize=ATMIO16F_SIZE; + + dev->n_subdevices=4; + + if((ret=alloc_subdevices(dev))<0) + goto cleanup; + if((ret=alloc_private(dev,sizeof(ni_F_private)))<0) + goto cleanup; + + s=dev->subdevices+0; + /* ai subdevice */ + s->type=COMEDI_SUBD_AI; + s->subdev_flags=SDF_READABLE; + s->n_chan=16; + s->maxdata=0xfff; + s->range_type=RANGE_unknown; + s->trig[0]=atmio16f_ai; + + s++; + /* ao subdevice */ + s->type=COMEDI_SUBD_AO; + s->subdev_flags=SDF_WRITEABLE; + s->n_chan=2; + s->maxdata=0xfff; + s->range_type=RANGE_unknown; + s->trig[0]=atmio16f_ao; + + s++; + /* di subdevice */ + s->type=COMEDI_SUBD_DI; + s->subdev_flags=SDF_READABLE; + s->n_chan=8; + s->maxdata=1; + s->range_type=RANGE_digital; + s->trig[0]=atmio16f_di; + + s++; + /* do subdevice */ + s->type=COMEDI_SUBD_DO; + s->subdev_flags=SDF_WRITEABLE; + s->maxdata=1; + s->n_chan=8; + s->range_type=RANGE_digital; + s->trig[0]=atmio16f_do; + + /* do some init */ + + dev->rem=atmio16f_rem; + + return 1; +cleanup: + atmio16f_rem(dev); + + return ret; +} + + +int atmio16f_rem(comedi_device *dev) +{ + + if(dev->iobase) + release_region(dev->iobase,dev->iosize); + + return 0; +} + diff --git a/comedi/old/atmio-16x.c b/comedi/old/atmio-16x.c new file mode 100644 index 00000000..c8ec0ebd --- /dev/null +++ b/comedi/old/atmio-16x.c @@ -0,0 +1,193 @@ +/* + module/atmio-16x.c + hardware driver for National Instruments AT-MIO-16X + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/* + specifications are found in NI document 320640b.pdf +*/ + +#define Command_Register_1 0x00 /* wo */ +#define Command_Register_2 0x02 /* wo */ +#define Command_Register_3 0x04 /* wo */ +#define Command_Register_4 0x06 /* wo */ +#define Status_Register_1 0x18 /* ro */ +#define Status_Register_2 0x1a /* ro */ + +#define ADC_FIFO_Register 0x00 /* ro */ +#define CONFIGMEM_Register 0x08 /* wo */ + +#define DAC0_Register 0x10 /* wo */ +#define DAC1_Register 0x12 /* wo */ + +#define CONFIGMEMCLR_Register 0x1b /* ro 8 */ +#define CONFIGMEMLD_Register 0x1b /* wo 8 */ +#define DAQ_Clear_Register 0x19 /* ro 8 */ +#define DAQ_Start_Register 0x1d /* ro 8 */ +#define Single_Conversion_Register 0x1d /* wo 8 */ +#define ADC_Calibration_Register 0x1f /* wo 8 */ + +#define TMRREQ_Clear_Register 0x1f /* ro 8 */ +#define DAC_Update_Register 0x18 /* wo */ +#define DAC_Clear_Register 0x1e /* ro 8 */ + +#define DMA_Channel_Clear_Register 0x0b /* ro 8 */ +#define DMATCA_Clear_Register 0x19 /* wo 8 */ +#define DMATCB_Clear_Register 0x09 /* ro 8 */ +#define External_Strobe_Register 0x1e /* wo 8 */ +#define Calibration_DAC_0_Load_Register 0x0a /* wo 8 */ +#define Calibration_DAC_1_Load_Register 0x1a /* wo 8 */ + +#define Am9513A_Data_Register 0x14 /* rw */ +#define Am9513A_Command_Register 0x16 /* wo */ +#define Am9513A_Status_Register 0x16 /* ro */ + +#define Digital_Input_Register 0x1c /* ro */ +#define Digital_Output_Register 0x1c /* wo */ + +#define RTSI_Switch_Shift_Register 0x0c /* wo 8 */ +#define RTSI_Switch_Strobe_Register 0x0e /* wo 8 */ + +#define _B(b) ((struct bitchan){_B_CHAN,(b)}) + +#define _B_CHAN Command_Register_1 +#define EEPROMCS _bit15 +#define SDATA _bit14 +#define SCLK _bit13 +#define SCANDIV _bit12 +#define INTGATE _bit10 +#define RETRIG_DIS _bit9 +#define DAQEN _bit8 +#define SCANEN _bit7 +#define SCN2 _bit6 +#define CNT32 _bit5 +#define RTSITRIG _bit4 +#undef _B_CHAN + +#define _B_CHAN Command_Register_2 +#define A4RCV _bit15 +#define A4DRV _bit14 +#define A2RCV _bit13 +#define A2DRV _bit12 +#define BIPDAC1 _bit11 +#define BIPDAC0 _bit10 +#define EXTREFDAC1 _bit9 +#define EXTREFDAC0 _bit8 +#define EISA_DMA _bit7 +#define DMACHBB _bits543 +#define DMACHAB _bits210 +#undef _B_CHAN + +#define _B_CHAN Command_Register_3 +#define ADCDSP _bit15 +#define DIOPBEN _bit14 +#define DIOPAEN _bit13 +#define DMATCINT _bit12 +#define DACCMPLINT _bit11 +#define DAQCMPLINT _bit10 +#define IO_INT _bit9 +#define DMACHA _bit8 +#define DMACHB _bit7 +#define ARCREQ _bit6 +#define DAC1REQ _bit5 +#define DAC0REQ _bit4 +#define DRVAIS _bit3 +#define INTCHB2 _bit2 +#define INTCHB1 _bit1 +#define INTCHB0 _bit0 +#undef _B_CHAN + +#define _B_CHAN Command_Register_3 +#define CLKMODEB1 _bit15 +#define CLKMODEB0 _bit14 +#define DAC1DSP _bit13 +#define DAC0DSP _bit12 +#define DACMB3 _bit11 +#define DACMB2 _bit10 +#define DACMB1 _bit9 +#define DACMB0 _bit8 +#define DACGATE _bit7 +#define DB_DIS _bit6 +#define CYCLICSTOP _bit5 +#define ADCFIFOREQ _bit4 +#define SRC3SEL _bit3 +#define GATE2SEL _bit2 +#define FIFODAC _bit1 +#define EXTTRIG_DIS _bit0 +#undef _B_CHAN + +#define _B_CHAN Status_Register_1 +#define DAQCOMP _bit15 +#define DAQPROG _bit14 +#define ADCFIFOHF _bit13 +#define ADCFIFOEF _bit12 +#define DMATCA _bit11 +#define DMATCB _bit10 +#define OVERFLOW _bit9 +#define OVERRUN _bit8 +#define TMRREQ _bit7 +#define DACCOMP _bit6 +#define DACFIFOFF _bit5 +#define DACFIFOHF _bit4 +#define DACFIFOEF _bit3 +#define EEPROMDATA _bit2 +#define EEPROMCD _bit1 +#define CFGMEMEF _bit0 +#undef _B_CHAN + +#define _B_CHAN +#define ADC_BUSY _bit0 +#undef _B_CHAN + +#define _B_CHAN CONFIGMEM_Register +#define CHAN_SE _bit15 +#define CHAN_AIS _bit14 +#define CHAN_CAL _bit13 +#define CHAN_BIP _bit12 +#define CHANSEL3 _bit9 +#define CHANSEL2 _bit8 +#define CHANSEL1 _bit7 +#define CHANSEL0 _bit6 +#define CH_GAIN2 _bit5 +#define CH_GAIN1 _bit4 +#define CH_GAIN0 _bit3 +#define CHAN_LAST _bit2 +#define CHAN_GHOST _bit1 +#define CHAN_DSP _bit0 +#endif _B_CHAN + + + + + + + + + + +int irqbits[]={ + -1, -1, -1, 0, 1, 2, -1, 3, + -1, -1, 4, 5, 6, -1, -1, 7 +} + + + + diff --git a/comedi/old/rti860.c b/comedi/old/rti860.c new file mode 100644 index 00000000..0ab08d56 --- /dev/null +++ b/comedi/old/rti860.c @@ -0,0 +1,1264 @@ +/***************************************************************** + Linux device driver for RTI860 hardware + + Copyright (C) 1995 Ralf Haller + (C) 1995, 1996 Harald Kirsch (kir@iitb.fhg.de) + + Fraunhofer Institut für + Informations- und Datenverarbeitung (IITB) + Fraunhoferstr. 1 + 76131 Karlsruhe + +*****************************************************************/ +/* + * values for ioctl + */ +#define RTI860_SET_PAR 1 +#define RTI860_GET_PAR 2 +#define RTI860_START 3 +#define RTI860_RESET 4 + +/* + * flags + */ +#define RTI860_F_PRESENT (1<<0) +#define RTI860_F_OPEN (1<<1) +#define RTI860_F_BUSY (1<<2) +#define RTI860_F_IRQ (1<<3) +#define RTI860_F_RESTART (1<<4) + +/* The following flag(s) can be changed by the user */ +#define RTI860_F_USER (RTI860_F_RESTART) + +typedef enum { + Channel16 =-16, + Channel15 =-15, + Channel14 =-14, + Channel13 =-13, + Channel12 =-12, + Channel11 =-11, + Channel10 =-10, + Channel9 =-9, + Channel8 =-8, + Channel7 =-7, + Channel6 =-6, + Channel5 =-5, + Channel4 =-4, + Channel3 =-3, + Channel2 =-2, + Channel1 =-1, + Channel1_4 = 0, + Channel5_8 = 1, + Channel9_12 = 2, + Channel13_16 = 3, + Channel1_16 = 4, + Channel1_8 = 5, + Channel9_16 = 6 +} Channel; + +typedef enum { + Free = 0, + BereichPlus = 1, + BereichMinus = 2, + PegelPlus = 3, + PegelMinus = 4, + TTLTrigger = 5 +} Trigger; + +/* + * parameter struct used for ioctl + */ +struct rti860_t +{ + unsigned int flags; /* flags */ + unsigned int base; /* base address */ + short channel; /* channels to scan, see enum above */ + unsigned short trigger; /* trigger mode, see enum above */ + signed char level; /* trigger level, a value between -128 + and 127 */ + int tics; /* >0: use the 5MHz internal clock with + ticks as the divider. + <0: use the external clock with ticks + as the divider. + In both cases the sampling frequency + is 1.0/(ticks*base_frequency) */ + int irq; /* interrupt channel, 0 if none */ + unsigned timeout; /* timeout in seconds */ + unsigned sleep_time; /* sleep time in polling mode in jiffies + */ +}; + +typedef struct rti860_t Rti860; + +#define F2TICS(f) (5e6/(f)) +#define TICS2F(tics) (5e6/(tics)) +/***************************************************************** + Linux device driver for RTI860 hardware + + Copyright (C) 1995 Ralf Haller + (C) 1995, 1996 Harald Kirsch (kir@iitb.fhg.de) + + Fraunhofer Institut für + Informations- und Datenverarbeitung (IITB) + Fraunhoferstr. 1 + 76131 Karlsruhe + +*****************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 +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 + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PARPORT_SIZE 3 + +#define PARPORT_A 0 +#define PARPORT_B 1 +#define PARPORT_C 2 + +static int parport_attach(comedi_device *dev,comedi_devconfig *it); +static int parport_detach(comedi_device *dev); +comedi_driver driver_parport={ + driver_name: "parport", + attach: parport_attach, + detach: parport_detach, +}; + + +static int parport_dio_a(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + outb(it->data[0],dev->iobase+PARPORT_A); + + return 1; +} + +static int parport_dio_b(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + it->data[0]=(inb(dev->iobase+PARPORT_B)>>3); + + return 1; +} + +static int parport_dio_c(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + outb(it->data[0],dev->iobase+PARPORT_C); + + return 1; +} + + +static int parport_attach(comedi_device *dev,comedi_devconfig *it) +{ + int ret; + comedi_subdevice *s; + + dev->iobase=it->options[0]; + printk("comedi%d: parport: 0x%04x ",dev->minor,dev->iobase); + if(check_region(dev->iobase,PARPORT_SIZE)<0){ + printk("I/O port conflict\n"); + return -EIO; + } + request_region(dev->iobase,PARPORT_SIZE,"parport (comedi)"); + dev->iosize=PARPORT_SIZE; + dev->irq=0; + dev->board_name="parport"; + + dev->n_subdevices=3; + if((ret=alloc_subdevices(dev))<0) + return ret; + + s=dev->subdevices+0; + s->type=COMEDI_SUBD_DO; + s->subdev_flags=SDF_WRITEABLE; + s->n_chan=8; + s->maxdata=1; + s->range_type=RANGE_digital; + s->trig[0]=parport_dio_a; + + s=dev->subdevices+1; + s->type=COMEDI_SUBD_DI; + s->subdev_flags=SDF_READABLE; + s->n_chan=4; + s->maxdata=1; + s->range_type=RANGE_digital; + s->trig[0]=parport_dio_b; + + s=dev->subdevices+2; + s->type=COMEDI_SUBD_DO; + s->subdev_flags=SDF_WRITEABLE; + s->n_chan=4; + s->maxdata=1; + s->range_type=RANGE_digital; + s->trig[0]=parport_dio_c; + + printk("\n"); + return 1; +} + + +static int parport_detach(comedi_device *dev) +{ + printk("comedi%d: parport: remove\n",dev->minor); + + release_region(dev->iobase,dev->iosize); + + return 0; +} + diff --git a/comedi/proc.c b/comedi/proc.c new file mode 100644 index 00000000..55944964 --- /dev/null +++ b/comedi/proc.c @@ -0,0 +1,113 @@ +/* + module/proc.c + /proc interface for comedi + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + 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 +#include +#include + + +#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;idriver_name, + comedi_devices[i].board_name, + comedi_devices[i].n_subdevices + ); + } + } + if(!devices_q){ + l+=sprintf(buf+l,"no devices\n"); + } + + return l; +} + + +void comedi_proc_init(void) +{ +#ifdef LINUX_V20 + proc_register_dynamic(&proc_root,&comedi_proc_entry); +#else + struct proc_dir_entry *comedi_proc; + + comedi_proc = create_proc_entry("comedi",S_IFREG | S_IRUGO,0); + if(comedi_proc) + comedi_proc->read_proc = comedi_read_procmem; +#endif +} + +void comedi_proc_cleanup(void) +{ +#ifdef LINUX_V20 + proc_unregister(&proc_root,comedi_proc_entry.low_ino); +#else + remove_proc_entry("comedi",0); +#endif +} + + diff --git a/comedi/range.c b/comedi/range.c new file mode 100644 index 00000000..e2944097 --- /dev/null +++ b/comedi/range.c @@ -0,0 +1,118 @@ +/* + module/range.c + comedi routines for voltage ranges + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1997-8 David A. Schleef + + 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 +#ifdef LINUX_V22 +#include +#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=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;irange_type_list){ + for(i=0;i=s->n_chan || + CR_RANGE(chanlist[i])>=RANGE_LENGTH(s->range_type_list[chan])){ + rt_printk("bad chanlist[%d]=0x%08x\n",i,chanlist[i]); + return -EINVAL; + } + } + }else{ + rt_printk("comedi: (bug) no range type list!\n"); + return -EINVAL; + } + return 0; +} + + diff --git a/comedi/realtime/vd_dds.c b/comedi/realtime/vd_dds.c new file mode 100644 index 00000000..17fc8963 --- /dev/null +++ b/comedi/realtime/vd_dds.c @@ -0,0 +1,244 @@ +/* + module/vd_dds.c + virtual driver for direct digital signal synthesis + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1999 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_COMEDI_RTL_V1 +#include +#endif +#ifdef CONFIG_COMEDI_RTL +#include +#include +#include +#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;in_chan;i++){ + chan=CR_CHAN(it->chanlist[i]); + + devpriv->args[chan]=it->data[i]; + } + return i; +} + + +static int dds_ao_mode2(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + int ret; + RTIME now,period; + struct timespec ts; + + //if(it->trigvar1!=0)return -EINVAL; + if(it->trigvar<100000)return -EINVAL; /* 10 khz */ + + ret=comedi_lock_ioctl(devpriv->device,devpriv->subd); + if(ret<0)goto out; + +#if 0 + struct timespec ts; + + ts.tv_sec=0; + ts.tv_nsec=it->trigvar; +#endif + + devpriv->trig.subdev=devpriv->subd; + devpriv->trig.mode=TRIG_WRITE; + devpriv->trig.flags=0; + devpriv->trig.n=1; + + ts.tv_sec=0; + ts.tv_nsec=it->trigvar; + period=timespec_to_RTIME(ts); + + rt_task_init(&devpriv->rt_task,dds_ai_task_func,(int)dev,3000,4); + + now=rt_get_time(); + rt_task_make_periodic(&devpriv->rt_task,now+period,period); + + return 0; + +unlock: + comedi_unlock_ioctl(devpriv->device,devpriv->subd); +out: + return ret; +} + +int dds_ao_cancel(comedi_device *dev,comedi_subdevice *s) +{ + rt_task_delete(&devpriv->rt_task); + + comedi_unlock_ioctl(devpriv->device,devpriv->subd); + + return 0; +} + +int dds_attach(comedi_device *dev,comedi_devconfig *it) +{ + int ret; + comedi_subdevice *s; + + if(strcmp("dds",it->board_name)) + return 0; + + printk("comedi%d: dds: ",dev->minor); + dev->board_name="dds"; + dev->driver_name="dds"; + + dev->n_subdevices=1; + if((ret=alloc_subdevices(dev))<0) + return ret; + if((ret=alloc_private(dev,sizeof(dds_private)))<0) + return ret; + + devpriv->device=it->options[0]; + devpriv->subd=it->options[1]; + + devpriv->dev=comedi_devices+devpriv->device; + devpriv->s=devpriv->dev->subdevices+devpriv->subd; + + s=dev->subdevices+0; + s->type=COMEDI_SUBD_AO; + s->subdev_flags=SDF_READABLE; + s->n_chan=devpriv->s->n_chan; + s->len_chanlist=1024; + s->trig[2]=dds_ao_mode2; + s->cancel=dds_ao_cancel; + s->maxdata=devpriv->s->maxdata; + s->range_type=devpriv->s->range_type; + s->timer_type=TIMER_nanosec; + + s=dev->subdevices+0; + s->type=COMEDI_SUBD_AO; + s->subdev_flags=SDF_READABLE; + s->n_chan=devpriv->s->n_chan; + s->len_chanlist=1024; + s->trig[2]=dds_ao_mode2; + s->cancel=dds_ao_cancel; + s->maxdata=devpriv->s->maxdata; + s->range_type=devpriv->s->range_type; + + devpriv->soft_irq=rtl_get_soft_irq(dds_interrupt,"dds"); + broken_rtl_dev=dev; + + printk("\n"); + + return 1; +} + + +static int dds_detach(comedi_device *dev) +{ + printk("comedi%d: dds: remove\n",dev->minor); + + free_irq(devpriv->soft_irq,NULL); + + return 0; +} + + diff --git a/comedi/realtime/vd_timer.c b/comedi/realtime/vd_timer.c new file mode 100644 index 00000000..517d2d58 --- /dev/null +++ b/comedi/realtime/vd_timer.c @@ -0,0 +1,250 @@ +/* + module/vd_timer.c + virtual driver for using RTL timing sources + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1999 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_COMEDI_RTL_V1 +#include +#include +#endif + +#ifdef CONFIG_COMEDI_RTL +#include +#include +#include +#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;nn;n++){ + for(i=0;in_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;idata[i]); + } + rt_task_wait(); + } + rtl_global_pend_irq(devpriv->soft_irq); + + rt_task_delete(&devpriv->rt_task); + + /* eek! */ +} + +static int timer_ai_mode0(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + return comedi_trig_ioctl(devpriv->device,devpriv->subd,it); +} + +static int timer_ai_mode1(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + return -EINVAL; +} + + +static int timer_ai_mode2(comedi_device *dev,comedi_subdevice *s,comedi_trig *it) +{ + int ret; + RTIME now,period; + struct timespec ts; + + //if(it->trigvar1!=0)return -EINVAL; + if(it->trigvar<100000)return -EINVAL; /* 10 khz */ + + ret=comedi_lock_ioctl(devpriv->device,devpriv->subd); + if(ret<0)goto out; + +#if 0 + struct timespec ts; + + ts.tv_sec=0; + ts.tv_nsec=it->trigvar; +#endif + + /* XXX this does not get freed */ + devpriv->data=kmalloc(sizeof(sampl_t)*it->n_chan,GFP_KERNEL); + if(!devpriv->data){ + ret=-ENOMEM; + goto unlock; + } + + devpriv->trig.subdev=devpriv->subd; + devpriv->trig.mode=0; + devpriv->trig.flags=0; + devpriv->trig.n=1; + + ts.tv_sec=0; + ts.tv_nsec=it->trigvar; + period=timespec_to_RTIME(ts); + + rt_task_init(&devpriv->rt_task,timer_ai_task_func,(int)dev,3000,4); + + now=rt_get_time(); + rt_task_make_periodic(&devpriv->rt_task,now+period,period); + + return 0; + +unlock: + comedi_unlock_ioctl(devpriv->device,devpriv->subd); +out: + return ret; +} + +int timer_cancel(comedi_device *dev,comedi_subdevice *s) +{ + rt_task_delete(&devpriv->rt_task); + + comedi_unlock_ioctl(devpriv->device,devpriv->subd); + + return 0; +} + +static int timer_attach(comedi_device *dev,comedi_devconfig *it) +{ + int ret; + comedi_subdevice *s; + + printk("comedi%d: timer: ",dev->minor); + dev->board_name="timer"; + + dev->n_subdevices=1; + if((ret=alloc_subdevices(dev))<0) + return ret; + if((ret=alloc_private(dev,sizeof(timer_private)))<0) + return ret; + + devpriv->device=it->options[0]; + devpriv->subd=it->options[1]; + + devpriv->dev=comedi_devices+devpriv->device; + devpriv->s=devpriv->dev->subdevices+devpriv->subd; + + s=dev->subdevices+0; + s->type=COMEDI_SUBD_AI; + s->subdev_flags=SDF_READABLE; + s->n_chan=devpriv->s->n_chan; + s->len_chanlist=1024; + s->trig[0]=timer_ai_mode0; + s->trig[1]=timer_ai_mode1; + s->trig[2]=timer_ai_mode2; + s->cancel=timer_cancel; + s->maxdata=devpriv->s->maxdata; + s->range_type=devpriv->s->range_type; + s->timer_type=TIMER_nanosec; + + devpriv->soft_irq=rtl_get_soft_irq(timer_interrupt,"timer"); + broken_rtl_dev=dev; + + printk("\n"); + + return 1; +} + + +static int timer_detach(comedi_device *dev) +{ + printk("comedi%d: timer: remove\n",dev->minor); + + free_irq(devpriv->soft_irq,NULL); + + return 0; +} + + diff --git a/comedi/rtai.c b/comedi/rtai.c new file mode 100644 index 00000000..3fcbe4d9 --- /dev/null +++ b/comedi/rtai.c @@ -0,0 +1,61 @@ +/* + * RTAI support rooutines + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +extern void rt_unmask_irq(unsigned int irq); + +static struct comedi_irq_struct *rtai_irq; + +static void handle_rtai_irq(void) +{ + struct comedi_irq_struct *it=rtai_irq; + + if(it) + it->handler(it->irq,it->dev_id,NULL); + + rt_unmask_irq(it->irq); +} + +int get_priority_irq(struct comedi_irq_struct *it) +{ + rtai_irq=it; + + //free_irq(it->irq,it->dev_id); + rt_request_global_irq(it->irq,handle_rtai_irq); + rt_enable_irq(it->irq); + + return 0; +} + +int free_priority_irq(struct comedi_irq_struct *it) +{ + rt_free_global_irq(it->irq); + //request_irq(it->irq,it->handler,it->flags&~SA_PRIORITY,"fixme",it->dev_id); + + return 0; +} + + +void comedi_rtai_init(void) +{ + +} + +void comedi_rtai_cleanup(void) +{ + +} + diff --git a/comedi/rtai.h b/comedi/rtai.h new file mode 100644 index 00000000..144c1dff --- /dev/null +++ b/comedi/rtai.h @@ -0,0 +1,23 @@ +/* + * RTL compatibility,, version 1 + * + */ + +#ifndef __COMEDI_RTAI_H +#define __COMEDI_RTAI_H + +#include + +#if 0 +int rt_printk(const char *fmt, ...); + +void rt_printk_cleanup(void); +int rt_printk_init(void); +#endif + +void comedi_rtai_init(void); +void comedi_rtai_cleanup(void); + + +#endif + diff --git a/comedi/rtl.c b/comedi/rtl.c new file mode 100644 index 00000000..da8c222f --- /dev/null +++ b/comedi/rtl.c @@ -0,0 +1,225 @@ +/* + * RTLinux support rooutines + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if 0 +#include +#endif + + + +/* rt_printk() section */ + +#define BUF_LEN (16384) + +static char rt_printk_buf[BUF_LEN]; +static char buf[1024]; + +static int buf_front,buf_back; + +static int rt_printk_irq; + +/* this is rather bogus, but it will catch most errors */ +static volatile int rt_printk_lock; + +static void rt_printk_interrupt(int irq,void *junk,struct pt_regs *regs); + +int rt_printk(const char *fmt, ...) +{ + va_list args; + int len,i; + + if(rtl_rt_system_is_idle()){ + va_start(args, fmt); + len=printk(fmt,args); + va_end(args); + return len; + } + + if(rt_printk_lock){ + /* force a panic */ + *(int *)0=0; + } + rt_printk_lock=1; + + va_start(args, fmt); + len = vsprintf(buf, fmt, args); /* hopefully i < sizeof(buf)-1 */ + va_end(args); + + if(buf_front+len>=BUF_LEN){ + i=BUF_LEN-buf_front; + memcpy(rt_printk_buf+buf_front,buf,i); + memcpy(rt_printk_buf,buf+i,len-i); + buf_front=len-i; + }else{ + memcpy(rt_printk_buf+buf_front,buf,len); + buf_front+=len; + } + + rt_printk_lock=0; + + rtl_global_pend_irq(rt_printk_irq); + + return len; +} + + +void rt_printk_interrupt(int irq,void *junk,struct pt_regs *regs) +{ + int tmp; + + for(;;){ + tmp=buf_front; + if(buf_back>tmp){ + printk("%.*s",BUF_LEN-buf_back,rt_printk_buf+buf_back); + buf_back=0; + } + if(buf_back==tmp)break; + printk("%.*s",tmp-buf_back,rt_printk_buf+buf_back); + buf_back=tmp; + } +} + +void rt_printk_cleanup(void) +{ + free_irq(rt_printk_irq,NULL); + rt_printk_irq=0; +} + +int rt_printk_init(void) +{ + rt_printk_irq=rtl_get_soft_irq(rt_printk_interrupt,"rt_printk"); + if(rt_printk_irq<0){ + printk("rt_printk: can't allocate soft irq\n"); + return -EIO; + } + + return 0; +} + + +#if 0 + +static ssize_t comedi_rtl_read(struct rtl_file *file,char *buf, + size_t len,loff_t *p) +{ + return -EIO; +} + +static ssize_t comedi_rtl_write(struct rtl_file *file,const char *buf, + size_t len,loff_t *p) +{ + return -EIO; +} + +static int comedi_rtl_ioctl(struct rtl_file *file,unsigned int cmd, + unsigned long arg) +{ + switch(cmd){ + case COMEDI_TRIG: + { + comedi_trig *it=(comedi_trig *)arg; + int minor=file->f_minor; + int subdev=it->subdev; + + return comedi_trig_ioctl(minor,subdev,it); + } + case COMEDI_LOCK: + { + int minor=file->f_minor; + int subdev=(int)arg; + + return comedi_lock_ioctl(minor,subdev); + } + case COMEDI_UNLOCK: + { + int minor=file->f_minor; + int subdev=(int)arg; + + return comedi_lock_ioctl(minor,subdev); + } + case COMEDI_CANCEL: + { + int minor=file->f_minor; + int subdev=(int)arg; + + return comedi_cancel_ioctl(minor,subdev); + } + } + + return -EIO; +} + +static int comedi_rtl_open(struct rtl_file *file) +{ + return 0; +} + +static int comedi_rtl_release(struct rtl_file *file) +{ + return 0; +} + + +struct rtl_file_operations comedi_rtl_fops={ + read: comedi_rtl_read, + write: comedi_rtl_write, + ioctl: comedi_rtl_ioctl, + open: comedi_rtl_open, + release: comedi_rtl_release, +}; + +#endif + + +static unsigned int handle_rtl_irq(unsigned int irq,struct pt_regs *regs) +{ + struct comedi_irq_struct *it; + + it=get_irq_struct(irq); + if(it) + it->handler(irq,it->dev_id,regs); + rtl_hard_enable_irq(irq); + return 0; +} + +int get_priority_irq(struct comedi_irq_struct *it) +{ + rtl_request_global_irq(it->irq,handle_rtl_irq); + + return 0; +} + +int free_priority_irq(struct comedi_irq_struct *it) +{ + rtl_free_irq(it->irq); + + return 0; +} + + +void comedi_rtl_init(void) +{ + rt_printk_init(); + //rtl_register_chardev(COMEDI_MAJOR,"comedi",&comedi_rtl_fops); +} + +void comedi_rtl_cleanup(void) +{ + rt_printk_cleanup(); + //rtl_unregister_chardev(COMEDI_MAJOR); +} + diff --git a/comedi/rtl.h b/comedi/rtl.h new file mode 100644 index 00000000..30b2c82e --- /dev/null +++ b/comedi/rtl.h @@ -0,0 +1,20 @@ +/* + * rt_printk.c, hacked from linux/kernel/printk.c + * + * Modified for RT support, David Schleef + */ + +#ifndef __COMEDI_RTL_H +#define __COMEDI_RTL_H + +int rt_printk(const char *fmt, ...); + +void rt_printk_cleanup(void); +int rt_printk_init(void); + +void comedi_rtl_init(void); +void comedi_rtl_cleanup(void); + + +#endif + diff --git a/comedi/rtl_v1.c b/comedi/rtl_v1.c new file mode 100644 index 00000000..b0a37533 --- /dev/null +++ b/comedi/rtl_v1.c @@ -0,0 +1,57 @@ +/* + * RTLinux v1 support rooutines + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +static struct comedi_irq_struct *rtlv1_irq; + +static void handle_rtlv1_irq(void) +{ + struct comedi_irq_struct *it=rtlv1_irq; + + if(it) + it->handler(it->irq,it->dev_id,NULL); +} + +int get_priority_irq(struct comedi_irq_struct *it) +{ + rtlv1_irq=it; + request_RTirq(it->irq,handle_rtlv1_irq); + + return 0; +} + +int free_priority_irq(struct comedi_irq_struct *it) +{ + free_RTirq(it->irq); + rtlv1_irq=NULL; + + return 0; +} + + + +void comedi_rtl_v1_init(void) +{ + +} + +void comedi_rtl_v1_cleanup(void) +{ + +} + + + diff --git a/comedi/rtl_v1.h b/comedi/rtl_v1.h new file mode 100644 index 00000000..97cceef2 --- /dev/null +++ b/comedi/rtl_v1.h @@ -0,0 +1,23 @@ +/* + * RTL compatibility,, version 1 + * + */ + +#ifndef __COMEDI_RTL_V1_H +#define __COMEDI_RTL_V1_H + +#include + +#if 0 +int rt_printk(const char *fmt, ...); + +void rt_printk_cleanup(void); +int rt_printk_init(void); +#endif + +void comedi_rtl_v1_init(void); +void comedi_rtl_v1_cleanup(void); + + +#endif + diff --git a/comedi_config/Makefile b/comedi_config/Makefile new file mode 100644 index 00000000..1fe9eee7 --- /dev/null +++ b/comedi_config/Makefile @@ -0,0 +1,12 @@ + +CFLAGS=-Wall -O2 -I../include +#LDFLAGS=-L../lib/ -lcomedi +LDFLAGS= + +OBJS=comedi_config.o + +comedi_config: $(OBJS) + $(CC) -o comedi_config $(OBJS) $(LDFLAGS) + +clean: + rm -f comedi_config *.o diff --git a/comedi_config/comedi_config.c b/comedi_config/comedi_config.c new file mode 100644 index 00000000..da4e90b8 --- /dev/null +++ b/comedi_config/comedi_config.c @@ -0,0 +1,169 @@ +/* + comedi_config/comedi_config.c + configuration program for comedi kernel module + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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]] ,,...\n" +"where 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) + 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;iAT-MIO-16E-2, National Instruments, High Speed 12-bit MIO DAQ Board<-- +# +# Logical device id NIC0000 +# Device support I/O range check register +# +# Edit the entries below to uncomment out the configuration required. +# Note that only the first value of any range is given, this may be changed if required +# Don't forget to uncomment the activate (ACT Y) when happy + +(CONFIGURE NIC1900/826 (LD 0 + (IO 0 (BASE 0x0260)) + (ACT Y) +)) + +(WAITFORKEY) + diff --git a/include/comedi.h b/include/comedi.h new file mode 100644 index 00000000..06b3a32e --- /dev/null +++ b/include/comedi.h @@ -0,0 +1,295 @@ +/* + include/comedi.h (installed as /usr/include/comedi.h) + header file for comedi + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1998 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef _COMEDI_H +#define _COMEDI_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* comedi's major device number */ +#define COMEDI_MAJOR 98 + +/* + maximum number of minor devices. This can be increased, although + kernel structures are currently statically allocated, thus you + don't want this to be much more than you actually use. + */ +#define COMEDI_NDEVICES 4 + +/* number of config options in the config structure */ +#define COMEDI_NDEVCONFOPTS 32 + +/* max length of device and driver names */ +#define COMEDI_NAMELEN 20 + + +typedef unsigned int lsampl_t; +typedef unsigned short sampl_t; + +/* packs and unpacks a channel/range number */ + +#define CR_PACK(chan,rng,aref) ( (((aref)&0x3)<<24) | (((rng)&0xff)<<16) | ((chan)&0xffff) ) + +#define CR_CHAN(a) ((a)&0xffff) +#define CR_RANGE(a) (((a)>>16)&0xff) +#define CR_AREF(a) (((a)>>24)&0x03) + +#define AREF_GROUND 0x00 /* analog ref = analog ground */ +#define AREF_COMMON 0x01 /* analog ref = analog common */ +#define AREF_DIFF 0x02 /* analog ref = differential */ +#define AREF_OTHER 0x03 /* analog ref = other (undefined) */ + +/* trigger flags */ + +#define TRIG_BOGUS 0x0001 /* do the motions */ +#define TRIG_DITHER 0x0002 /* enable dithering */ +#define TRIG_DEGLITCH 0x0004 /* enable deglitching */ +#define TRIG_RT 0x0008 /* perform op in real time */ +#define TRIG_CONFIG 0x0010 /* perform configuration, not triggering */ +#define TRIG_WAKE_EOS 0x0020 /* wake up on end-of-scan events */ +#define TRIG_WRITE 0x0040 /* write to bidirectional devices */ + +/* trigger sources */ + +#define TRIG_ANY 0 +#define TRIG_NONE 0 /* never trigger */ +#define TRIG_NOW 1 /* trigger now + N ns */ +#define TRIG_FOLLOW 2 /* trigger on next lower level trig */ +#define TRIG_TIME 3 /* trigger at time N ns */ +#define TRIG_TIMER 4 /* trigger at rate N ns */ +#define TRIG_COUNT 5 /* trigger when count reaches N */ +#define TRIG_EXT 6 /* trigger on external signal N */ +#define TRIG_INT 7 /* trigger on comedi-internal signal N */ + +#define TRIG_INVAL 8 /* choice was invalid */ + +/* subdevice flags */ + +#define SDF_BUSY 0x0001 /* device is busy */ +#define SDF_BUSY_OWNER 0x0002 /* device is busy with your job */ +#define SDF_LOCKED 0x0004 /* subdevice is locked */ +#define SDF_LOCK_OWNER 0x0008 /* you own lock */ +#define SDF_MAXDATA 0x0010 /* maxdata depends on channel */ +#define SDF_FLAGS 0x0020 /* flags depend on channel */ +#define SDF_RANGETYPE 0x0040 /* range type depends on channel */ +#define SDF_MODE0 0x0080 /* can do mode 0 */ +#define SDF_MODE1 0x0100 /* can do mode 1 */ +#define SDF_MODE2 0x0200 /* can do mode 2 */ +#define SDF_MODE3 0x0400 /* can do mode 3 */ +#define SDF_MODE4 0x0800 /* can do mode 4 */ + +#define SDF_READABLE 0x00010000 /* subdevice can be read (e.g. analog input) */ +#define SDF_WRITEABLE 0x00020000 /* subdevice can be written (e.g. analog output) */ +#define SDF_INTERNAL 0x00040000 /* subdevice does not have externally visible lines */ +#define SDF_RT 0x00080000 /* subdevice is RT capable */ +#define SDF_GROUND 0x00100000 /* can do aref=ground */ +#define SDF_COMMON 0x00200000 /* can do aref=common */ +#define SDF_DIFF 0x00400000 /* can do aref=diff */ +#define SDF_OTHER 0x00800000 /* can do aref=other */ +#define SDF_DITHER 0x01000000 /* can do dithering */ +#define SDF_DEGLITCH 0x02000000 /* can do deglitching */ +#define SDF_MMAP 0x04000000 /* can do mmap() */ +#define SDF_RUNNING 0x08000000 /* subdevice is acquiring data */ +#define SDF_LSAMPL 0x10000000 /* subdevice uses 32-bit samples */ + + +/* subdevice types */ + +#define COMEDI_SUBD_UNUSED 0 /* unused */ +#define COMEDI_SUBD_AI 1 /* analog input */ +#define COMEDI_SUBD_AO 2 /* analog output */ +#define COMEDI_SUBD_DI 3 /* digital input */ +#define COMEDI_SUBD_DO 4 /* digital output */ +#define COMEDI_SUBD_DIO 5 /* digital input/output */ +#define COMEDI_SUBD_COUNTER 6 /* counter */ +#define COMEDI_SUBD_TIMER 7 /* timer */ +#define COMEDI_SUBD_MEMORY 8 /* memory, EEPROM, DPRAM */ +#define COMEDI_SUBD_CALIB 9 /* calibration DACs */ +#define COMEDI_SUBD_PROC 10 /* processor, DSP */ + + +#define COMEDI_INPUT 0 +#define COMEDI_OUTPUT 1 + +/* ioctls */ + +#define CIO 'd' +#define COMEDI_DEVCONFIG _IOW(CIO,0,comedi_devconfig) +#define COMEDI_DEVINFO _IOR(CIO,1,comedi_devinfo) +#define COMEDI_SUBDINFO _IOR(CIO,2,comedi_subdinfo) +#define COMEDI_CHANINFO _IOR(CIO,3,comedi_chaninfo) +#define COMEDI_TRIG _IOWR(CIO,4,comedi_trig) +#define COMEDI_LOCK _IO(CIO,5) +#define COMEDI_UNLOCK _IO(CIO,6) +#define COMEDI_CANCEL _IO(CIO,7) +#define COMEDI_RANGEINFO _IOR(CIO,8,comedi_rangeinfo) +#define COMEDI_CMD _IOR(CIO,9,comedi_cmd) + + + +/* structures */ + +typedef struct comedi_trig_struct comedi_trig; +typedef struct comedi_cmd_struct comedi_cmd; +typedef struct comedi_chaninfo_struct comedi_chaninfo; +typedef struct comedi_subdinfo_struct comedi_subdinfo; +typedef struct comedi_devinfo_struct comedi_devinfo; +typedef struct comedi_devconfig_struct comedi_devconfig; +typedef struct comedi_rangeinfo_struct comedi_rangeinfo; +typedef struct comedi_krange_struct comedi_krange; + +struct comedi_trig_struct{ + unsigned int subdev; /* subdevice */ + unsigned int mode; /* mode */ + unsigned int flags; + unsigned int n_chan; /* number of channels */ + unsigned int *chanlist; /* channel/range list */ + sampl_t *data; /* data list, size depends on subd flags */ + unsigned int n; /* number of scans */ + unsigned int trigsrc; + unsigned int trigvar; + unsigned int trigvar1; + unsigned int data_len; + unsigned int unused[3]; +}; + +struct comedi_cmd_struct{ + unsigned int subdev; + unsigned int flags; + + unsigned int start_src; + unsigned int start_arg; + + unsigned int scan_begin_src; + unsigned int scan_begin_arg; + + unsigned int convert_src; + unsigned int convert_arg; + + unsigned int scan_end_src; + unsigned int scan_end_arg; + + unsigned int stop_src; + unsigned int stop_arg; + + unsigned int *chanlist; /* channel/range list */ + unsigned int chanlist_len; + + sampl_t *data; /* data list, size depends on subd flags */ + unsigned int data_len; +}; + +struct comedi_chaninfo_struct{ + unsigned int subdev; + lsampl_t *maxdata_list; + unsigned int *flaglist; + unsigned int *rangelist; + unsigned int unused[4]; +}; + +struct comedi_rangeinfo_struct{ + unsigned int range_type; + void *range_ptr; +}; + +struct comedi_krange_struct{ + int min; /* fixed point, multiply by 1e-6 */ + int max; /* fixed point, multiply by 1e-6 */ + unsigned int flags; +}; + +struct comedi_subdinfo_struct{ + unsigned int type; + unsigned int n_chan; + unsigned int subd_flags; + unsigned int timer_type; + unsigned int len_chanlist; + lsampl_t maxdata; + unsigned int flags; /* channel flags */ + unsigned int range_type; /* lookup in kernel */ + unsigned int unused[10]; +}; + +struct comedi_devinfo_struct{ + unsigned int version_code; + unsigned int n_subdevs; + char driver_name[COMEDI_NAMELEN]; + char board_name[COMEDI_NAMELEN]; + int options[COMEDI_NDEVCONFOPTS]; +}; + +struct comedi_devconfig_struct{ + char board_name[COMEDI_NAMELEN]; + int options[COMEDI_NDEVCONFOPTS]; +}; + + +/* range stuff */ + +#define __RANGE(a,b) ((((a)&0xffff)<<16)|((b)&0xffff)) + +#define RANGE_OFFSET(a) (((a)>>16)&0xffff) +#define RANGE_LENGTH(b) ((b)&0xffff) + +#define RF_UNIT(flags) ((flags)&0xff) +#define RF_EXTERNAL (1<<8) + +#define UNIT_volt 0 +#define UNIT_mA 1 +#define UNIT_none 2 + + +/* Kernel internal stuff. Needed by RTLinux modules and such. */ + +#ifdef __KERNEL__ + +/* callback stuff */ + +#define COMEDI_CB_EOS 1 /* end of scan */ +#define COMEDI_CB_EOA 2 /* end of acquisition */ +#define COMEDI_CB_BLOCK 4 /* convenient block size */ +#define COMEDI_CB_EOBUF 8 /* end of buffer */ + +/* exported functions */ + +int comedi_trig_ioctl(unsigned int minor,unsigned int subdev,comedi_trig *it); +int __comedi_trig_ioctl(unsigned int minor,unsigned int subdev,comedi_trig *it); +int comedi_lock_ioctl(unsigned int minor,unsigned int subdev); +int comedi_unlock_ioctl(unsigned int minor,unsigned int subdev); +int comedi_cancel_ioctl(unsigned int minor,unsigned int subdev); +int comedi_register_callback(unsigned int minor,unsigned int subdev, + unsigned int mask,int (*cb)(unsigned int,void *),void *arg); + +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif /* _COMEDI_H */ + diff --git a/man/comedi.7 b/man/comedi.7 new file mode 100644 index 00000000..9be638b7 --- /dev/null +++ b/man/comedi.7 @@ -0,0 +1,59 @@ +.TH comedi 7 "" +.SH NAME +\fBcomedi\fR - the Linux Control and Measurement Device Interface +.SH SYNOPSIS +\fB#include \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, + diff --git a/man/comedi_config.8 b/man/comedi_config.8 new file mode 100644 index 00000000..9296475c --- /dev/null +++ b/man/comedi_config.8 @@ -0,0 +1,177 @@ +.TH comedi_config 8 "" +.SH NAME +\fBcomedi_config\fR - COMEDI configuration utility +.SH SYNOPSIS +\fB/usr/sbin/comedi_config\fR [-vVq] /dev/comediN +[,...] +.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 + + +\fBdas08\fR - Keithley DAS08 and compatibles +.br + + +\fBdt2811\fR - Data Translation DT2811 +.br + +.br + Interrupts are not used with this board. + +\fBdt2814\fR - Data Translation DT2814 +.br + , +.br + Set IRQ to -1 to probe for IRQ. + +\fBdt2817\fR - Data Translation DT2817 +.br + + +\fBdt282x\fR - Data Translation DT2821 series +.br + ,,,, +, +, +, +, +, +, +. +.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 + , +.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 + +.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 + +.br + Use the driver name \fBpcl711b\fR to configure for a 711B board. + +\fBpcl725\fR - PC-LabCard PCL-725 +.br + +.br + +\fBpcl726\fR - PC-LabCard PCL-726 +.br + +.br + +\fBrti800\fR - Analog Devices RTI-800/815 +.br + + +.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 + + ,,... + +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, + diff --git a/scripts/Configure b/scripts/Configure new file mode 100755 index 00000000..c1dae68c --- /dev/null +++ b/scripts/Configure @@ -0,0 +1,478 @@ +#!/bin/sh +# +# This script was hacked from the one used to configure the linux kernel. +# + +# +# check running kernel vs. /usr/src/linux and warn if necessary +# +read dummy dummy dummy2 <$LINUXDIR/include/linux/version.h +UTS_VERSION=`echo $dummy2|sed 's/"//g'` + +echo UTS_VERSION=$UTS_VERSION >.uts_version + +. $LINUXDIR/.config + +if [ "$CONFIG_MODULES" = "n" ] +then + cat <&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 >$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_. Thus, if the user selects , +# the CPP symbol CONFIG_ will be defined and the +# shell variable CONFIG_ will be set to "y". +# +function choice () { + question="$1" + choices="$2" + old= + def=$3 + + # determine default answer: + names="" + set -- $choices + firstvar=$2 + while [ -n "$2" ]; do + if [ -n "$names" ]; then + names="$names, $1" + else + names="$1" + fi + if [ "$(eval echo \"\${$2}\")" = "y" ]; then + old=$1 + def=$1 + fi + shift; shift + done + + val="" + while [ -z "$val" ]; do + ambg=n + readln "$question ($names) [$def] " "$def" "$old" + ans=$(echo $ans | tr a-z A-Z) + set -- $choices + while [ -n "$1" ]; do + name=$(echo $1 | tr a-z A-Z) + case "$name" in + "$ans"* ) + if [ "$name" = "$ans" ]; then + val="$2" + break # stop on exact match + fi + if [ -n "$val" ]; then + echo;echo \ + " Sorry, \"$ans\" is ambiguous; please enter a longer string." + echo + val="" + ambg=y + break + else + val="$2" + fi;; + esac + shift; shift + done + if [ "$val" = "" -a "$ambg" = "n" ]; then + help "$firstvar" + fi + done + set -- $choices + while [ -n "$2" ]; do + if [ "$2" = "$val" ]; then + echo " defined $val" + define_bool "$2" "y" + else + define_bool "$2" "n" + fi + shift; shift + done +} + +CONFIG=.tmpconfig +CONFIG_H=.tmpconfig.h +trap "rm -f $CONFIG $CONFIG_H ; exit 1" 1 2 + +# +# Make sure we start out with a clean slate. +# +echo "#" > $CONFIG +echo "# Automatically generated make config: don't edit" >> $CONFIG +echo "#" >> $CONFIG + +echo "/*" > $CONFIG_H +echo " * Automatically generated C config: don't edit" >> $CONFIG_H +echo " */" >> $CONFIG_H + +echo '#define COMEDI_VERSION "'$VERS1.$VERS2.$VERS3\" >> $CONFIG_H +echo '#define COMEDI_VERSION_CODE ('0x1000*$VERS1+0x100*$VERS2+$VERS3')' >> $CONFIG_H + +DEFAULT="" +if [ "$1" = "-d" ] ; then + DEFAULT="-d" + shift +fi + +CONFIG_IN=./scripts/config.in +if [ "$1" != "" ] ; then + CONFIG_IN=$1 +fi + +DEFAULTS=./scripts/config.dist +if [ -f .config ]; then + DEFAULTS=.config +fi + +if [ -f $DEFAULTS ]; then + echo "#" + echo "# Using defaults found in" $DEFAULTS + echo "#" + . $DEFAULTS + sed -e 's/# \(.*\) is not.*/\1=n/' < $DEFAULTS > /tmp/conf.$$ + . /tmp/conf.$$ + rm /tmp/conf.$$ +else + echo "#" + echo "# No defaults found" + echo "#" +fi + +. $CONFIG_IN + +rm -f .config.old +if [ -f .config ]; then + mv .config .config.old +fi +mv .tmpconfig .config +mv .tmpconfig.h include/config.h + +echo +echo "Makefiles for comedi should now be configured. Run 'make' to compile," +echo "and then 'make install' to install." +echo + +exit 0 diff --git a/scripts/Configure.help b/scripts/Configure.help new file mode 100644 index 00000000..de46677b --- /dev/null +++ b/scripts/Configure.help @@ -0,0 +1,117 @@ + + +Prompt for development and/or incomplete code/drivers +CONFIG_EXPERIMENTAL + Some of the various things that Linux supports (such as network + drivers, filesystems, network protocols, etc.) can be in a state + of development where the functionality, stability, or the level of + testing is not yet high enough for general use. This is usually + known as the "alpha-test" phase amongst developers. If a feature is + currently in alpha-test, then the developers usually discourage + uninformed widespread use of this feature by the general public to + avoid "Why doesn't this work?" type mail messages. However, active + +Exported functions +CONFIG_EXPORT + If you want to use comedi from a kernel module, select yes. + RTLinux tasks and comedi value-added devices are both kernel + modules, so select yes if you want to use either of these. + +Real Time Support +CONFIG_COMEDI_RT + This selection requires that you have an RTLinux kernel, and will + not compile without the correct header files. If this option is + selected, all the exported functions are RT-safe, i.e., they can + be called from an RTLinux task or interrupt. + +Verbose Debugging +CONFIG_DEBUG + Selecting yes here will cause comedi to print error messages to + syslog at every opportunity. This can be useful when you are + writing programs that use comedi. + +Data Translation DT2811 driver +CONFIG_DT2811 + Includes support for DT2811. + +Data Translation DT2814 driver +CONFIG_DT2814 + Includes support for DT2814. + +Data Translation DT2815 driver +CONFIG_DT2815 + Includes support for DT2815. + +Data Translation DT2817 driver +CONFIG_DT2817 + Includes support for DT2817. + +Data Translation DT2821 series driver +CONFIG_DT282x + Includes support for DT2821 series hardware. + +National Instruments AT-MIO E series driver +CONFIG_ATMIO_E + Includes support for National Instruments AT-MIO E series hardware. + This driver should also work with DAQcard-MIO E series PCMCIA + hardware. If you have an E series board with an 8255 chip, you + also need to select the 8255 driver below. To use this driver, + you will also need the isapnptools package, which is included + with many current Linux distributions. + + Support for the PCI-MIO E series is in a separate driver -- you + should select N here. + +National Instruments PCI-MIO E series driver +CONFIG_PCIMIO_E + Includes support for National Instruments PCI-MIO E series hardware. + If you have an E series board with an 8255 chip, you also need to + select the 8255 driver below. + +Keithley Metrabyte DAS08 driver +CONFIG_DAS08 + Includes support for Keithley Metrabye DAS08 and compatibles. + +Support for 8255 +CONFIG_8255 + Includes support for 8255 chip. This is necessary for boards + that use an 8255 for digital I/O. + +Support for comedi controlling a standard PC parallel port +CONFIG_COMEDI_PARPORT + If you want to use your PC's parallel port for additional + digital I/O channels, select Y here. This driver is not activated + automatically, so you don't have to fear that comedi will + cause your printer to stop working. This driver is not + compatible with the generic Linux parallel port interface. + +Analog Devices RI800 driver +CONFIG_RTI800 + Includes support for RTI800 and RTI815 boards. + +Analog Devices RI802 driver +CONFIG_RTI802 + Includes support for RTI802. + +PCL 711 driver +CONFIG_PCL711 + Includes support for PCL 711, 711b and ACL 8112. + +PCL 725 driver +CONFIG_PCL725 + Includes support for PCL 725. + +PCL 726 driver +CONFIG_PCL726 + Includes support for PCL 726. + +Keithley Metrabyte DAS1600 driver +CONFIG_DAS1600 + Includes support for Keithley Metrabyte DAS1600, DAS1608 and + compatibles. To use the digital I/O provided by the 8255 chip + on these boards, you need to include the 8255 driver above. + +Quanser Consulting MultiQ-3 driver +CONFIG_MULTIQ3 + Includes support for Quanser Consulting MultiQ-3 boards. + diff --git a/scripts/check_kernel b/scripts/check_kernel new file mode 100755 index 00000000..72ed648d --- /dev/null +++ b/scripts/check_kernel @@ -0,0 +1,18 @@ +#!/bin/bash + + + +if [ $(grep -c "$(uname -r)" /usr/include/linux/version.h) != 0 ] +then + echo <